データベースを操作する上でアソシエーションはとても便利ですが、3層以上のデータをまとめて取得する方法を知らずに苦労していました。ついに解決法が見つかりましたので、メモとして残しておきたいと思います。
2層のデータを取得する場合
例えば、ブログ記事を保存するテーブルArticlesと、ユーザ名の管理をするテーブルusersが以下のようにあるとします。
Articlesテーブル
id | user_id | contents |
1 | 0 | 今日は公園に… |
…. | …. | …. |
Usersテーブル
id | user_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のレコードをすべてまとめて引き出すことが出来ます。お試しを!