こんにちは!フリーエンジニアのせきです。
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/
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); } }
カテゴリ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’であるデータが表示されます。
‘id’のように2つのテーブルで同じカラム名がある場合は、「テーブル名.カラム名」の形で指定してください。
以下は、BookCategoriesのidが3のデータを取得します。
$query = $this->Books->find()->contain('BookCategories')->where(['BookCategories.id' => '3']);
BookCategoriesのid列を「BookCategories.id」と記述しています。
表示するとBookCategoriesのidが3のデータが表示されます。
まとめ
今回は関連するテーブルデータを検索する方法について解説しました。
複数のテーブルを使用するシステム開発では、関連データの取得は必須です。
関連するテーブルデータを検索する方法を忘れてしまったら、この記事を思い出して下さい!