CakePHPクッキング

アソシエーション

開発環境: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';
}
The following two tabs change content below.

柴田 篤志

愛知県一宮市在住のWEBシステム屋(SE・プログラマ)。PHP、 CakePHPを利用して日々開発中。CakePHPをこれから始める方、ある程度CakePHPで開発をしている初級〜中級者の方を対象に、CakePHPでの開発をスピードアップするための情報をまとめています。CakePHPの案件のご依頼、御見積はこちらへご連絡ください。

最新記事 by 柴田 篤志 (全て見る)

WordPressとCakePHPの共存

サイトを作るのに便利なWordPress。プラグインも数多く用意されて

サイトマップの作成方法

Googleのウェブマスターツールに渡すためのサイトマップの作成方法で

INSERTしたIDを取得する

データを保存した後にINSERTされたデータのIDを使って何かの処理を

ファイルの存在確認やフォルダー内のファイル一覧取得

特定のファイルの存在を確認したい場合や特定のフォルダー内にあるファイル

リンクの最後に常にスラッシュをつける方法

URLの最後に常にスラッシュをつけるかつけないかを統一する方法は、UR

countやsumを使う時のフィールド名を指定する

CakePHPでcountやsumなどを使う場合、ちょっとした工夫が必

Formの便利な使い方

問い合わせフォームや会員機能など、WEBサイトを作る際に必ず必要になる

ヘルパーをオーバーライドする

CakePHPで便利な機能が詰まったヘルパーですが、時々もう少しこうだ

特定の時だけSQLの条件を追加するならSet::mergeを使う

SQLの条件を追加したい時に使うのがarray_mergeなどですが、

ページネーションではDISTINCTではなく、GROUP BYを使う

重複したデータを省く時に使うDISTINCTですが、ページ送りで使う時

Model内で別のModelを呼び出す

モデル内で別のモデルを使いたいケースがあった場合、次のようにすることで

CakePHPでGoogleAnalyticsAPIを使う

CakePHPでGoogleAnalyticsを使う方法について見てい

独自のfindを定義するfindMethods

findをより便利に、よりスッキリ書くのに、findMethodsを使

TCPDFを使ってCakePHPでPDFを作成

CakePHPでPDFを作成するには幾つかの方法がありますが、今回はT

JavaScriptをコードの最下部に配置

SEOの観点からJavaScriptのコードは一番下に記述したいところ

無限スクロールをさせる

TwitterやFacebookのタイムラインを見ていて、ページの下に

PHPExcelの導入

ExcelにDBのデータを表示して一覧を作ったり、請求書などのフォーマ

Warning: SplFileInfo::openFile〜の警告が表示されたら

ShellとWEBの両方で同一のModelを呼び出す時に「Warnin

連結されていないデータを抽出

通常、テーブルを連結してデータを取得するには、アソシエーションを利用し

データを取得する

データを取得したい時に少し変わった条件で取得したい場合があります。

PAGE TOP ↑