この記事について
CakePHPであるテーブルにWHERE句付きのテーブルをJOINさせたい場合、いろいろやり方があると思います。が、今回はAssociationを使ってなるべくCakePHPが推奨するやり方でデータを取得する方法を示したいと思います。
CakePHPの推奨するやり方
CakePHPでは、AssociationでObjectごとの関係を示し、contain()またはmatching()を使ってデータを取得するやり方がおすすめされています。
公式によれば、matching()はprimary modelのデータを制限したい時に使われ、contain()はassociationでprimary modelのものではないデータを取りたい時に使うように設計されています。場合によって使い分けましょう。
ケーススタディ
例を使った方がしっくりくると思うので、以下のようなシチュエーションを想定します。
作りたい機能:「アクセスしたユーザーの書いた記事のカテゴリ一覧」
テーブル:categories
, articles
(以下参照)
例えばアクセスしたユーザーのIDが「1」である場合、赤字のカテゴリーのみが取得されるようにFinderやテーブルを定義していきます。
アプローチ
STEP1. ArticlesTableでログインユーザーに紐づく記事だけを取得されるようなFinderを定義。
STEP2. CategoriesControllerで、ログインユーザーに紐づく記事のカテゴリを取得。
STEP1
STEP1はメインではないので割愛します。ぶっちゃけこのケーススタディ程単純な例であれば、Dynamic Findersを使ってArticlesを絞ることもできます。
public function findArticlesOfUser(Query $query, array $options)
のような物を定義したとします。
STEP2
STEP1で作ったFinderを使えば、ログインユーザーに紐づく記事だけを取得できるので、そこで絞られたArticlesに紐づくカテゴリを取得します。今回の目的は「Primary ModelであるCategoryをSecondary ModelのArticleを使って絞りこむ」ことであるため、matching()
を使用するのが適切です。「Primary ModelであるCategoryからSecondary ModelのArticleを取得する」であればcontain()
が適切でしょう。正直、仕様によってはどちらでも問題なく動くことがありますが、CakePHPを理解した上でコードを書く分にはどちらが良いかを考えた方が良いでしょう。
CategoriesControllerは以下のようになります。
public function index()
{
$categories = $this->Categories->find()
->matching('Articles', function (Query $q) {
return $q->find(
'findArticlesOfUser',
['user' => $this->Authentication->getIdentity()]
);
});
//...あとは省略