公式のドキュメント
http://www.doctrine-project.org/projects/orm/1.2/docs/manual/dql-doctrine-query-language/ja#join%E3%81%AE%E6%A7%8B%E6%96%87:on%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89によると、ONキーワードでリレーション条件がカスタム指定できるようなことが書いてありますが、
実際には効きませんorz試行錯誤の末、無理やりLEFT OUTER JOINぽいことを実行させることができたので忘れないようにメモ。
実験しながらエラーMSGでググると、symfony公式のフォーラムでも世界各国の方々がLEFT OUTER JOINができないと悩んでいた。
なんでONを自分で指定したいかと言うとモデルを次のように定義してあるとして、
Able:
columns:
id: { type: integer(1), primary: true }
Baker:
columns:
id: { type: integer(1), primary: true }
out_class: string(4)
out_id: integer(1)
Cat:
columns:
id: { type: integer(1) }
Bakerはout_classの値によりAbleかCatどちらかにJOIN先が変わります。
なんでこんな変な構造にするか詳細は聞かないでください(笑
【実験1】
まず公式のドキュメント通りに。
$q = Doctrine::getTable("Baker")->createQuery("b")->leftJoin("Able a ON a.id ON b.out_id");
⇒...FROM Baker b, Able a...
ONどこいった??単にFROMにカンマ区切りで入れられると、SQL側でAble.idとBaker.idで勝手にJOINされちゃって都合が悪い。
【実験2】
公式ドキュメントにWITHというのも書いてあるので使ってみた。
$q = Doctrine::getTable("Baker")->createQuery("b")->leftJoin("Able a ON a.id =b.out_id");
⇒...FROM Baker b, Able a...
WITHどこいった??
【実験3】
無理を承知でINNER JOINにしてみる。Linux系OSのMySQLで、MyISAMテーブル(=外部キー制約なし)だとBaker.out_idとA.idのINNER JOINとか普通に成立した記憶が。
$q = Dcotrine::getTable("Baker")->createQuery("b")->innerJoin("Able a ON a.id = b.out_id");
⇒エラー。AbleとBakerの間にリレーションが定義されてないとか何とか。まあ当然。
ここで公式APIドキュメントを読みに行く。
Doctrine_Query_Abstruct::leftJoin($join, $params=array())
色々場合分けはあるが、要は最後に $this->dqlQueryPart['from']に 'LEFT JOIN '.$joinをセットする仕組みのよう。
【実験4】
とりあえずセットした後にちゃんとセットされているかどうかチェックしてみる。
$q = Doctrine::getTable("Baker")->createQuery("b")->leftJoin("Able a ON a.id = b.out_id");
var_dump($q->getDql());
⇒FROM Baker b LEFT JOIN Able a ON a.id = b.out_id
ちゃんとDQLにはセットされていることが確認できた。
が、$q->getSqlQuery()すると相変わらず FROM Baker b, Able aのみ。どうやらDQLからSQLを生成する時にONが無視されてしまう様子。
ここでふと思い出した。Pear::DBを使ってSQLを勉強し始めた頃、FROMにテーブルを羅列してWHEREでJOIN条件書いてもほしいデータが取れてたなー。その後SQL実行速度の問題でちゃんとJOINを書くようになったんだけど。
【実験5】
だめもとで。
$q = Doctrine::getTable("Baker")->createQuery("b")->addFrom("Able a")->addWhere("b.out_id = a.id");
⇒実験3と同じエラー。でも、$q->getSqlQuery()ではちゃんとSQLが出て来るし、それをコピペしてphpmyadminに実行させたらちゃんと想定通りのデータが出た。
【実験6】
実験5ではAbleのデータを入れるコンポーネント(プロパティ)がないよ!って怒られてる気がしてきた。同時にBakerとJOINするAbleまたはCatのデータはBakerに取得用のメソッドを作ってあるのでAbleのデータを最初のSELECTで取らなくてもいいよね、と考える。
$q = Doctrine::getTable("Baker")->createQuery("b")->addFrom("Able a")->addWhere("b.out_id = a.id")->select("b.*");
⇒成功!!
ここまで来るのに休み休み(家事育児やりながら^^;)で4時間近くかかってしまった(汗
【結論】
スマートじゃない&MySQLエンジンがんばって!なSQLだけど、上記の方法でLEFT OUTER JOINっぽいことはできることがわかりました。