【CakePHP入門】関連するテーブルデータを検索する方法(contain)

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

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テーブルの初期データ

idnameinsert_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テーブルの初期データ

idtitlebook_category_id
1線形代数入門1
2力学2
3量子論2
4PHP入門3
5Javaプログラミング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');

ControllerTemplateを作成し、確認してみます。

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

まとめ

今回は関連するテーブルデータを検索する方法について解説しました。

複数のテーブルを使用するシステム開発では、関連データの取得は必須です。

関連するテーブルデータを検索する方法を忘れてしまったら、この記事を思い出して下さい!

この記事を書いた人

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

目次