質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.13%

cakephp3のeventについて

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 3,287

score 1086

cakephp3のeventについて質問があります。tableクラスからdispatch()を使って、eventをイベントリスナーに通知するところまでは理解できたのですが、eventを通知されたイベントリスナーがどのような動きをしているのか分かりません。

class ArticlesTable extends Table {

    public function initialize(array $config)
        {
        $this->belongsTo('Posts', [
                    'foreignKey' => 'posts_id'
            ]);

            $listener = new ContactsListener();
            $this->eventManager()->on($listener);
        }

public function checkSomething()
    {
        $arr = ['a'=> 1, 'b'=> 2];
        //... some code here
        $event = new Event('Model.Articles.SomethingCheck', $this, [
            'check_arr' => $arr
        ]);
        $this->eventManager()->dispatch($event);

    }

↓イベントリスナー

class ContactsListener implements EventListenerInterface
{

    public function implementedEvents()
    {
        return [
            'Model.Articles.SomethingCheck' => 'createNotificationAfterCheck',
        ];
    }

    public function createNotificationAfterCheck(Event $event, array $arr)
    {
        dump($event);

    }
}


eventの追加はbeforeSaveのような概念を新しく追加しているだけなのでしょうか。上のcheckDuplicates()で仕込んだcheck_arrの連想配列はどうゆう場合に有効利用できるのでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

+1

eventの追加はbeforeSaveのような概念を新しく追加しているだけなのでしょうか。

beforeSaveなどもイベントの1つなので、おっしゃる通りです。
ただ、beforeSaveなどはCakePHPのライフサイクルに組み込まれているため、自分でイベントを新たに設定するよりも使う機会は多いと思います。
どちらにせよ、指定するイベントが起こったら、特定の処理を実行したいというときに使います。

上のcheckDuplicates()で仕込んだcheck_arrの連想配列はどうゆう場合に有効利用できるのでしょうか。

分かりにくくなっている原因は、以下のようにcreateNotificationAfterCheck(Event $event, array $arr)と書いているためかもしれません。
Eventクラスのコンストラクタに設定する第3引数の配列を$aとし、イベントを$eventとすると、createNotificationAfterCheck($event, $a[0], $a[1], $a[2], ……)と表せます。$aの値は$eventにも含まれているので(Event $event)だけでも十分です。

public function createNotificationAfterCheck(Event $event, array $arr)
{
}
// この方が分かりやすいです
public function createNotificationAfterCheck(Event $event)
{
}

次に'check_arr'ですが、'check_arr'は何かの判断材料に使ったり、save()する前にデータを一定の基準で変更したりするときなどに使います。
何かデータを渡さないと、できることの幅が狭くなります。

// この方が分かりやすいです
public function createNotificationAfterCheck(Event $event)
{
    // 処理の方法はCakePHPのバージョンによって違うので、適当に列挙しています

    // $event->getData('check_arr')
    // $event->setResult(['check_arr' => データ]);
    // $event->data['check_arr']
}

利用法を適当に列挙してみます。

  • save()する前に、渡したデータを一定の条件で修正する
  • 渡す値によって異なるログファイルにログを書き込む
  • 渡す値によって異なる相手にメールを送る
  • 渡す値によって異なるキャッシュファイルに書き込む
  • 渡す値によってイベントを中止する

コメントに対するコメント

$this->eventManager()->dispatch($event);の部分が気になるのですが、これはEventManager.phpに$eventを通知して、atachされているeventに対して、適用するという動きでいいんでしょうか。

そうですね。新しいイベントを事前に紐づけていたリスナーに伝え、そのリスナーのメソッドを実行させるということですね。
具体的には、EventManager.phpのdispatchメソッドに$eventを渡し、そこでリスナーのメソッドに$eventを渡してそのメソッドを実行するという流れです。

createNotificationAfterCheckはpublic function createNotificationAfterCheck(Event $event , $entity , $options) {}を書かないと呼ばれないと思うのですが、beforeSaveだと「呼ばれるのはsave前だな」とフックされるタイミングが直感的にわかるのですが、独自イベントだとそのタイミングってどこで設定するんでしょうか。結局、コントローラからTableを呼ぶだけみたいになってしまうんでしょうか。

やはりイベントが発生したことをどこかで伝える必要があるので、$this->eventManager()->dispatch($event);は必要になるのではないでしょうか。ウェブサイト表示のリクエストを受けてからデータを返すまでの流れ以外については、CakePHPではタイミングを判断しようがありません。

しかし、独自にイベントを作成した場合、そのイベントの中で処理する内容に対して独自に優先順位をつけることはできます。具体的には、$this->eventManager()->on()['priority' => 数値](デフォルトは10)を設定する方法です(バージョンによってはattach())。$this->eventManager()->on()で同じリスナーを複数加えられるので、それらの優先順位を設定すれば、タイミングを好きに設定できます。例えば、①save()の前に行う処理、②save()、③save()の後に行う処理、のように分ければbeforeSaveやafterSaveができます。

あと設計スタイルの話になりますが、呼び出すには当然、public function beforeSave(Event $event , $entity , $options) {}の記述が必要かと思いますが、それってsaveの処理を対応するコントローラに書かないといけないので、コントローラから新たにサブコントローラを呼んでみたいな細分化された設計ってできないのかなぁとちょっと思いました。

すみません、なんとなくしかやりたいことが分からないのですが、管理が面倒になるだけでサブコントローラー(サブクラス)を作ってもかまわないと思います。

それと、save()はコントローラーだけではなく、config/bootstrap.phpでもできます。どこかで$this->eventManager()->dispatch($event);を実行した段階で、bootstrap.phpに書いたコードがイベントのdispatchを検知して、指定したリスナー名に該当した場合に処理を行うことができます。そこではsave()もできます。

例えば以下のように設定すると、Model.Hoge.exampleにイベントが伝わったことを検知して、function()内の処理を行います。
なお、Model.Hoge.exampleをModel.beforeSaveにすると、コントローラーにpublic function beforeSave(Event $event , $entity , $options) {}を記述しなくてもbeforeSaveを使うことができます。

// bootstrap.php
use Cake\ORM\TableRegistry;

$hoge = TableRegistry::get('Hoges');
$hoge
    ->eventManager()
    ->on('Model.Hoge.example', ['priority' => 15], function($event, $entity) use ($hoge)
    {
        // バージョンによって$event->data['hoge']の取得方法が異なる
        if($hoge->save($event->data['hoge'])) {
            debug('save()できた!');
        }
    });

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/17 06:51 編集

    利用法についても言及していただき、ありがとうございます。

    $this->eventManager()->dispatch($event);の部分が気になるのですが、これはEventManager.phpに$eventを通知して、atachされているeventに対して、適用するという動きでいいんでしょうか。

    createNotificationAfterCheckはpublic function createNotificationAfterCheck(Event $event , $entity , $options) {}を書かないと呼ばれないと思うのですが、beforeSaveだと「呼ばれるのはsave前だな」とフックされるタイミングが直感的にわかるのですが、独自イベントだとそのタイミングってどこで設定するんでしょうか。結局、コントローラからTableを呼ぶだけみたいになってしまうんでしょうか。


    あと設計スタイルの話になりますが、呼び出すには当然、public function beforeSave(Event $event , $entity , $options) {}の記述が必要かと思いますが、それってsaveの処理を対応するコントローラに書かないといけないので、コントローラから新たにサブコントローラを呼んでみたいな細分化された設計ってできないのかなぁとちょっと思いました。

    キャンセル

  • 2017/05/17 11:51

    長くなってしまったので、ここにコメントせず、上に「コメントに対するコメント」を追記しました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る