トレイト

トレイトの基本

トレイトとは、メソッドやプロパティをまとめて切り出しておくための仕組み。「断片的なクラス」のようなもので、自分自身をインスタンス化することはできない。

<?php
// トレイトを定義
trait MachineTrait
{
    private $starting = 'Starting...Run!';

    public function run()
    {
        print $this->starting;
    }
}
?>
<?php
class Fax
{
    // トレイトをインポート
    use MachineTrait;
 
    public function send()
    {
        print 'Sending Fax...Sended!';
    }
}

$fax = new Fax();
$fax->run(); // Starting...Run!
?>

traitブロックの構文はほぼclassブロックに準ずるが、以下の制約がある。

トレイトとインターフェイス

<?php
interface IFax
{
    function send();
}

interface IPrinter
{
    function print();
}

trait FaxTrait
{
    public function send() {
        print 'Sending...';
    }
}

trait PrinterTrait
{
    public function print() {
        print 'Printing...';
    }
}

class FaxPrinter implements IFax, IPrinter
{
    use FaxTrait, PrinterTrait;
}

$fax_printer = new FaxPrinter
$fax_printer->send(); // 'Sending...'
?>

クラスの多重継承は禁止されているが、トレイトはクラスから複数利用することができる。
インターフェイスも同様にクラスから複数利用することができるが、インターフェイス

  • 実装を持たない型の宣言に過ぎない(内容はそれをオーバーライドするメソッドが決める)

のに対し、トレイトは

  • 実装を表す(ただし型を表現できない)

という相違点がある。トレイトはインターフェイスとあわせて利用するのがお作法らしいが、インターフェイスを定義せずそのままトレイトをインポートするだけでもコードは正しく動作する。

定数

クラス定数へのアクセス

クラス名::定数でアクセスする。同クラスからは、self::定数でアクセスする。

<?php
class MyClass
{
    const CONSTANT = 'constant value';

    function showConstant()
    {
        echo  self::CONSTANT . "\n"; // 同クラスからのアクセス
    }
}

echo MyClass::CONSTANT . "\n";
?>

cakePHPにおいて、他クラスの定数にアクセスする際の実装例。

<?php
use App\...\OtherClass;

class MyClass
{
    function __construct()
    {
        $this->OtherClass = TableRegistry::get('OtherClass'); // テーブル
        $this->OtherClass = new OtherClass(); // その他 サービスクラスなど
    }

    function showConstant()
    {
        echo  $this->OtherClass::CONSTANT . "\n";
    }
}
?>

クラス定数のアクセス範囲

PHP7.1以降、クラス定数のアクセス範囲を以下の記法で指定できる。

<?php
class Foo {
    public const BAR = 'bar';
    private const BAZ = 'baz';
}
?>

クラス定数のデフォルトのアクセス範囲はpublic。

define

define(定数名, 値)で定数を定義できる。

<?php
define("CONSTANT", "Hello world.");
?>

エラーと例外処理

cakePHPでは、エラー処理と例外処理が用意されている。PHPエラーはトラップされ、表示またはログに記録される。キャッチされなかった例外はエラーページに自動的にレンダリングされる。

例外処理の設定

例外処理の設定はconfig/app.phpで行われる。デフォルトではCake\Error\ErrorHandlerを使う。

例外処理のカスタマイズ

例外の処理方法を調整するいくつかの方法が用意されている。

エラーテンプレートのカスタマイズ

エラーページのビューはApp/Template/Errorに配置される。
すべての 4xx エラーは error400.ctp テンプレートを使い、 すべての 5xx エラーは error500.ctp を使う。
エラーテンプレートの変数は次の通り。

  • message: 例外メッセージ
  • code: 例外コード
  • url: リクエストURL
  • error: 例外オブジェクト
<?php
// src/Template/Error/error400.ctp の中で
$this->layout = 'my_error';
?>

上記は、エラーページのレイアウトとして src/Template/Layout/my_error.ctp を使用する。

ErrorControllerのカスタマイズ

App\Controller\ErrorController クラスは CakePHP の例外レンダリングでエラーページビューを 描画するために使われ、すべての標準リクエストライフサイクルイベントを受け取る。

<?php
namespace App\Controller\Admin;

use App\Controller\AppController;
use Cake\Event\EventInterface;

class ErrorController extends AppController
{
    /**
     * Initialization hook method.
     *
     * @return void
     */
    public function initialize(): void
    {
        $this->loadComponent('RequestHandler');
    }

    /**
     * beforeRender callback.
     *
     * @param \Cake\Event\EventInterface $event Event.
     * @return void
     */
    public function beforeRender(EventInterface $event)
    {
        $this->viewBuilder()->setTemplatePath('Error');
    }
}
?>

どのコンポーネントが使用され、どのテンプレートが描画されるかを制御する。

独自アプリケーション例外の作成

組み込みの SPL の例外 、 Exception そのもの、または Cake\Core\Exception\Exception のいずれかを使って、独自のアプリケーション例外を作ることができる。

<?php
use Cake\Core\Exception\Exception;

class MissingWidgetException extends Exception
{
    // コンテキストデータはこのフォーマット文字列に差し込まれます。
    protected $_messageTemplate = '%s が見当たらないようです。';

    // デフォルトの例外コードも設定できます。
    protected $_defaultCode = 404;
}

throw new MissingWidgetException(['widget' => 'Pointy']);
?>

上記の例ではMissingWidgetExceptionという例外を作成している。このときテンプレートはsrc/Template/Error/missing_widget.ctpになる。

ExceptionRendererのカスタマイズ

アプリケーション固有の例外クラスに対してカスタムエラーページを提供する。カスタム例外レンダラークラスは src/Error に配置する。

<?php
// src/Error/AppExceptionRenderer.php の中で
namespace App\Error;

use Cake\Error\ExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{
    public function missingWidget($error)
    {
        $response = $this->controller->response;
        return $response->withStringBody('おっとウィジェットが見つからない!');
    }
}
?>
// config/app.php の中で
'Error' => [
    'exceptionRenderer' => 'App\Error\AppExceptionRenderer',
    // ...
],
// ...
?>

上記の例では、カスタム例外レンダラークラスの中でMissingWidgetExceptionに対して missingWidgetというメソッドを作成している。

cakePHP用の組み込みの例外

  • Cake\Http\Exception\BadRequestException: 400 Bad Request
  • Cake\Http\Exception\UnauthorizedException: 401 Unauthorized
  • Cake\Http\Exception\ForbiddenException: 403 Forbidden
  • Cake\Http\Exception\InvalidCsrfTokenException: (無効なCSRFトークンによる)403 Forbidden
  • Cake\Http\Exception\NotFoundException: 404 Not Found
  • Cake\Http\Exception\MethodNotAllowedException: 405 Method Not Allowed
  • Cake\Http\Exception\NotAcceptableException: 406 Not Acceptable
  • Cake\Http\Exception\NotAcceptableException: 406 Not Acceptable
  • Cake\Http\Exception\ConflictException: 409 Conflict
  • Cake\Http\Exception\GoneException: 410 Gone
  • Cake\Http\Exception\InternalErrorException: 500 Internal Server Error
  • Cake\Http\Exception\NotImplementedException: 501 Not Implemented
  • Cake\Http\Exception\ServiceUnavailableException: 503 Service Unavailable などなど
<?php
use Cake\Http\Exception\NotFoundException;

public function view($id = null)
{
    $article = $this->Articles->findById($id)->first();
    if (empty($article)) {
        throw new NotFoundException(__('記事が見つかりません'));
    }
    $this->set('article', $article);
    $this->set('_serialize', ['article']);
}
?>

コレクション

コレクションクラスは、配列(またはTraversableオブジェクト)を操作するためのメソッドを提供する。

コレクションの生成

<?php

use Cake\Collection\Collection;

$items = ['apple', 'banana', 'kiwi'];
$collection = new Collection($items);

?>

反復処理に関するメソッド

each

コレクション内の各オブジェクトに対して関数を実行する。

<?php
$collection->each(function ($value, $key) {
    echo "要素 $key: $value";
});
?>
map

コレクション内の各オブジェクトに対して関数を実行し、新しいコレクションを返す。

<?php
$new = $collection->map(function ($value, $key) {
    return $value * 2;
});
?>
extract

特定のプロパティの値を含むコレクションを作成する。

<?php
$collection = new Collection($people);
$names = $collection->extract('name');

// $result には ['mark', 'jose', 'barbara'] が含まれています。
$result = $names->toList();
?>

取得したいプロパティがパスで表現できない場合は、それを返すようにコールバック関数を使用できる。

<?php
$collection = new Collection($articles);
$names = $collection->extract(function ($article) {
    return $article->author->name . ', ' . $article->author->last_name;
});
?>
combine

combine('keyにするプロパティ', 'valueにするプロパティ'); で、キーと値から作られた新しいコレクションを作成できる。

<?php
$items = [
    ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
    ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
    ['id' => 3, 'name' => 'baz', 'parent' => 'a'],
];
$combined = (new Collection($items))->combine('id', 'name');

// 配列に変換すると、結果は次のようになります。
[
    1 => 'foo',
    2 => 'bar',
    3 => 'baz',
];
?>
unfold

すべての要素に対する反復処理が一回で済むように、コレクションの内部構造を平坦化する。

<?php
$items = [[1, 2, 3], [4, 5]];
$collection = new Collection($items);
$new = $collection->unfold();

// $result には [1, 2, 3, 4, 5] が含まれています。
$result = $new->toList();
?>
chunk

コレクションを任意の大きさに分割する。連想配列を分割する場合は、chunkWithKeys()を用いる。

<?php
$items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
$collection = new Collection($items);
$chunked = $collection->chunk(2);
$chunked->toList(); // [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]]
?>

フィルタリングに関するメソッド

filter

基準のコールバックに一致する要素の新しいコレクションを生成する。逆に一致する要素を取り除いたコレクションを生成する場合は、reject()を用いる。

<?php
$collection = new Collection($people);
$ladies = $collection->filter(function ($person, $key) {
    return $person->gender === 'female';
});
?>
every

コレクションのすべての要素がコールバックの条件を満たしているかを判定する。少なくとも1つの要素が条件を満たしているかを判定する場合は、some()を用いる。

<?php
$collection = new Collection($people);
$ladies = $collection->filter(function ($person, $key) {
    return $person->gender === 'female';
});
?>
match

指定したプロパティの特定の値を持つ要素の新しいコレクションを生成する。最初に一致した要素を取得する場合は、firstMatch()を用いる。

<?php
$collection = new Collection($comments);
$commentsFromMark = $collection->match(['user.name' => 'Mark']);
?>

要素の取得に関するメソッド

sample

指定した数の要素を取得する。

<?php
$collection = new Collection($people);

// このコレクションからランダムに最大 20 ユーザーを取り出します。
$testSubjects = $collection->sample(20);
?>
take

コレクションのスライスを取得する。

<?php
// ポジション 4 から始まるコレクションから5人取り出します。
$nextTopFive = $collection->sortBy('age')->take(5, 4);
?>
first

コレクションの最初の要素を取得する。最後の要素を取得する場合はlast()を用いる。

<?php
$collection = new Collection([5, 4, 3, 2]);
$collection->first(); // 戻り値は 5
?>

集約に関するメソッド

reduce

コレクションの各要素の値を集約し一つの値を取得する。reduceの第一引数のコールバックの第一引数に集約した値を格納する変数、第二引数にコレクションの要素を表す変数を渡す。reduceの第二引数は初期値。

<?php
$totalPrice = $collection->reduce(function ($accumulated, $orderLine) {
    return $accumulated + $orderLine->price;
}, 0); // $accumulatedの初期値
?>
min

指定したプロパティの値が最小である要素を取得する。最大である要素を取得する場合はmax()を用いる。

<?php
$collection = new Collection($people);
$youngest = $collection->min('age');
?>
sumOf

すべての要素の指定したプロパティの値の合計を取得する。

<?php
$collection = new Collection($people);
$sumOfAges =  $collection->sumOf('age');
?>
avg

すべての要素の指定したプロパティの値の平均を取得する。中央値を取得する場合はmedian()を用いる。

<?php
$items = [
   ['invoice' => ['total' => 100]],
   ['invoice' => ['total' => 200]],
];

// 平均値: 150
$average = (new Collection($items))->avg('invoice.total');
?>
groupBy

コレクションの要素がプロパティに同じ値を持つ場合、キー別にグループ化した新しいコレクションを取得できる。

<?php
$students = [
    ['name' => 'Mark', 'grade' => 9],
    ['name' => 'Andrew', 'grade' => 10],
    ['name' => 'Stacy', 'grade' => 10],
    ['name' => 'Barbara', 'grade' => 9]
];
$collection = new Collection($students);
$studentsByGrade = $collection->groupBy('grade');

// 配列に変換すると、結果は次のようになります。
[
  10 => [
    ['name' => 'Andrew', 'grade' => 10],
    ['name' => 'Stacy', 'grade' => 10]
  ],
  9 => [
    ['name' => 'Mark', 'grade' => 9],
    ['name' => 'Barbara', 'grade' => 9]
  ]
]
?>

コールバックを渡すことで動的にコレクションを生成できる。

<?php
$classResults = $students->groupBy(function ($student) {
    return $student->grade > 6 ? 'approved' : 'denied';
});
?>
countBy

グループごとの出現数を取得する。

<?php
$classResults = $students->countBy(function ($student) {
    return $student->grade > 6 ? 'approved' : 'denied';
});

// 配列に変換すると、結果は次のようになります。
['approved' => 70, 'denied' => 20]
?>

ソートに関するメソッド

sortBy

指定したプロパティによってソートされた新しいコレクションを取得する。デフォルトでは降順並べ替えになっており、昇順の場合は第二引数にSORT_ASCを渡す。

<?php
$collection = new Collection($people);
$sorted = $collection->sortBy('age');
$sorted = $collection->sortBy('age', SORT_ASC); // 昇順

$sorted_by_association = $collection->sortBy('address.country'); // 関連するモデルの値でソート
?>

コレクションの拡張に関するメソッド

append

複数のコレクションから、両方のソースの値を含む新しい一つのコレクションを生成する。

<?php
// cakephpに関するツイートとphpに関するツイートのコレクションを統合
$cakephpTweets = new Collection($tweets);
$myTimeline = $cakephpTweets->append($phpTweets);

// 両方のソースから cakefest を含むつぶやきを取得
$myTimeline->filter(function ($tweet) {
    return strpos($tweet, 'cakefest');
});
?>
appendItem

オプションのキーを持つアイテムをコレクションに追加する。

<?php
$cakephpTweets = new Collection($tweets);
$myTimeline = $cakephpTweets->appendItem($newTweet, 99);
?>

その他のメソッド

<?php
$collection->isEmpty(); // コレクションに要素が含まれているか確認
$collection->contains('foo'); // コレクションに特定の値が含まれているか確認
$collection->shuffle(); // 要素の順序をシャッフル
?>

フォームコントロール

フォームコントロールの生成

control('フィールド名', [オプション配列]); を使うと完全のフォームコントロールを生成できる。

<?php

echo $this->Form->create($user);
echo $this->Form->control('username');
echo $this->Form->control('password');
// 'approved' を datetime か timestamp フィールドとみなし、
// 以下は、日・月・年・時・分を生成します
echo $this->Form->control('approved');
// 以下は、テキストエリア要素を生成します
echo $this->Form->control('quote');

echo $this->Form->button('Add');
echo $this->Form->end();

?>

これらのコントロールには、必要に応じて囲い込む div 、 label 、コントロールウィジェット、および検証エラーが含まれる。
また、作成されたコントロールの型はモデルの内部で推測され、カラムのデータ型に依存する。

フィールドの命名規則

フィールド名はエンティティの属性と一致させる必要がある。

<?php
echo $this->Form->create($article);
echo $this->Form->control('title');
echo $this->Form->control('publisher.name'); // 関連するモデルのフォームを生成
?>

control('publisher.name')というフィールドを作成した場合、publisher[name]というname属性になる。

コントロールのオプション

type

フォームのタイプを指定。

<?=
$this->Form->control('field', [
    'type' => 'file'
]); 
?>

出力結果

<div class="input file">
    <label for="field">Field</label>
    <input type="file" name="field" value="" id="field" />
</div>
label

ラベルの見出しを指定。

<?= 
$this->Form->control('name', [
    'label' => 'The User Alias'
]);
?>

出力結果

<div class="input">
    <label for="name">The User Alias</label>
    <input name="name" type="text" value="" id="name" />
</div>
error

デフォルトのエラーメッセージ表示を無効にできる。

<?= $this->Form->control('name', ['error' => false]); ?>

フォーム全体の生成

複数のフォームコントロールの作成

controls([フィールド名の配列], [オプション配列]); で複数のフォームを生成できる。

<?=
$this->Form->controls([
    'name',
    'email'
]);
?>

フォームにオプションを追加する場合。

<?=
$this->Form->controls([
    'name' => ['label' => 'カスタムラベル']
]);
?>
エンティティ全体のフォームコントロールを作成

allControls([フィールド名のオプション配列], [オプション配列]);

<?= $this->Form->allControls(['password' => false]); ?>

'フィールド名' => falseとすると、その属性のフォームの生成が無効になる。

フォーム

フォームの開始

エンティティの新規登録と編集フォームはどちらも以下のロジックから開始される。

  • $articleが空のエンティティの場合、新規登録フォームが生成される。
  • $articleが既存のエンティティの場合、エンティティの編集フォームが生成される。
<?php
$this->Form->create($article);
?>

上のロジックでフォームの開始タグが生成される。

<form method="post" action="/users/add">

input要素の生成

テキスト
<?php
$this->Form->text('username', ['class' => 'users']); // テキストフィールド
$this->Form->password('password'); // パスワードフィールド
$this->Form->hidden('id'); // 非表示フィールド
$this->Form->textarea('comment', ['rows' => '5', 'cols' => '5']); // テキストエリア
?>

このロジックで、それぞれ以下のフォームが出力される。

<input name="username" type="text" class="users">

<input name="password" value="" type="password">

<input name="id" value="(int)" type="hidden" />

<textarea name="comment" cols="5" rows="5"></textarea>
チェックボックス

checkbox('フィールド名', [オプション]);

<?php
$this->Form->checkbox('done');
?>

出力結果

<input type="hidden" name="done" value="0">
<input type="checkbox" name="done" value="1">
セレクトボックス

select('フィールド名', [選択項目を含むオプション配列]);

<?php
$this->Form->select(
    'field', 
    [1, 2, 3, 4, 5]
);
?>

出力結果

<select name="field">
    <option value="0">1</option>
    <option value="1">2</option>
    <option value="2">3</option>
    <option value="3">4</option>
    <option value="4">5</option>
</select>

オプション配列の選択項目は連想配列でも可。キーがvalue属性、値がラベルの見出しになる。

<?php
$this->Form->select('field', [
    'Value 1' => 'Label 1',
    'Value 2' => 'Label 2',
    'Value 3' => 'Label 3'
]);
?>
<select name="field">
    <option value="Value 1">Label 1</option>
    <option value="Value 2">Label 2</option>
    <option value="Value 3">Label 3</option>
</select>
ラジオボタン

radio('フィールド名', [選択項目を含むオプション配列]);

<?php
$this->Form->radio('gender', ['Masculine','Feminine','Neuter']);
?>

出力結果

<input name="gender" value="" type="hidden">
<label for="gender-0">
    <input name="gender" value="0" id="gender-0" type="radio">
    Masculine
</label>
<label for="gender-1">
    <input name="gender" value="1" id="gender-1" type="radio">
    Feminine
</label>
<label for="gender-2">
    <input name="gender" value="2" id="gender-2" type="radio">
    Neuter
</label>
ファイル入力

file('フィールド名');

<?= $this->Form->create($document, ['type' => 'file']); ?> // まずフォーム開始時にtypeをfileに指定
    <?= $this->Form->file('submittedfile'); ?>
日時選択
<?php
$this->Form->dateTime('published'); // 日時

$this->Form->date('registered', [ // 日付
    'minYear' => 2018
]);

$this->Form->time('released', [ // 時間
    'interval' => 15
]);

$this->Form->year('published', [ // 年
    'minYear' => 2000,
    'maxYear' => date('Y')
]);

$this->Form->month('published'); // 月
$this->Form->day('published'); // 日

$this->Form->hour('created', [ // 時
    'format' => 24
]);

$this->Form->minute('arrival', [ // 分
    'interval' => 10
]);
?>

ラベルの生成

label('フィールド名', 見出しのオプション文字列', [オプション配列]);

<?php
$this->Form->label('name', 'Your username', ['class' => 'highlight']);
?>

出力結果

<label for="name" class="highlight">Your username</label>

エラーメッセージの表示

error('フィールド名', 'エラーメッセージの文字列or配列');

<?php
$this->Form->error('ticket', 'Completely custom error message!');
?>

出力結果

<div class="error-message">Completely custom error message!</div>

ボタンの生成

送信ボタン

submit('ボタンの見出し');

<?php
$this->Form->submit('Submit!');
?>

出力結果

<div class="submit"><input value="Submit!" type="submit"></div>
ボタン全般

button('ボタンの見出し', [オプション配列]);

<?php
$this->Form->button('標準のボタン', ['type' => 'button']);
$this->Form->button('フォームのリセット', ['type' => 'reset']);
$this->Form->button('フォームの送信', ['type' => 'submit']);
?>

出力結果

<button type="button">別のボタン</button>
<button type="reset">フォームのリセット</button>
<button type="submit">フォームの送信</button>

フォームの終了

<?php
$this->Form->end();
?>

フォームのオプション

type

作成するフォームのHTTPメソッドを指定。'get', 'post', 'put', 'patch', 'delete'が指定できる。

<?php
$this->Form->create($article, ['type' => 'get']);
?>
url

フォームを送信するURLを指定。フォームを特定のアクションに向ける。

<?php
$this->Form->create($article, ['url' => ['action' => 'publish']]);
?>

目的のアクションが現在のコントローラにない場合。

<?php
$this->Form->create(null, [
    'url' => [
        'controller' => 'Articles',
        'action' => 'publish'
    ]
]);
?>

外部ドメインを指定する場合。

<?php
$this->Form->create(null, [
    'url' => 'http://www.google.com/search',
    'type' => 'get'
]);
?>
validator

フォームにカスタムバリデーターを適用させる。

<?php
$this->Form->create($user, [
    'context' => ['validator' => 'register']
]);
?>

上の例では、UsersTable::validationRegister() で定義されている register バリデーターを$userに適用する。

その他のオプション

アソシエーション

アソシエーション

アソシエーションはテーブルオブジェクトの initialize() の中で定義される。関連付けたいテーブルのエイリアスをメソッドに渡す。デフォルトでは

<?php
namespace App\Model\Table;
use Cake\ORM\Table;

class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Authors');
    }
}
?>

関連付けたいテーブルのエイリアスをbefongsToメソッドに渡す。上の例では規約によりauthersテーブルと紐づく。

<?php
class CategoriesTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasMany('SubCategories', [
            'className' => 'Categories'
        ]);

        $this->belongsTo('ParentCategories', [
            'className' => 'Categories'
        ]);
    }
}
?>

classNameオプションで関連づくモデルの名前を指定し、エイリアスとしては異なる名称を渡すときの例。このように、同一テーブルを異なるエイリアスとして定義したりできる。

hasOne

<?php
class UsersTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasOne('Address');
    }
}
?>

hasOneで使用できるオプション

  • className: 当該のモデルに関連付けられるモデルのクラス名。
  • foreignKey: 相手側のテーブル上の外部キーの名前。デフォルトでは当該モデル名_id 'user_id'など。
  • bindingKey: foreignKey での紐付けに使用される、当該のテーブルのカラム名。デフォルトでは主キーが使われる。
  • conditions: ['Addresses.primary' => true] のような find() 互換の条件の配列。
  • joinType: SQL クエリーで使われる結合の種別。デフォルトはLEFT。
  • dependent: trueのとき、エンティティーが削除されたときに関連付けられたモデルのレコードも削除される。
  • propertyName: 関連付けられたテーブルからソースのテーブルの結果にデータを埋める際のプロパティー名。デフォルトでは'user'など。
  • strategy: クエリーで使うためのストラテジー。既定は 'join'。他に'select'が有効。
  • finder: 関連付けられたレコードを読み込む時に使われるファインダーメソッド。

belongsTo

<?php
class AddressesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Users');
    }
}
?>

belongsToで使用できるオプション

  • className: 当該のモデルに関連付けられるモデルのクラス名。
  • foreignKey: 当該テーブル上の外部キーの名前。デフォルトでは相手側モデル名_id 'user_id'など。
  • bindingKey: foreignKey での紐付けに使用される、相手側のテーブルのカラム名。デフォルトでは主キーが使われる。
  • conditions: ['Users.active' => true] のような find() 互換の条件の配列。
  • joinType: SQL クエリーで使われる結合の種別。デフォルトはLEFT。
  • propertyName: 関連付けられたテーブルからソースのテーブルの結果にデータを埋める際のプロパティー名。デフォルトでは'user'など。
  • strategy: クエリーで使うためのストラテジー。既定は 'join'。他に'select'が有効。
  • finder: 関連付けられたレコードを読み込む時に使われるファインダーメソッド。

hasMany

<?php
class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->hasMany('Comments');
    }
}
?>

hasManyで使用できるオプション

  • className: 当該のモデルに関連付けられるモデルのクラス名。
  • foreignKey: 相手側のテーブル上の外部キーの名前。デフォルトでは当該モデル名_id 'article_id'など。
  • bindingKey: foreignKey での紐付けに使用される、当該のテーブルのカラム名。デフォルトでは主キーが使われる。
  • conditions: ['Comments.visible' => true] のような find() 互換の条件の配列。
  • sort: ['Comments.created' => 'ASC'] のような find() 互換の order 句の配列。
  • dependent: trueのとき、エンティティーが削除されたときに関連付けられたモデルのレコードも削除される。
  • propertyName: 関連付けられたテーブルからソースのテーブルの結果にデータを埋める際のプロパティー名。デフォルトではアソシエーションの名前の複数形'comments'など。
  • strategy: クエリーで使うためのストラテジー。既定は 'select'。他に'subquery'が有効で、これは IN のリストを等価のサブクエリーに置き換える。
  • finder: 関連付けられたレコードを読み込む時に使われるファインダーメソッド。

belongsToMany

<?php
// src/Model/Table/ArticlesTable.php の中で
class ArticlesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Tags');
    }
}

// src/Model/Table/TagsTable.php の中で
class TagsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Articles');
    }
}
?>

belongsToManyで使用できるオプション

  • className: 当該のモデルに関連付けられるモデルのクラス名。
  • joinTable: このアソシエーションで使われる結合テーブルの名前。
  • foreignKey: 結合テーブル上の当該のモデルを参照する外部キーの名前。デフォルトでは当該モデル名_id 'article_id'など。
  • bindingKey: foreignKey での紐付けに使用される、当該のテーブルのカラム名。デフォルトでは主キーが使われる。
  • targetForeignKey: 結合モデル上の対象モデルを参照する外部キーの名前。デフォルトでは当該モデル名_id 'article_id'など。
  • conditions: ['Comments.visible' => true] のような find() 互換の条件の配列。
  • sort: ['Comments.created' => 'ASC'] のような find() 互換の order 句の配列。
  • dependent: falseのとき、エンティティーが削除されても結合テーブルのレコードは削除されない。
  • through: 結合テーブルで使用する Table インスタンスエイリアス。これにより、結合テーブルのキーのカスタマイズが可能になる。詳細は次節。
  • propertyName: 関連付けられたテーブルからソースのテーブルの結果にデータを埋める際のプロパティー名。デフォルトではアソシエーションの名前の複数形'tags'など。
  • strategy: クエリーで使うためのストラテジー。既定は 'select'。他に'subquery'が有効で、これは IN のリストを等価のサブクエリーに置き換える。
  • finder: 関連付けられたレコードを読み込む時に使われるファインダーメソッド。
throughオプション

結合テーブルに追加の情報をもたせたり、規約から外れる結合カラムを使用する場合、throughオプションを定義する必要がある。

Student BelongsToMany Course
Course BelongsToMany Student

このようなアソシエーションがあるとき、結合テーブルは以下のカラムで事足りるが、

id | student_id | course_id

このように追加の情報をもたせたい場合もある。

id | student_id | course_id | days_attended | grade

そうした場合、結合テーブルのクラスを定義し(必要であれば追加の情報をもたせるなどの設定を行い)、throughオプションで結合テーブルを指定する。

<?php
class StudentsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Courses', [
            'through' => 'CoursesMemberships',
        ]);
    }
}

class CoursesTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Students', [
            'through' => 'CoursesMemberships',
        ]);
    }
}

class CoursesMembershipsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Students');
        $this->belongsTo('Courses');
    }
}
?>