テーブルオブジェクトは特定のテーブルに保存されたエンティティーのコレクションへのアクセスを提供します。 それぞれのテーブルは、与えられたテーブルによって繋がれた関連付けられたテーブルクラスを持ちます。 もし、与えられたテーブルの振る舞いをカスタマイズする必要ないなら、CakePHP はテーブルのインスタンスを 作ります。
テーブルオブジェクトと ORM を作る前に データベースへの接続 がなされているか確かめましょう。
まずはじめにテーブルクラスを作ってください。これらのクラスは src/Model/Table に作ります。 テーブルは、リレーショナルデータベースに特化したモデルコレクションです。 そして、CakePHP の ORM の中で、あなたのデータベースへの主なインターフェースです。 最も基本的なテーブルクラスは次のようになります。
// src/Model/Table/ArticlesTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
}
このクラスで使用するテーブル名を ORM に伝えていないことに注目してください。規約により、
テーブルオブジェクトは、クラス名を小文字とアンダースコアー区切りにした名前のテーブルを使用します。
上記の例では articles
テーブルが使用されます。テーブルクラスが BlogPosts
という名前の場合、テーブルは blog_posts
と名付けてください。
あなたは、 setTable()
メソッドを使用することでテーブルを指定できます。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->setTable('my_table');
// 3.4 より前
$this->table('my_table');
}
}
テーブルを指定した時は、命名規則は適用されません。規約により、ORM はそれぞれのテーブルが
id
という名前の主キーを持っていることを前提としています。もし主キーの名前を変更する
必要がある場合、 setPrimaryKey()
メソッドが使用できます。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->setPrimaryKey('my_id');
// 3.4 より前
$this->primaryKey('my_id');
}
}
デフォルトではテーブルオブジェクトは命名規則に従った Entity クラスを使います。
たとえば、 ArticlesTable
というテーブルクラスの名前だったらエンティティーは Article
に、 PurchaseOrdersTable
というテーブルクラスの名前だったらエンティティーは PurchaseOrder
になります。もし命名規約に従わない場合は、 setEntityClass()
メソッドで設定を変えられます。
class PurchaseOrdersTable extends Table
{
public function initialize(array $config)
{
$this->setEntityClass('App\Model\Entity\PO');
// 3.4 より前
$this->entityClass('App\Model\Entity\PO');
}
}
上記の例では、テーブルオブジェクトはコンストラクターの最後に呼ばれる initialize()
メソッドを持ちます。コンストラクターをオーバーライドする代わりに、
このメソッドで初期化することを推奨します。
テーブルにクエリーを実行する前に、テーブルインスタンスを取得する必要があります。
TableRegistry
クラスを使用することで取得できます。
// コントローラーやテーブルのメソッド内で
use Cake\ORM\TableRegistry;
// Prior to 3.6 use TableRegistry::get('Articles')
$articles = TableRegistry::getTableLocator()->get('Articles');
TableRegistry クラスはテーブルを作るための様々な依存関係を提供します。 そして、作成されたすべてのテーブルインスタンスの設定を維持し、リレーションの構築と ORM の設定を簡単にしてくれます。詳細は TableRegistry の利用 をご覧ください。
テーブルクラスがプラグインの中にある場合、あなたのテーブルクラスのために正しい名前を 必ず使用してください。それに失敗すると、デフォルトのクラスが正しいクラスの代わりに使われてしまい、 バリデーションルールやコールバックが呼ばれないなどの結果を生じます。プラグインのテーブルクラスを 正しくロードするために、次のように使用してください。
// プラグインの Table
// Prior to 3.6 use TableRegistry::get('PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('PluginName.Articles');
// ベンダープレフィックス付きのプラグイン Table
// Prior to 3.6 use TableRegistry::get('VendorName/PluginName.Articles')
$articlesTable = TableRegistry::getTableLocator()->get('VendorName/PluginName.Articles');
上記で示した通り、テーブルオブジェクトは、いろいろなイベントを起こします。イベントは、 ORM 内でフックしたり、サブクラス化やメソッドをオーバーライドせずにロジックを加えたい時に便利です。 イベントリスナーはテーブルクラスやビヘイビアークラスで定義できます。 また、テーブルのイベントマネージャーをリスナーをバインドするために使えます。
コールバックメソッドを使うとき、 initialize()
メソッドで追加されたビヘイビアーは、
テーブルコールバックメソッドが開始する 前に 呼ばれるリスナーを持ちます。
これは、コントローラーやコンポーネントと同じ流れに従います。
イベントリスナーにテーブルクラスやビヘイビアーを追加するには、単純にメソッド名を以下の様に使います。 イベントサブシステムの使い方の詳細は イベントシステム をご覧ください。
Model.initialize
Model.beforeMarshal
Model.beforeFind
Model.buildValidator
Model.buildRules
Model.beforeRules
Model.afterRules
Model.beforeSave
Model.afterSave
Model.afterSaveCommit
Model.beforeDelete
Model.afterDelete
Model.afterDeleteCommit
Model.initialize
イベントは、コンストラクターと initialize メソッドが呼ばれた後に発行されます。
デフォルトでは、 Table
クラスは、このイベントを購読しません。そして、代わりに initialize
フックメソッドを使います。
Model.initialize
イベントに応答するために、 EventListenerInterface
を実装したリスナークラスを作成することができます。
use Cake\Event\EventListenerInterface;
class ModelInitializeListener implements EventListenerInterface
{
public function implementedEvents()
{
return array(
'Model.initialize' => 'initializeEvent',
);
}
public function initializeEvent($event)
{
$table = $event->getSubject();
// ここで何かする
}
}
そして、以下のように EventManager
にリスナーを追加します。
use Cake\Event\EventManager;
$listener = new ModelInitializeListener();
EventManager::instance()->attach($listener);
これは、任意の Table
クラスが構築されたとき、 initializeEvent
を呼びます。
Model.beforeMarshal
イベントは、リクエストデータがエンティティーに変換される前に発行されます。
詳細は エンティティー構築前のリクエストデータ変更 をご覧ください。
Model.beforeFind
イベントは各 find 操作の前に発行されます。イベントを止めて戻り値を返すことで
find を完全にバイパスできます。 $query インスタンスに対してなされた全ての変更は find 処理の間
維持されます。 $primary
パラメーターは、これがルートクエリーなのか、それともアソシエーションの
クエリーなのかを示します。クエリーに含まれる全てのアソシエーションで Model.beforeFind
イベントが呼ばれます。 JOIN を使うアソシエーションに対しては、ダミーのクエリーが渡されます。
イベントリスナーでは、追加のフィールド、検索条件、 JOIN や結果のフォーマッターを設定出来ます。
これらのオプションや機能はルートクエリーにコピーされます。
ユーザーの役職に基づいて find の操作を制限したり、現在の負荷状況に基づいてキャッシュの判断をしたり するために、このコールバックを利用できるかもしれません。
CakePHP の旧バージョンでは afterFind
コールバックがありましたが、 結果を Map/Reduce で変更する
機能とエンティティーのコンストラクターに置き換えられました。
Model.buildValidator
イベントは $name
バリデーターが作られた時に発行されます。
ビヘイビアーは、バリデーションメソッドに追加するために、このフックが使用できます。
Model.buildRules
イベントはルールインスタンスが作られた後と、
Table の beforeRules()
メソッドが呼ばれた後に発行されます。
Model.beforeRules
イベントはエンティティーにルールが適用される前に発行されます。
このイベントが止まると、チェックのためのルールを停止して、適用したルールの結果を
セットすることができます。
Model.afterRules
イベントはルールがエンティティーに適用された後に発行されます。
このイベントが止まると、操作をチェックするためのルールの結果の値を返すことができます。
Model.beforeSave
イベントはエンティティーが保存する前に発行されます。
このイベントを止めることによって、保存を停止できます。イベントが停止すると、
このイベントの結果が返されます。
イベントを停止する方法は、 こちら に記載されています。
Model.afterSave
イベントはエンティティーを保存した後に発行されます。
Model.afterSaveCommit
イベントは、保存処理がラップされたトランザクションが
コミットされた後に発行されます。データベース操作が暗黙的にコミットされる非アトミックな保存でも
引き起こされます。イベントは、 save()
が直接呼ばれた最初のテーブルだけに引き起こされます。
save が呼ばれる前にトランザクションが始まっている場合、イベントは起こりません。
Model.beforeDelete
イベントはエンティティーを削除する前に発行されます。
このイベントを停止することによって、削除を中止できます。イベントが停止すると、
このイベントの結果が返されます。
イベントを停止する方法は、 こちら に記載されています。
Model.afterDelete
イベントはエンティティーが削除された後に発行されます。
Model.afterDeleteCommit
イベントは、削除処理がラップされたトランザクションが
コミットされた後に発行されます。データベース操作が暗黙的にコミットされる非アトミックな保存でも
引き起こされます。イベントは、 delete()
が直接呼ばれた最初のテーブルだけに引き起こされます。
delete が呼ばれる前にトランザクションが始まっている場合、イベントは起こりません。
ビヘイビアーは、テーブルクラスにまたがって関連するロジックの再利用可能な部品を作成する 簡単な方法を提供します。なぜビヘイビアーが通常のクラスで、トレイトではないのか 不思議に思うかもしれません。第一の理由は、ビヘイビアーはイベントリスナーだからです。 トレイトは再利用可能なロジックの部品になりえますが、イベントをバインドするのは厄介です。
ビヘイビアーをテーブルに追加するために addBehavior()
メソッドが使えます。
一般的に、これを initialize()
でやるのがもっともよいです。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
}
}
アソシエーションには プラグイン記法 と追加の設定オプションが使えます。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp', [
'events' => [
'Model.beforeSave' => [
'created_at' => 'new',
'modified_at' => 'always'
]
]
]);
}
}
CakePHP によって提供されるビヘイビアーを含む、ビヘイビアーに関する詳細は ビヘイビアー の章をご覧ください。
デフォルトでは、全てのテーブルインスタンスは default
データベース接続を使用します。
もし、複数のデータベース接続を使用している場合、どのコネクションを使用してテーブルを
設定したくなるでしょう。これは、 defaultConnectionName()
メソッドで出来ます。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table
{
public static function defaultConnectionName() {
return 'slavedb';
}
}
注釈
defaultConnectionName()
メソッドはスタティックで なければなりません 。
これまで見てきたように、TableRegistry クラスは factory/registry を アプリケーションのテーブルインスタンスにアクセスするために使うことを簡単にします。 これには他にも便利な機能があります。
テーブルをレジストリーからロードする時に、依存関係をカスタマイズするか、
$options
配列が用意するモックオブジェクトを使います。
$articles = TableRegistry::getTableLocator()->get('Articles', [
'className' => 'App\Custom\ArticlesTable',
'table' => 'my_articles',
'connection' => $connectionObject,
'schema' => $schemaObject,
'entityClass' => 'Custom\EntityClass',
'eventManager' => $eventManager,
'behaviors' => $behaviorRegistry
]);
接続とスキーマ設定に注意して下さい。それらは文字列変数ではなくオブジェクトです。
この接続は Cake\Database\Connection
のオブジェクトと
スキーマの Cake\Database\Schema\Collection
を扱います。
注釈
テーブルは initialize()
メソッドで追加の設定を行う場合、それらの値は
レジストリーの設定を上書きします。
また、事前にレジストリーを config()
メソッドを使って設定できます。
設定データは エイリアスごと に保存され、オブジェクトの
initialize()
メソッドで上書きできます。
TableRegistry::config('Users', ['table' => 'my_users']);
注釈
そのエイリアスにアクセスする前か、最初 のアクセス時だけテーブルの設定が可能です。 レジストリーが投入された後に設定しても効果がありません。
注釈
CakeORMTableRegistry のスタティック API は 3.6.0 で非推奨になりました。 代わりにテーブルロケーターを直接使用してください。
テストケースで、レジストリーをフラッシュしたいこともあるでしょう。 モックオブジェクトを使う時やテーブルの依存関係を設定する時に便利です。
TableRegistry::clear();
もし、規約に従わない場合、おそらくテーブルやエンティティークラスは CakePHP によって検知されません。
これを修正するために、 Cake\Core\Configure::write
メソッドで名前空間をセットできます。
例えば、
/src
/App
/My
/Namespace
/Model
/Entity
/Table
は、次のように設定されます。
Cake\Core\Configure::write('App.namespace', 'App\My\Namespace');