CakePHP4 がリリース されてから、これまで作ってきたものを CakePHP4 でいちから書き直してみてます。
この記事は EventListener の使い方を簡単に説明します。 CakePHP3 でも同じなハズです。
便利な Behavior を使いたい
Cookbook では Behavior の例として TimestampBehavior
が記述されています。
https://book.cakephp.org/3/ja/orm/behaviors.html
TimestampBehavior
は汎用性が高く、すべてのテーブルで利用したくなります。
これまでは AppTable を作って継承してた
これまでは、 AppTable
を作成して initialize()
で TimestampBehavior
を追加して、
<?php declare(strict_types=1); namespace App\Model\Table; use Cake\ORM\Table; class AppTable extends Table { /** * @inheritDoc */ public function initialize(array $config): void { // 登録時 created と modified の自動設定 $this->addBehavior('Timestamp'); } }
各テーブルで AppTable
を継承するという手段をとっていました。
<?php declare(strict_types=1); namespace App\Model\Table; class ArticlesTable extends AppTable { }
AppTable
には、他にも beforeSave, beforeDelete でログを出す処理を入れていました。
このやり方だと、TimestampBehavior
を利用するためだけにわざわざ Table クラスを作る必要があります。
もっと楽にやれないかとググってたら issue がありました。
EventListener でイベントとして登録しておく
CakePHP にはイベントシステムがあります。
https://book.cakephp.org/3/ja/core-libraries/events.html
src/Event
にリスナークラスを置きます。
<?php declare(strict_types=1); namespace App\Event; use Cake\Datasource\EntityInterface; use Cake\Event\Event; use Cake\Event\EventListenerInterface; use Cake\Log\Log; class ModelListener implements EventListenerInterface { /** * イベントに処理を設定する。 * * @return array */ public function implementedEvents(): array { return [ 'Model.initialize' => 'initializeEvent', 'Model.beforeSave' => 'loggingEntity', 'Model.beforeDelete' => 'loggingEntity', ]; } /** * Model の initialize 時の処理。 * * @param \Cake\Event\Event $event イベント * @return void */ public function initializeEvent(Event $event) { $table = $event->getSubject(); // 登録時 created と modified の自動設定 $table->addBehavior('Timestamp'); } /** * エンティティをログに出力します。 * * @param \Cake\Event\Event $event イベント * @param \Cake\Datasource\EntityInterface $entity エンティティ * @param \ArrayObject $options オプション * @return void */ public function loggingEntity(Event $event, EntityInterface $entity, \ArrayObject $options) { // DebugKit は除く if (strpos(get_class($entity), 'DebugKit') === 0) { return; } if (env('ENVIRONMENT') !== 'production') { Log::debug($event->getName() . ': ' . json_encode($entity, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); } } }
後はリスナーを EventManager
に登録するだけです。
<?php declare(strict_types=1); namespace App; use App\Event\ModelListener; use Cake\Event\EventManager; class Application extends BaseApplication { public function bootstrap(): void { /* 中略 */ // モデルに関するイベントを設定 $listener = new ModelListener(); EventManager::instance()->on($listener); } }
僕は常に利用したいので src/Application.php
で設定してますが、各コントローラなんかでも設定できます。
最後に
AppTable
と各テーブルのクラスを作ってたけど、リスナークラス 1 つだけ作ればよくなった!