CakePHP 3 ではアソシエーションを設定し関連データを取得することが出来る。
CakePHP 2 以前もあった仕組みであるが hasMany
若しくは belongsToMany
で関連付けたデータを contain
にして where
で絞り込もうとすると失敗する:
$this->find()
->contain(['Comments']) // Post has many comments とする
->where(['Comments.type' => 1]); // 指定できそうに見えるが失敗!!
この Post
が belongsTo
の場合は内部的に INNER JOIN
若しくは OUTER JOIN
された SQL が発行されるので大丈夫なのだが hasMany
若しくは belongsToMany
は複数の SQL に分割して発行されるので、最初の SQL に Comments
が存在せず、条件を指定してもそれが最初の SQL に対し指定されてしまい「存在しないカラムに対し条件を指定している」ことになり失敗してしまう:
# 内部的にこのように分けて発行されるので最初の SQL には Comments は存在しない!!
SELECT * FROM articles;
SELECT * FROM comments WHERE article_id IN (1, 2, 3, 4, 5);
これを避けるために contain
に対し以下のようにクロージャを渡すことにより 2 つ目の SQL に対し条件を渡すことが出来る:
$query = $articles->find()->contain([
'Comments' => function ($q) {
return $q->where(['Comments.type' => 1]);
}
]);
つまり contain
している Entity
のアソシエーションがどの関係であるかを思い浮かべて適切なコードを書かなければならない。
この手の O/R マッパーを使用する際はしばしば内部で吐かれる SQL を dump しながら挙動を確認しなければならないのが辛い所ではある。