Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

イベントシステム

これは
イベントシステム - 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 イベント

テーブルオブジェクト - 3.x

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

コントローラーイベント

コントローラー - 3.x

Controller.initialize
Controller.startup
Controller.beforeRedirect
Controller.beforeRender
Controller.shutdown

ビューイベント

ビュー - 3.x

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()

イベントの注意

イベントは便利だが無思慮に使うとデバッグが困難になるので注意。