【CakePHP入門】アソシエーションの使い方をマスターしよう!

こんにちは!フリーエンジニアのせきです。

CakePHPでは、モデルにモデル同士の関連を定義することができます。

この記事では、

・アソシエーションとは何か知りたい
・アソシエーションの種別ごとの使い方を知りたい

といった内容について、わかりやすく解説します!

目次

アソシエーションとは

CakePHPでは、モデル同士の関連アソシエーションといいます。

アソシエーションを使うことで、一度の処理で関連する複数のモデルからデータを取得したり、保存したりすることができます。

アソシエーションには4つの種類があります。

次の章から、それぞれのアソシエーションについて解説していきます。

アソシエーションの使い方

hasOne

hasOneは「1対1」の関連を表します。

例えば、1人の顧客は1つの生年月日を持ちます。

この時、顧客と生年月日は「1対1」の関係になります。

この章では、顧客テーブルと生年月日テーブルを作成し、hasOneの関係になるように設定していきます。

以下のようなテーブルを作成し、データを登録しておきます。

顧客テーブルのテーブル定義

create table users
(
	id int not null auto_increment, -- ID
	name varchar(32), -- 名前
 	primary key (id)
);

顧客テーブルの初期データ

idname
1小林
2田中
3小沢

生年月日テーブルのテーブル定義

create table user_birthdays
(
	id int not null auto_increment,  -- ID
	user_id int not null,  -- ユーザID
	year int, -- 年
    month int, -- 月
    day int, -- 日
	primary key (id)
);

生年月日テーブルは顧客テーブルを参照する外部キーuser_idフィールドを持ちます。

外部キーは、デフォルトでは「相手側のモデル名(単数形)_id」を使用します。

生年月日テーブルの初期データ

iduser_idyearmonthday
11197035
2219751010
331965228

それぞれのModelを作成しておきます。

顧客テーブルのModel

src\Model\Entity\User.php

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class User extends Entity
{
}

src\Model\Table\UsersTable.php

<?php
namespace App\Model\Table;

use Cake\ORM\Table;

class UsersTable extends Table
{
}

生年月日テーブルのModel

src\Model\Entity\UserBirthday.php

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class UserBirthday extends Entity
{
}

src\Model\Table\UserBirthdaysTable.php

<?php
namespace App\Model\Table;

use Cake\ORM\Table;

class UserBirthdaysTable extends Table
{
}

hasOneアソシエーションは、Tableオブジェクトのinitialize()に以下のように定義します。

$this->hasOne('モデル名');

UsersTableに、initialize()を追加します。

    public function initialize(array $config)
    {
        $this->hasOne('UserBirthdays');
    }

このように定義しておくと、UsersのControllerでcontainを使用し、UserBirthdaysのデータを取得することができます。

bakeControllerの雛形を作成します。

bin/cake bake controller users

bakeについては、以下の記事で詳しく解説しています!

作成された「src\Controller\UsersController.php」のindexメソッドを、以下のように修正します。

    public function index()
    {
		$query = $this->Users->find('all')->contain(['UserBirthdays']);
		$this->set('users', $query);
    }

「find(‘all’)」で全件検索し、「contain(‘UserBirthdays’)」でUserBirthdaysのデータも一緒に取得するように指定しています。

一覧表示のTemplateを作成します。

src\Template\Users\index.ctp

<table cellpadding="0" cellspacing="0">
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>生年月日</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($users as $user): ?>
        <tr>
            <td><?= $user->id ?></td>
            <td><?= $user->name ?></td>
			<td><?= $user->user_birthday->year ?>/<?= $user->user_birthday->month ?>/<?= $user->user_birthday->day ?></td>
        </tr>
        <?php endforeach; ?>
    </tbody>
</table>

UserBirthdaysのデータは「$user->user_birthday」で取得することができます。

表示してみます。

http://[サーバ名]/[プロジェクト名]/users/
hasone

UserBirthdaysのデータも取得できています。

hasMany

hasManyは「1対多」の関連を表します。

例えば、1人の顧客が複数の注文データを持つことができるので、顧客と注文は「1対多」の関係になります。

hasManyについては、以下の記事で詳しく解説しています!

belongsTo

belongsToは「多対1」の関連を表します。

hasManyの章で、顧客と注文は「1対多」の関係になると解説しましたが、belongsToはその逆で、注文と顧客は「多対1」の関係になります。

belongsToについては、以下の記事で詳しく解説しています!

belongsToMany

belongsToManyは「多対多」の関連を表します。

例えば、顧客は複数の商品を注文し、商品は複数の顧客から注文されます。

この時、顧客と商品は「多対多」の関係になります。

通常、多対多の関連を表すには中間テーブルを使用します。

今回の例だと、注文のデータがあって顧客と商品が多対多の関連を持つので、注文テーブルが中間テーブルになります。

usersテーブル(顧客)とproductsテーブル(商品)の中間テーブルは、デフォルトではusers_productsテーブルと命名します。

users_productsテーブルは、user_idフィールドを持ち顧客テーブルと1対多、さらにproduct_idフィールドを持ち商品テーブルとも1対多の関係となります。

belongsToManyアソシエーションは、他のアソシエーションと同様、Tableオブジェクトのinitialize()に以下のように定義します。

$this->belongsToMany('モデル名');

次のように、両方のモデルでbelongsTo アソシエーションを定義することができます。

src\Model\Table\UsersTable.php

<?php
namespace App\Model\Table;

use Cake\ORM\Table;

class UsersTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Products');
    }
}

src\Model\Table\ProductsTable.php

<?php
namespace App\Model\Table;

use Cake\ORM\Table;

class ProductsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Users');
    }
}

検索も他のアソシエーション同様、Controllerでcontainを使用し、関連テーブルのデータを取得することができます。

$query = $this->Users->find('all')->contain(['Products']);

CakePHP3.0より前はbelongsToManyアソシエーションではなく、hasAndBelongsToManyが使用されていたので、注意してください

まとめ

今回はアソシエーションの使い方について解説しました。

実際にシステム等で使用されるテーブルには多くの関連が存在します。

アソシエーションの種別をひとつずつ覚えて、ぜひマスターしてください。

アソシエーションについて忘れてしまったら、この記事を思い出して下さい!

この記事を書いた人

フリーランスでWebシステム開発やゲーム開発をしています。
読者の方にプログラミングの面白さをお伝えしたいです。

目次