こんにちは!フリーエンジニアのせきです。
CakePHPには、テーブルデータを検索する時に、関連するテーブルデータも一緒に取得する方法があります。
この記事では、
・containとは何か知りたい
・containで関連するテーブルデータを検索する方法を知りたい
という基本的な内容から、
・containで条件を指定して検索する方法を知りたい
といった応用的な内容に関しても解説していきます。
今回はそんな関連するテーブルデータを検索する方法について、わかりやすく解説します!
containとは
containとは、テーブルデータを検索する時に、関連するテーブルデータも一緒に取得することができる機能です。
ただデータが取得できるだけでなく、取得する列を指定したり、条件を指定したりすることもできます。
今回は、book_categoriesテーブル、booksテーブルの2つのテーブルを使ってcontainの動きを確認していきます。
booksテーブルはカテゴリIDをデータに持ち、そのカテゴリはbook_categoriesテーブルで管理されているものとします。
book_categoriesテーブルのテーブル定義
create table book_categories
(
id int not null auto_increment, -- カテゴリID
name varchar(32), -- カテゴリ名
insert_date date, -- 登録日時
primary key (ID)
);
book_categoriesテーブルの初期データ
id | name | insert_date |
1 | 数学 | 2015-01-20 |
2 | 物理 | 2016-01-01 |
3 | プログラミング | 2017-12-20 |
booksテーブルのテーブル定義
create table books
(
id int not null auto_increment, -- 本ID
title varchar(32), -- タイトル
book_category_id int, -- カテゴリID
primary key (ID)
);
booksテーブルの初期データ
id | title | book_category_id |
1 | 線形代数入門 | 1 |
2 | 力学 | 2 |
3 | 量子論 | 2 |
4 | PHP入門 | 3 |
5 | Javaプログラミング | 3 |
それぞれのModelを作成しておきます。
book_categoriesテーブルのModel
src\Model\Entity\BookCategory.php
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class BookCategory extends Entity
{
}
src\Model\Table\BookCategoriesTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class BookCategoriesTable extends Table
{
public function initialize(array $config)
{
}
}
booksテーブルのModel
src\Model\Entity\Book.php
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Book extends Entity
{
}
src\Model\Table\BooksTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class BooksTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('BookCategories');
}
}
booksテーブルとbook_categoriesテーブルは、1つのカテゴリIDに対し複数のbooksデータが存在するので、「多対1」という関係になります。
「多対1」の関連はbelongsToで定義できるので、BooksTableには「$this->belongsTo('BookCategories');」の記述が必要になります。
containの基本的な使い方
全列を取得する
booksテーブルと、それに関連するbook_categoriesテーブルのデータをcontainを使って検索します。
containは、検索するfindメソッドのオプションとして指定する方法と、クエリオブジェクト(SQLを実行するためのオブジェクト)のメソッドとして記述する方法があります。
findメソッドのオプションとして指定する場合は、BooksのControllerに以下のように記述します。
$query = $this->Books->find('all', ['contain' => 'BookCategories']);
クエリオブジェクトのメソッドとして記述する場合は、BooksのControllerに以下のように記述します。
$query = $this->Books->find('all')->contain('BookCategories');
ControllerとTemplateを作成し、確認してみます。
Controllerです。
containをメソッドとして記述する方法を使っています。
src\Controller\BooksController.php
<?php
namespace App\Controller;
use App\Controller\AppController;
class BooksController extends AppController
{
public function index()
{
$query = $this->Books->find('all')->contain(['BookCategories']);
$this->set('books', $query);
}
}
Templateです。
src\Template\Books\index.ctp
<table>
<thead>
<tr>
<th>ID</th>
<th>タイトル</th>
<th>カテゴリID</th>
<th>カテゴリ名</th>
<th>カテゴリ登録日</th>
</tr>
</thead>
<tbody>
<?php foreach ($books as $book): ?>
<tr>
<td><?= $book->id ?></td>
<td><?= $book->title ?></td>
<td><?= $book->book_category->id ?></td>
<td><?= $book->book_category->name ?></td>
<td><?= $book->book_category->insert_date ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
表示してみます。
http://[サーバ名]/[プロジェクト名]/books/
![001]()
book_categoriesテーブルのすべての列が取得できています。
列を指定して取得する
containを使って他のテーブルデータを取得する時、必要な列だけ取得することもできます。
以下は、id列とname列を取得する記述方法です。
$query = $this->Books->find()->contain(['BookCategories' => function ($q) {
return $q->select(['id', 'name']);
}]);
containの引数に渡す配列に「'テーブル名' => function($q){}」のように関数を指定すると、$qでcontain先のテーブルのクエリオブジェクトを使うことができます。
そのため、「$q->select(['id', 'name'])」でid列とname列が取得されます。
BooksControllerを以下のように修正し、表示してみます。
<?php
namespace App\Controller;
use App\Controller\AppController;
class BooksController extends AppController
{
public function index()
{
$query = $this->Books->find()->contain(['BookCategories' => function ($q) {
return $q->select(['id', 'name']);
}]);
$this->set('books', $query);
}
}
![002]()
カテゴリIDとカテゴリ名は取得しているので表示され、カテゴリ登録日は表示されていません。
containの応用的な使い方
条件を指定して検索する
containを使って取得した他のテーブルデータを、元のテーブルの検索条件に指定することができます。
カテゴリ登録日が'2016-01-01'であるBooksテーブルのデータを取得します。
<?php
namespace App\Controller;
use App\Controller\AppController;
class BooksController extends AppController
{
public function index()
{
$query = $this->Books->find()->contain('BookCategories')->where(['insert_date' => '2016-01-01']);
$this->set('books', $query);
}
}
whereメソッドでBookCategoriesテーブルの'insert_date'を使用することができます。
表示するとinsert_dateが'2016-01-01'であるデータが表示されます。
![003]()
'id'のように2つのテーブルで同じカラム名がある場合は、「テーブル名.カラム名」の形で指定してください。
以下は、BookCategoriesのidが3のデータを取得します。
$query = $this->Books->find()->contain('BookCategories')->where(['BookCategories.id' => '3']);
BookCategoriesのid列を「BookCategories.id」と記述しています。
表示するとBookCategoriesのidが3のデータが表示されます。
![004]()
まとめ
今回は関連するテーブルデータを検索する方法について解説しました。
複数のテーブルを使用するシステム開発では、関連データの取得は必須です。
関連するテーブルデータを検索する方法を忘れてしまったら、この記事を思い出して下さい!