【CakePHP3】アソシエーションで3層以上のデータを同時に取得する方法




データベースを操作する上でアソシエーションはとても便利ですが、3層以上のデータをまとめて取得する方法を知らずに苦労していました。ついに解決法が見つかりましたので、メモとして残しておきたいと思います。

2層のデータを取得する場合

例えば、ブログ記事を保存するテーブルArticlesと、ユーザ名の管理をするテーブルusersが以下のようにあるとします。

Articlesテーブル

iduser_idcontents
10今日は公園に…
….….….

Usersテーブル

iduser_name
0太郎
1花子

 

このようにArticlesテーブルはUsersテーブルに対してbelongsToのテーブルを持っています。このとき、下記のコードによってまとめてデータを引き出すことが出来ます。

// コントローラーやテーブルのメソッド内で
use Cake\ORM\TableRegistry;

Articles = TableRegistry::getTableLocator()->get('Articles');
Users = TableRegistry::getTableLocator()->get('Users');

// 3.6.0 より前
Articles = TableRegistry::get('Articles');
Users = TableRegistry::get('Users');

//メソッド内でデータを配列として取得
$articles = $this->Articles->find('all')->contain('Users')->toArray()

今回のトピックで重要なのは「contain(‘Users’)」の部分です。このメソッドを使うことにより、Articlesテーブルのデータだけではなく、レコードに紐付いたUsersデータも取得することが出来ます。この場合は以下のようなデータを取得できます。

//$articlesとして以下のような中身を得られる

(int) 0 => [
 'id' => 0,
 'user_id' => 0,
 'contents' => '今日は公園に...',
 'usrs' => [
   'user_name' => '太郎'
  ]
],
(int) 1 =>....(略)

3層以上をデータ取得する場合

結論から言えば、3つのテーブルA・B・Cがあったとして以下のコードで実装できます。findに対する引数で指定します。

//上手くいく○

$options = [
 'contain' => ['B', 'C'],
];
//AはTableRegistryによるテーブルインスタンス
$this->A->find('all', $options)->toArray();

//エラーが出る×
//AはTableRegistryによるテーブルインスタンス
$this->A->find('all')->contain(['B', 'C'])->toArray();

find()の後にcontain()をつなげることによって2層は取得できたので、そのままリストにしようと思いましたがエラーが出ました。この場合だと、AとCがアソシエーション定義されていないよ、というエラーが出てしまいます。

そのため、find()を使ってしているすることで、Aのレコードに紐付いたBのレコードの紐付いたCのレコードをすべてまとめて引き出すことが出来ます。お試しを!