イベントシステム
これは
イベントシステム - 3.x
の勉強メモである。
オブザーバーパターンの利用
イベントオブジェクトはすべてのリスナーに行き渡る。
そしてすべてのビュー、コントローラ、テーブルで
$events = $this->eventManager();
がある。。各モデルは独立したイベントマネージャーを持つ。
イベントの通知(dispatch)
// Cart/Model/Table/HogesTable.php namespace Cart\Model\Table; use Cake\Event\Event; use Cake\ORM\Table; class HogesTable extends Table { public function place($order) { if ($this->save($order)) { $this->Cart->remove($order); // イベント作成 $event = new Event('Model.Order.afterPlace', $this, [ 'order' => $order ]); // 通知 $this->eventManager()->dispatch($event); return true; } return false; } }
グローバルイベントマネージャーはアプリ内で起こる任意のイベントを受け取れる。
Cake\Event\EventManagerのシングルトン
// イベントの前に実行される任意の設定ファイルやコードの一部の中で
use Cake\Event\EventManager;
EventManager::instance()->on(
'Model.Order.afterPlace',
$aCallback
);
イベントの追跡
EventManager::instance()->setEventList(new EventList());
これで何を通知していったかわかる。
$eventsFired = EventManager::instance()->getEventList();
$firstEvent = $eventsFired[0];
コアイベント
ORM/Model イベント
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
コントローラーイベント
Controller.initialize
Controller.startup
Controller.beforeRedirect
Controller.beforeRender
Controller.shutdown
ビューイベント
View.beforeRender
View.beforeRenderFile
View.afterRenderFile
View.afterRender
View.beforeLayout
View.afterLayout
リスナーの追加方法
Cake\Event\EventListenerInterfaceを実装するすなわち
implementedEvents()を実装する。
use Cake\Event\EventListenerInterface; class Hoge implements EventListenerInterface { // イベントごとにどのメソッドを呼ぶか分ける public function implementedEvents() { return [ 'Model.Order.afterPlace' => 'updateBuyStatistic', ]; } // 各イベントの対処 public function updateBuyStatistic($event, $order) { // 統計値を更新するコード } } // 登録 // Hoge オブジェクトを Order のイベントマネージャーに追加 $hoge = new Hoge(); $this->Orders->eventManager()->on($hoge);
無名リスナーの登録
ブロックで直接登録してしまう。
use Cake\Log\Log; $this->Orders->eventManager()->on('Model.Order.afterPlace', function ($event) { Log::write( 'info', 'A new order was placed with id: ' . $event->getSubject()->id ); });
リスナーがいるかのチェック
リスナー登録が
// EventManager にリスナーを追加 $this->eventManager()->on('Hoge.Registration', [$this, 'hogeRegistration']); $this->eventManager()->on('Hoge.Verification', [$this, 'hogeVerification']); $this->eventManager()->on('Hoge.Authorization', [$this, 'hogeAuthorization']);
として
// アプリケーションのどこか別の場所で $events = $this->eventManager()->matchingListeners('Verification'); if (!empty($events)) { $this->eventManager()->off('Hoge.Verification'); } else { // 'Verification' イベントリスナーが存在しない場合のロジックを実行。 }
リスナーには呼び出し順にpriorityをつけることもできる。勉強中
イベントデータ
$this->eventManager() ->dispatch(new Event('View.afterRender', $this, ['view' => $viewFileName]));
の場合、afterRenderのコールバックのリスナー(実装メソッドは第2引数で$viewFileNameを受け取る。
function (Event $event, $viewFileName)
イベントの送信
// 第1引数はイベント名 // 第2引数はsubject(イベントに関連づけるオブジェクト(通常自分) // 第3引数はイベントパラメータで辞書がおすすめ $event = new Event('Model.Order.afterPlace', $this, [ 'order' => $order ]); $this->eventManager()->dispatch($event);
イベントの中止(後続のイベントコールバックを防ぐ)
通知を受けたメソッドがfalseをreturnするか
$event->stopPropagation();
を呼べば良い。
ストップすると$event->isStopped()がtrueになる。
イベント結果を返す。
// リスナーコールバック public function doSomething($event) { // .結果としてalteredDataを作って結果を返す $alteredData = $event->getData('order') + $moreData; return $alteredData; } // 別のリスナーコールバック public function doSomethingElse($event) { // returnで返す以外にsetResultというのでも返せる。 $event->setResult(['order' => $alteredData] + $this->result()); } // イベントを通知してその結果を得る public function place($order) { $event = new Event('Model.Order.beforePlace', $this, ['order' => $order]); // 配信 $this->eventManager()->dispatch($event); // 配信結果をうけとる if (!empty($event->getResult()['order'])) { $order = $event->getResult()['order']; } if ($this->Orders->save($order)) { // ... } // ... }
*** リスナーの削除
Cake\Event\EventManager::off()
イベントの注意
イベントは便利だが無思慮に使うとデバッグが困難になるので注意。