あっぽログ
← 記事一覧に戻る

CakePHPのORM入門:テーブル・エンティティ・クエリビルダを使いこなす

CakePHPのORMとは

CakePHPは、PHPで広く使われるフルスタックWebフレームワークです。その中でも**ORM(Object-Relational Mapper)**は、データベース操作をPHPオブジェクトとして扱えるようにする中核機能です。

CakePHPのORMはLaravelのEloquentとは設計思想が異なり、TableクラスとEntityクラスの2層構造になっています。

  • Tableクラス:テーブル全体を表し、クエリ・バリデーション・アソシエーションを担う
  • Entityクラス:1行のレコードを表すオブジェクト

この分離のおかげで、複雑なビジネスロジックを整理しやすくなっています。


環境準備

CakePHPプロジェクトはComposerで作成できます。

composer create-project --prefer-dist cakephp/app:~4.0 myapp
cd myapp

config/app_local.php にデータベース接続情報を設定します。

'Datasources' => [
    'default' => [
        'host'     => 'localhost',
        'username' => 'root',
        'password' => 'secret',
        'database' => 'myapp_db',
        'driver'   => 'Cake\Database\Driver\Mysql',
    ],
],

Tableクラスを作る

Artisanに相当するCakePHPのCLIツール「bake」でTableクラスを自動生成できます。

bin/cake bake model Articles

src/Model/Table/ArticlesTable.php が生成されます。手動で作る場合は以下のようになります。

<?php
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;

use Cake\ORM\Table;
use Cake\Validation\Validator;

class ArticlesTable extends Table
{
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('articles');       // テーブル名
        $this->setPrimaryKey('id');        // 主キー
        $this->setEntityClass('App\Model\Entity\Article');

        // タイムスタンプ自動更新
        $this->addBehavior('Timestamp');
    }

    // バリデーション定義
    public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->notEmptyString('title', 'タイトルは必須です')
            ->maxLength('title', 255, 'タイトルは255文字以内です')
            ->notEmptyString('body', '本文は必須です');

        return $validator;
    }
}

Entityクラスを作る

Entityは1レコードを表します。アクセス可能なフィールドを $_accessible で宣言します。

<?php
// src/Model/Entity/Article.php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class Article extends Entity
{
    // mass assignmentを許可するフィールド
    protected array $_accessible = [
        'title'      => true,
        'body'       => true,
        'published'  => true,
        'created'    => true,
        'modified'   => true,
    ];

    // 仮想プロパティ:タイトルを大文字に変換して返す
    protected function _getTitleUpper(): string
    {
        return strtoupper($this->title);
    }
}

コントローラから $article->title_upper でアクセスできます。


クエリビルダで検索する

基本的な取得

// コントローラ内での利用例
$articlesTable = $this->fetchTable('Articles');

// 全件取得
$articles = $articlesTable->find()->all();

// 主キーで1件取得
$article = $articlesTable->get(1);

// 条件付き取得
$published = $articlesTable->find()
    ->where(['published' => true])
    ->orderBy(['created' => 'DESC'])
    ->limit(10)
    ->all();

SELECT・LIKE・IN句

// 特定のカラムだけ取得
$titles = $articlesTable->find()
    ->select(['id', 'title'])
    ->all();

// LIKEで部分一致検索
$results = $articlesTable->find()
    ->where(['title LIKE' => '%CakePHP%'])
    ->all();

// IN句を使う
$specific = $articlesTable->find()
    ->where(['id IN' => [1, 2, 3]])
    ->all();

カスタムFinderを定義する

Tableクラスにカスタムfinderを追加することで、クエリを再利用しやすくなります。

// ArticlesTable.php に追加
public function findPublished(Query $query, array $options): Query
{
    return $query->where(['published' => true])
                 ->orderBy(['created' => 'DESC']);
}

呼び出しはシンプルです。

$articles = $articlesTable->find('published')->all();

レコードの保存・更新・削除

新規保存

$article = $articlesTable->newEmptyEntity();
$article = $articlesTable->patchEntity($article, [
    'title'     => 'CakePHP入門',
    'body'      => 'ORMを学ぼう',
    'published' => true,
]);

if ($articlesTable->save($article)) {
    echo "保存成功: ID = " . $article->id;
} else {
    // バリデーションエラーの取得
    $errors = $article->getErrors();
}

更新

$article = $articlesTable->get(1);
$article = $articlesTable->patchEntity($article, ['title' => '更新されたタイトル']);
$articlesTable->save($article);

削除

$article = $articlesTable->get(1);
$articlesTable->delete($article);

アソシエーション(テーブル間の関連)

CakePHPのORMは hasMany / belongsTo / hasOne / belongsToMany をサポートしています。

// ArticlesTable.php
public function initialize(array $config): void
{
    parent::initialize($config);

    // 1記事は複数コメントを持つ
    $this->hasMany('Comments', [
        'foreignKey' => 'article_id',
    ]);

    // 1記事は1ユーザーに属する
    $this->belongsTo('Users', [
        'foreignKey' => 'user_id',
    ]);
}

関連データを含めて取得するには contain を使います。

$article = $articlesTable->get(1, contain: ['Comments', 'Users']);

echo $article->user->name;
foreach ($article->comments as $comment) {
    echo $comment->body;
}

まとめ

CakePHPのORMをざっくりまとめると以下のとおりです。

概念役割
Tableクラステーブル全体の操作・バリデーション・アソシエーション
Entityクラス1レコードの表現・仮想プロパティ
QueryBuilder柔軟なSELECT / WHERE / ORDER句の構築
カスタムFinder再利用可能なクエリの定義

LaravelのEloquentに慣れた方には最初少し戸惑うかもしれませんが、責務が明確に分離されているため、大規模なアプリケーションでも管理しやすいのがCakePHP ORMの強みです。ぜひ実際のプロジェクトで試してみてください。

← 記事一覧に戻る