こんにちは!フリーエンジニアのせきです。
CakePHPでは、モデルにモデル同士の関連を定義することができます。
この記事では、
・belongsToとは何か知りたい
・belongsToの使い方を知りたい
・外部キーや結合方法をカスタマイズする方法を知りたい
という基本的な内容から、
・階層の深いモデルのデータを取得する方法を知りたい
といった応用的な内容に関しても解説していきます。
今回はそんなモデル同士の関連を定義するbelongsToについて、わかりやすく解説します!
belongsToとは
CakePHPでは、モデル同士の関連をアソシエーションといいます。
belongsToはアソシエーションのひとつであり、「多対1」の関連を表します。
例えば、1つの部署には複数の従業員が所属します。
この時、従業員と部署は「多対1」の関係になります。
belongsToの使い方
先ほどの従業員と部署を例に解説していきます。
以下のようなテーブルを作成し、データを登録しておきます。
従業員テーブルのテーブル定義
create table employees ( id int not null auto_increment, -- ID section_id int, -- 部署ID employee_name varchar(32), -- 従業員名 age int, -- 年齢 primary key (id) );
従業員テーブルの初期データ
ID | 部署ID | 従業員名 | 年齢 |
---|---|---|---|
1 | 2 | 小林 | 30 |
2 | 1 | 丸山 | 35 |
3 | 3 | 大田 | 27 |
4 | 4 | 中村 | 36 |
5 | 2 | 木村 | 40 |
部署テーブルのテーブル定義
create table sections ( id int not null auto_increment, -- ID office_id int, -- オフィスID section_name varchar(32), -- 部署名 primary key (id) );
部署テーブルの初期データ
ID | オフィスID | 部署名 |
---|---|---|
1 | 1 | 営業 |
2 | 1 | 開発 |
3 | 2 | 人事 |
それぞれのModelを作成しておきます。
従業員テーブルのModel
src\Model\Entity\Employee.php
<?php namespace App\Model\Entity; use Cake\ORM\Entity; class Employee extends Entity { }
src\Model\Table\EmployeesTable.php
<?php namespace App\Model\Table; use Cake\ORM\Table; class EmployeesTable extends Table { }
部署テーブルのModel
src\Model\Entity\Section.php
<?php namespace App\Model\Entity; use Cake\ORM\Entity; class Section extends Entity { }
src\Model\Table\SectionsTable.php
<?php namespace App\Model\Table; use Cake\ORM\Table; class SectionsTable extends Table { }
基本的な使い方
アソシエーションは、Tableオブジェクトのinitialize()に以下のように定義します。
$this->belongsTo('モデル名');
EmployeesTableに、initialize()を追加します。
public function initialize(array $config) { $this->belongsTo('Sections'); }
このように定義しておくと、EmployeesのControllerでcontainを使用し、Sectionsのデータを取得することができます。
EmployeesのControllerです。
src\Controller\EmployeesController.php
<?php namespace App\Controller; use App\Controller\AppController; class EmployeesController extends AppController { public function index() { $query = $this->Employees->find('all')->contain('Sections')->order('Employees.id'); $this->set('employees', $query); } }
「find(‘all’)」で全件検索し、「contain(‘Sections’)」でSectionsのデータも取得するように指定しています。
「order(‘Employees.id’)」は従業員のID順でソートするための指定です。
以下のTemplateを作成し、表示してみます。
src\Template\Employees\index.ctp
<table> <thead> <tr> <th>ID</th> <th>部署名</th> <th>従業員名</th> <th>年齢</th> </tr> </thead> <tbody> <?php foreach ($employees as $employee): ?> <tr> <td><?= $employee->id ?></td> <td><?= $employee->section ? $employee->section->section_name : '-' ?></td> <td><?= $employee->employee_name ?></td> <td><?= $employee->age ?></td> </tr> <?php endforeach; ?> </tbody> </table>
http://[サーバ名]/[プロジェクト名]/employees/
Sectionsのデータも取得できました。
containについては、以下の記事で詳しく解説しています。
外部キーを指定する方法
モデルを関連づけるための外部キーは、デフォルトでは「相手側のモデル名(単数形)_id」が使用されます。
Employeesでいうと、「section_id」がSectionsとの外部キーになります。
この外部キーは、別の名前を指定することもできます。
「sec_id」を外部キーの名前としたい場合は、TableオブジェクトのbelongsToの定義と同時に、以下のように記述します。
$this->belongsTo('Sections') ->setForeignKey('sec_id');
配列を使用して記述することもできます。
$this->belongsTo('Sections',[ 'foreignKey' => 'sec_id' ]);
結合方法を指定する方法
belongsToでテーブルを結合した場合、結合方法は「LEFT JOIN」が使用されます。
「INNER JOIN」を使用したい場合は、TableオブジェクトのbelongsToの定義と同時に、以下のように記述します。
$this->belongsTo('Sections') ->setJoinType('INNER');
「INNER JOIN」を指定して、一覧にアクセスしてみます。
従業員テーブルのID4のデータは、部署IDが4であり、部署テーブルには存在しないIDです。
そのため、「LEFT JOIN」の時には従業員ID4のデータが表示されていましたが、「INNER JOIN」では表示されません。
階層の深いモデルのデータを取得する方法
部署テーブルと「多対1」の関連をもつテーブルを作成し、EmployeesのControllerからさらに階層の深いモデルのデータを取得してみます。
以下のようなオフィステーブルを作成します。
テーブル定義
create table offices ( id int not null auto_increment, -- ID office_name varchar(32), -- オフィス名 primary key (id) );
初期データ
ID | オフィス名 |
---|---|
1 | 東京 |
2 | 大阪 |
SectionsTableにbelongsToの定義を追加します。
public function initialize(array $config) { $this->belongsTo('Offices'); }
EmployeesControllerのcontainの部分を、以下のように記述します。
$query = $this->Employees->find('all')->contain(['Sections', 'Sections.Offices'])->order('Employees.id');
「.(ドット)」を使いモデル名を連結することで、さらに階層の深いモデルのデータも取得することができます。
Templateにオフィス名を追加し、表示してみます。
<table> <thead> <tr> <th>ID</th> <th>部署名</th> <th>オフィス</th> <th>従業員名</th> <th>年齢</th> </tr> </thead> <tbody> <?php foreach ($employees as $employee): ?> <tr> <td><?= $employee->id ?></td> <td><?= $employee->section ? $employee->section->section_name : '-' ?></td> <td><?= $employee->section ? $employee->section->office->office_name : '-' ?></td> <td><?= $employee->employee_name ?></td> <td><?= $employee->age ?></td> </tr> <?php endforeach; ?> </tbody> </table>
Officesのデータも取得できました。
まとめ
今回はアソシエーションのひとつであるbelongsToについて解説しました。
アソシエーションの種別をひとつずつ覚えて、使いこなせるようになりましょう。
belongsToについて忘れてしまったら、この記事を思い出して下さい!