PHP
Symfony
doctrine

DoctrineのLEFT JOINはまともに使えない

More than 3 years have passed since last update.

DISTINCT / LEFT JOIN


schema.yml

TableA:

actAs:
Timestampable: ~
columns:
columna:
type: string(255)

TableB:
actAs:
Timestampable: ~
columns:
columnb:
type: string(255)
relations:
TableA:
local: a_id
foreign: id
type: one
foreignType: many


テーブルAとBに1対多のリレーションをはっておく。

    $data = Doctrine_Query::create()->from('TableA a')->leftJoin('a.TableB b')->limit(5)->execute();

// Expected result : SELECT * FROM TableA a LEFT JOIN TableB b ON a.id = b.a_id LIMIT 5
// Actual result : SELECT * FROM TableA a LEFT JOIN TableB b ON a.id = b.a_id WHERE a.id IN (1,2,3,4,5)

全体に掛けていたはずのLIMIT 5が、『テーブルAの件数が5件』という意味に勝手に書き換えられている。

ふざけてるの?

このファッキンな挙動を止める術は一応あって、

    $q = Doctrine_Query::create()->from('TableA a')->leftJoin('a.TableB b')->limit(5);

$q->setDisableLimitSubquery(true);
$data = $q->execute();

で余計な変換をせず想定したとおりのLIMITがかかります。

何故こちらがデフォルトでないのか理解に苦しむ。

あと返り値がbooleanなのでメソッドチェーンできない。

さて、これでめでたしかと思えば実はそんなことはない。

帰ってくるデータは実際はDoctrine_Collectionですが、配列として示すとこんなかんじになっています。

    $data = [

0=>[
id=>1,
TableB=>[
0=>[],
1=>[],
2=>[],
]
],
1=>[
id=>2,
TableB=>[]
],

];

テーブルBが、同じa_id毎にまとめられてしまうのだ。

これはこれで便利な機能ではあるのですが、問題はこの挙動をやめさせる手段がないことです。

executeの引数で、返り値の形をある程度変更できます。

    // デフォルト、入れ子のDoctrine_Collectionで取得

$data = $q->execute(array(), Doctrine_Core::HYDRATE_RECORD);
// 入れ子の配列で取得
$data = $q->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
// フラットな配列で取得
$data = $q->execute(array(), Doctrine_Core::HYDRATE_ARRAY_SHALLOW);

配列ならフラットな形で取得できるのですが、何故かフラットなDoctrine_Collectionで取得する方法が無い。

素直にforeachループ一回で全TableBを扱わせろ。