結果セット整形

Posted by muchag | Symfony 1.x | 2016-11-19 (土) 12:04:24

DQL における、結果セットの整形。
Data Hydrators

【環境】
Symfony:1.4.13
doctrine:1.2.4
書式

execute, fetchOne, fetchAll などの第2引数に指定する。

  1. $q = Doctrine_Core::getTable('User')
  2.     ->createQuery('u')
  3.     ->leftJoin('u.Email e')
  4.     ->where('u.username = ?', 'jwage');
  5.  
  6. $user = $q->fetchOne(array(), Doctrine_Core::HYDRATE_RECORD);
詳細
HYDRATE_RECORD

レコードをオブジェクトとして取得。
デフォルト値なので、省略すればこれになる。

HYDRATE_ARRAY

連想配列として取得。

HYDRATE_SCALAR

連想配列として取得。
キーが、カラム名_フィールド名 という書式になっている。

HYDRATE_SINGLE_SCALAR

配列として取得。
WHERE 句で指定したカラムの値を、
添え字 0 から始まる配列の要素に格納(しているように見える)。

HYDRATE_ON_DEMAND

公式サイトを読む限り、
「1レコードしか読み込まないのでメモリを節約できるよ」
って書いてある気がするけど
実験したら、時間制限に引っ掛かって戻ってこなかったので
今回はパス。

HYDRATE_RECORD_HIERARCHY

nested ビヘイビアを利用しているときに使える引数っぽい。
オブジェクトとして取得。

HYDRATE_ARRAY_HIERARCHY

nested ビヘイビアを利用しているときに使える引数っぽい。
連想配列として取得。

HYDRATE_NONE

配列として取得。
取得してきたデータを、添え字 0 から始まる配列の要素に格納(しているように見える)。

HYDRATE_ARRAY_SHALLOW

連想配列として取得。
SELECT で指定したカラムだけを取得。

※下2つは、下記参考サイトから拾ってきた。
公式サイトに全種類が載っていないのは不思議・・・

HYDRATE_RECORD_SHALLOW

上記定数を参考に、こんなのもあるかと思ったけどなかったw

参考サイト

stackoverflow:Select One column Doctrine DQL(2013-01-19)

Symfony 1.x | 2016-11-19 (土) 12:04:24 |

Doctrine -> DISTINCT, LEFT JOIN

Posted by muchag | Symfony 1.x | 2015-12-22 (火) 16:19:36

注意書きを見つけたのでメモ。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
参考サイト

Qiita:DoctrineのDISTINCTは正しく動作しない
→回避策あり

Qiita:DoctrineのLEFT JOINはまともに使えない
→回避策あり

Symfony 1.x | 2015-12-22 (火) 16:19:36 |

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘hoge’ for key ‘PRIMARY’

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 17:52:00

【環境】
Symfony:1.4.13
doctrine:1.2.4
状況

既存データの更新をしようとして起きた。

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘hoge’ for key ‘PRIMARY’

ちょこっといじったら、エラー内容が下記に変わったけど、エラー内容はほぼ同じ?

SQLSTATE[23000]: Integrity constraint violation: 1557 Upholding foreign key constraints for table ‘op_category’, entry ”, key 1 would lead to a duplicate entry
原因

既存データとセーブデータの ID(プライマリーキー)が被ってるよ、という警告のまんま。

経緯

フォームを利用しているものの、Ajax 通信であったため、フォームをそのまま突っ込むのではなく
手動でデータをセットして保存しようとしていた。

Symfony(Doctrine)では、save メソッドで、INSERT と UPDATE を自動判別してくれるとのことで
INSERT のときと、同じデータのセットの仕方にしていたけど
これがいけなかったみたい。

解決

後述の通り、事前にフォームにデフォルト値を設定することで
エラーは消えた。

INSERT と UPDATE を自動判別

フォーム -> 表示(Model 有り) にあるように

  1. $sample = Doctrine::getTable('Sample')->find($request->getParameter('id'));
  2. $this->form = new MySampleForm($sample);

とすることで、フォームのデフォルト値(既存データ)がセットされ
これによって、INSERT と UPDATE を自動判別しているっぽい。

Symfony 1.x | 2015-11-17 (火) 17:52:00 |

SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 16:12:02

【環境】
Symfony:1.4.13
doctrine:1.2.4
エラー
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

引数の数が合ってないよっと。

状況1

i18n データを取得しようとして、データ取得 の如く
find メソッドで取得しようとして起きた。

原因

i18n データを取得するのに、親テーブルへアクセスして
Translation テーブルの主キーのデータを渡していた。

  1. $category = Doctrine::getTable('Category')->find(Array($id, $culture));
解決

親テーブルの主キーは ID のみなので、下記のように変更して解決。

  1. $category = Doctrine::getTable('Category')->find($id);
状況2

DQL でサブクエリを利用しようとした。
ただ、メインクエリとサブクエリと
同一のテーブルだった。

  1. $query = Doctrine::getTable('Relation')->createQuery('r')
  2.     ->select('r.hoge_id');
  3.  
  4. $sub_query = $query->createSubquery()
  5.     ->select('sub.hoge_id')
  6.     ->from('Hoge sub')
  7.     ->where('sub.category_id = ?', 123);
  8.    
  9. $query->where("r.hoge_id IN ({$sub_query->getDql()})")
  10.     ->andWhere('r.category_id = ?', 456);
原因

テーブルエイリアスを変えてみたものの
category_id というカラムは同一と見なされ、値は2種類あるので、
数が合ってない、ということみたい。

解決

解決できてないけど、そもそもこの DQL は、本命からずれたお試しだったので
本命の DQL に戻しただけ。

直接の解決策は見つかっていない。

Symfony 1.x | 2015-11-17 (火) 16:12:02 |

Invalid hydration mode specified: ja_JP

Posted by muchag | Symfony 1.x | 2015-11-17 (火) 15:33:17

【環境】
Symfony:1.4.13
doctrine:1.2.4
状況

データ取得 の find メソッドを初めて実装したときに起きた。

Invalid hydration mode specified: ja_JP
原因

複合主キーの検索値を配列ではなく、そのまま引数に入れていたため。

  1. $category = Doctrine::getTable('Category')->find($id, $culture);
解決

配列に書きなおして無事解決。 😯

  1. $category = Doctrine::getTable('Category')->find(Array($id, $culture));
別の問題

この問題は解決したんだけど、別の問題が残っていたのは秘密
下記コードには別の間違いがありました。。。
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

Symfony 1.x | 2015-11-17 (火) 15:33:17 |

SQL の確認

Posted by muchag | OpenPNE 3.x,Symfony 1.x | 2015-10-08 (木) 18:34:07

発行されている SQL 文を確認する方法。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
モデル

モデル生成時に自動的に生成される Table クラスで書く場合。

  1. $result = $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1)
  4.     ->execute();

上記のようにすることで目的のレコードを取得できるけど
createQuery メソッドで Doctrine_Query が返ってきているので
最後の execute メソッドを getSqlQuery に変更することで、生 SQL 文を取得できる。

  1. echo $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1)
  4.     ->getSqlQuery();
実際に利用してみて思った
  1. $query = $this->createQuery('p')
  2.     ->leftJoin('p.Translation t WITH t.lang = ?', $culture)
  3.     ->andWhere('p.id = ?', 1);
  4.  
  5. echo $query->getSqlQuery();
  6.  
  7. $result = $query->execute();

こういう風に記述してやると見やすい。

色々な記事で、execute() を切り離して記述しているのは
こういうことがあるからかも?

OpenPNE 3.x,Symfony 1.x | 2015-10-08 (木) 18:34:07 |

SoftDelete

Posted by muchag | OpenPNE 3.x,Symfony 1.x | 2015-09-06 (日) 17:41:37


SoftDelete:日本語版

レコードを削除する際に、本当に削除(物理削除)するのではなく
削除フラグを立てることによって、削除モードにする(論理削除)機能。

【環境】
OpenPNE:3.8.15
Symfony:1.4.13
doctrine:1.2.4
設定
ProjectConfiguration

ここまで実装方法などについて書いてきましたが、実はこれだけだと動きません。
Doctrineのデフォルトの設定では、Doctrine_Queryを実行前後で修正することが出来なくなっており、そこを設定してあげる必要があります。この設定の方法がsymfony 1.0とsymfony 1.1で変わってきます。

symfony 1.1ではProjectConfigurationクラスに設定を記述します。

アシアルブログ:Doctrineで論理削除を意識せずに扱う

ProjectConfiguration.class.php
  1. class ProjectConfiguration extends sfProjectConfiguration
  2. {
  3.   protected $plugins = array('sfDoctrinePlugin');
  4.  
  5.   public function configureDoctrine($manager)
  6.   {
  7.     $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
  8.   }
  9. }
OpnePNE3

OpenPNE3 では、こういう設定を追加する場所が用意されているっぽい。
まだ、成功体験をしていないので、本当にこれでよいかは不明。

(openpne3)\config\ProjectConfiguration.class.php
  1. public function setupProjectOpenPNEDoctrine($manager)
  2. {
  3.     // You can write your own configurations.
  4.  
  5.     // In default, OpenPNE uses foreign key.
  6.     // If you want not to use foreign key, comment out the following configuration:
  7.     // $manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL ^ Doctrine::EXPORT_CONSTRAINTS);
  8.  
  9.     $manager->setAttribute(Doctrine::ATTR_USE_DQL_CALLBACKS, true);
  10. }
schema
schema.yml
  1. Category:
  2.   actAs:
  3.    - SoftDelete
設定変更

カラム名やデータ型を変更したい場合は、下記のように記述すればよいみたい。
Symfony world:Doctrine SoftDelete behavior usage
→この記事のコメント投稿内情報。未検証。

  1. Category:
  2.   actAs:
  3.     SoftDelete:
  4.       name: is_deleted
  5.       type: datetime

力技でやっている人もいるみたい。
@chisei のはてなブログ:Doctrineの論理削除

マニュアルとの差異

このように設定することで、当該テーブルへ deleted_at カラムが追加される
んだけど・・・

公式マニュアル では、delete カラムが追加される、とされている。
データ型も、公式マニュアルでは、TYNYINT とあるけど、実際は DATETIME になる。
バージョンの問題かしらね~。

データ挿入

んで、実際に試してみた。
CSV で、データを突っ込む際に、deleted_at カラムには null を指定しておいたところ
当該カラムの値は 0000-00-00 00:00:00 となった。

CSV から Null を放り込む方法

文系プログラマによるTIPSブログ:MySQLのload dataで日付型にnullをセットする
こちらの記事がビンゴ!

  1. LOAD DATA
  2.     LOCAL INFILE 'data.csv'
  3.     INTO TABLE [table_name]
  4.     FIELDS TERMINATED BY ','
  5.     ENCLOSED BY '"'
  6.     SET
  7.         deleted_at = nullif(deleted_at, '0000-00-00 00:00:00')
  8. ;

上記記事では、指定項目がもっといっぱいあったけど
ひとまず、私の今回の環境およびデータでは、上記 SQL にて、
無事に daleted_at カラムへ Null 値を放り込めた。

nullif

どうやら第2引数に、データ型に合わせたデフォルト値を設定するみたい。
なので、その他のデータ型は下記の通り。

  1. SET
  2.         datetime = nullif(datetime, '0000-00-00 00:00:00')
  3.         date = nullif(date, '0000-00-00')
  4.         time = nullif(time, '00:00:00')
データ取得
  1. $result = $this->createQuery('c')
  2.     ->execute();

データ取得を試みたところ
取得データ数は 0 。

SQL 確認

SQL の確認 のごとく、発行された SQL 文を確認すると、
ちゃんと WHERE 句が付記されている。

  1. WHERE (o.deleted_at IS NULL)

※エイリアスに p を指定しているのに、テーブル名の先頭文字 o をエイリアスにされているが気になるけど。。。

エイリアス o

これは、こちらの指定した DQL から Doctrine が SQL を生成する際に、
指定されたテーブルを順番に o1, o2, o3, … と再指定しているためだった。

その o も、恐らく、テーブル名の頭文字を取っていると思われる。
OpenPNE3 の場合、テーブル接頭辞を付けているため
全テーブルが op_ で始まるのよね。

原因

Doctrine が NULL を探しているのに対して、
データ側は ‘0000-00-00 00:00:00’ という値であるため
スルーされていた。

解決

上記 データ挿入→CSV から Null を放り込む方法 を用いて
値を Null にしてやることで、ちゃんとデータを取得できるようになった。

過去の対応策
  1. $result = $this->createQuery('p')
  2.     ->andWhere('p.deleted_at = ?', '0000-00-00 00:00:00')
  3.     ->execute();

これで一応拾ってくることはできた。

とりあえずはこれでいくしかないけど。。。意味なーい!
いつか解明しなければ。。。
(やっと解明できた)

エイリアス

ちなみにテーブルエイリアスを付けないと、上記記述でも拾ってこない。

  1. // NG
  2. $result = $this->createQuery()
  3.     ->andWhere('deleted_at = ?', '0000-00-00 00:00:00')
  4.     ->execute();

必ずエイリアスをつける必要があります。
ただしこれはSELECT限定で、DELETEのときはエイリアスがなくても動くようです。

アシアルブログ:Doctrineで論理削除を意識せずに扱う

OpenPNE 3.x,Symfony 1.x | 2015-09-06 (日) 17:41:37 |

SQLSTATE[HY000]: General error: 1005 Can’t create table

Posted by muchag | OpenPNE 3.x,Symfony 1.x | 2015-05-21 (木) 22:49:18

【環境】
Symfony:1.4.13
OpnePNE:3.8.15
状況

OpenPNE3 のプラグインを開発していて、DB の再構築を行ったところ
下記のようなエラーメッセージが出た。

エラーメッセージ
SQLSTATE[HY000]: General error: 1005 Can’t create table ‘database_name.#sql-16e8_55a’
(errno: 150).
Failing Query:
“ALTER TABLE op_my_hoge ADD CONSTRAINT op_my_hoge_parent_id_op_my_parent_id FOREIGN KEY (parent_id) REFERENCES op_my_parent(id)”.
Failing Query:
ALTER TABLE op_my_hoge ADD CONSTRAINT op_my_hoge_parent_id_op_my_parent_id FOREIGN KEY (parent_id) REFERENCES op_my_parent(id)
原因

よく見てみると
op_my_hoge.parent_id と op_my_parent.id のデータ型が異なっていた。

OpenPNE 3.x,Symfony 1.x | 2015-05-21 (木) 22:49:18 |

公式チュートリアル

Posted by muchag | Symfony 1.x | 2015-05-14 (木) 14:47:06

公式のチュートリアルに一覧表がないので
一覧のためのページ。

gentle-introduction
  1. 第1章 – symfony の紹介
  2. 第2章 – symfony のコードを探求する
  3. 第3章 – symfony を動かす
  4. 第4章 – ページ作成の基本
  5. 第5章 – symfony を設定する
  6. 第6章 – コントローラーレイヤーの内側
  7. 第7章 – ビューレイヤーの内側
  8. 第8章 – モデルレイヤーの内側 (Doctrine)
  9. 第9章 – リンクとルーティングシステム
  10. 第10章 – フォーム
  11. 第11章 – Eメール
  12. 第12章 – キャッシュ
  13. 第13章 – 国際化とローカライゼーション
  14. 第14章 – Admin ジェネレーター
  15. 第15章 – ユニットテストと機能テスト
  16. 第16章 – アプリケーションの運用ツール
  17. 第17章 – symfony を拡張する
  18. 第18章 – パフォーマンス
  19. 第19章 – symfony の設定ファイルをマスターする
フォーム

目次:Forms
(やっと目次を発見できた・・・)

  1. 第1章 – フォームの作成
  2. 第2章 – フォームのバリデーション
  3. 第3章 – Web デザイナのためのフォーム
  4. 第4章 – Propel との統合
jobeet 1.4 on doctrine
  1. 1日目: プロジェクトを始める
  2. 2日目: プロジェクト
  3. 3日目: データモデル
  4. 4日目: Controller と View
  5. 5日目: ルーティング
  6. 6日目: モデルの詳細
  7. 7日目: カテゴリページで遊ぶ
  8. 8日目: ユニットテスト
  9. 9日: 機能テスト
  10. 10日目: フォーム
  11. 11日目: フォームをテストする
  12. 12日目: アドミンジェネレータ
  13. 13日目: ユーザー
  14. 14日目: フィード
  15. 15日目: Web サービス
  16. 16日目: メーラー
  17. 17日目: 検索
  18. 18日目: AJAX
  19. 19日目: 国際化とローカライゼーション
  20. 20日目: プラグイン
  21. 21日目: キャッシュ
  22. 22日目: デプロイ
  23. 23日目: 別の視点から symfony を見る
1.4 Doctrine

Doctrine のテーブル継承の活用

1.2 Doctrine

Doctrine

Symfony 1.x | 2015-05-14 (木) 14:47:06 |

Symfony 1.x のデバッグ

Posted by muchag | Eclipse,OpenPNE 3.x,Symfony 1.x | 2015-04-27 (月) 12:19:28

自作のプログラムなら簡単にデバッグできるけど
Symfony のようなフレームワークは、よくわからなかったので、調べてみた。

【環境】
Eclipse: Luna Service Release 1 (4.4.1)
Symfony: 1.4.13
デバッグの構成

[実行]-[デバッグの構成]
[PHP Web アプリケーション]
初めての場合は、自動で新規構成が表示される。
2つ目以降は、左ペインツールバーにある [新規の起動構成] をクリック。

サーバー
名前

自由。

サーバー

任意に選択。

未設定の場合は、Xdebug 再び! 参照。

ファイル

[参照] から、(Symfony プロジェクト)\web\index.php を選択すると
/(Symfony プロジェクト)/web/index.php
と入力される。

2015-12-17 追記 ここから

今日は、上述のやり方ではできなかった。
Xdebug 再び! に追記しておいたので、参照。

2015-12-17 追記 ここまで
URL

[自動生成] のチェックを外す。
すると、右のテキストボックスがアクティブになる。

左のテキストボックスには「http://localhost/」と入っている。

これで、XAMPP の htdocs までのパスは確保されているので
右のテキストボックスに、それ以下を記述。
「/(appname)/index.php」(自分の環境に適宜合わせる)

適用

[適用] をクリック。

参考サイト

Layer8:Windows上のEclipseでsymfonyをデバッグする方法

Eclipse,OpenPNE 3.x,Symfony 1.x | 2015-04-27 (月) 12:19:28 |
次ページへ »