アソシエーション
開発環境:CakePHP2.5.1
CakePHPの便利な機能の一つにアソシエーションがあります。
アソシエーションはそれぞれのModel間をつなぐ役割をしてくれます。
そのアソシエーションは全部で4種類あります。
| アソシエーション名 | リレーションシップ |
|---|---|
| hasOne | 1 対 1 |
| hasMany | 1 対 多 |
| belongsTo | 多 対 1 |
| hasAndBelongsToMany | 多 対 多 |
アソシエーションは2つのModelをつなぐことはもちろん、3つ以上のModelをつなぐことも可能です。
ただし、そのようにどんどんつないでいくとコードが煩雑になり、バグが発生する可能性が増えてしまいます。
ですので、アソシエーションはとても便利な機能の一つですが、あまり使用せずに、必要な場合は対応したSQLのビューを作成し、それを単体Modelとして呼び出すとシンプルなコードになり、可読性があがり、バグの発生を減らすことができるのでおすすめです。
シンプルなアソシエーション
UserモデルがProfileモデルと繋がる場合、まずはUserモデルにProfileモデルとのアソシエーションを定義します。
<?php
class User extends AppModel
{
public $hasOne = 'Profile';
}
この時の各テーブルの構造の条件としては、Userモデルはid、Profileモデルはuser_idが必要となります。
そして、コントローラー側でいつも通りにfindをするだけでProfileデータも取得することができます。
<?php
public function index() {
$data = $this->User->find('all');
print_r($data);
}
結果は次のようになります。
Array
(
[0] => Array
(
[User] => Array
(
[id] => 1
[first_name] => 山田
[last_name] => 太郎
)
[Profile] => Array
(
[id] => 1
[user_id] => 1
[food] => イチゴ
)
)
)
多階層でアソシエーションデータを取得
UserモデルがProfileモデルと繋がっていて、更に、ProfileモデルがFoodモデルと繋がっているような多階層でアソシエーションを組みたい場合は次のようにします。
まずはUserモデルにProfileモデルとのアソシエーションを定義します。
<?php
class User extends AppModel
{
public $hasOne = 'Profile';
}
この時の各テーブルの構造の条件としては、Userモデルはid、Profileモデルはuser_idが必要となります。
次にProfileモデルにFoodモデルとのアソシエーションを定義します。
<?php
class Profile extends AppModel
{
public $belongsTo = 'Food';
}
この時の各テーブルの構造の条件としては、Profileモデルはfood_id、Foodモデルはidが必要となります。
そして、コントローラー側でいつも通りのfindの前にrecursiveの設定をする必要があります。
<?php
public function index() {
$this->User->recursive = 2;
$data = $this->User->find('all');
print_r($data);
}
これで2階層までアソシエーションをしてくれるようになります。
結果は次のようになります。
Array
(
[0] => Array
(
[User] => Array
(
[id] => 1
[first_name] => 山田
[last_name] => 太郎
)
[Profile] => Array
(
[id] => 1
[user_id] => 1
[food_id] => 1
[Food] => Array
(
[id] => 1
[name] => イチゴ
)
)
)
)
一時的なアソシエーション
アソシエーションをModelに記載すると常にアソシエーションを組むことになりますが、一時的にアソシエーションを使いたい時があります。
そんな時は、使いたい場所でbindModelを設定することで対応できます。
例えばコントローラーで使う場合は以下の様になりまます。
<?php
$this->Post->bindModel(
array('hasOne' => array(
'User' => array(
'className' => 'User'
)
)
)
);
$data = $this->Post->find('all');
注意しなければいけないのが、この設定は1回の呼び出しにしか効果がないので、ページネーションで使う場合には第二引数にfalseを設定する必要があります。
paginateは内部的にデータを取得するSQLとカウントするSQLの2回呼び出されるためです。
<?php
$this->Post->bindModel(
array('hasOne' => array(
'User' => array(
'className' => 'User'
)
)
), false
);
$this->Paginator->settings = $this->paginate;
$data = $this->Paginator->paginate('Post');
主キーを変更する
通常モデルはidが主キーになっていますが、アソシエーションを組む際にidとは違うカラムをキーとしたい場合があります。
そんな時はprimaryKeyを使います。
<?php
class Post extends AppModel
{
public $primaryKey = 'sample_id';
}
