0 フォロワー

イベント

イベントを使用すると、特定の実行ポイントで既存のコードにカスタムコードを挿入できます。イベントにカスタムコードをアタッチすると、イベントがトリガーされたときにコードが自動的に実行されます。たとえば、メーラーオブジェクトは、メッセージの送信に成功すると `messageSent` イベントをトリガーする可能性があります。送信に成功したメッセージを追跡する場合は、`messageSent` イベントに追跡コードをアタッチするだけで済みます。

Yii は、イベントをサポートするために yii\base\Component という基本クラスを導入しています。クラスでイベントをトリガーする必要がある場合は、yii\base\Component またはその子クラスを継承する必要があります。

イベントハンドラ

イベントハンドラは、アタッチされているイベントがトリガーされたときに実行される PHP コールバック です。次のコールバックを使用できます。

  • 文字列で指定されたグローバルPHP関数(括弧なし)、例:`'trim'`。
  • オブジェクトとメソッド名の配列として指定されたオブジェクトメソッド(括弧なし)、例:`[$object, 'methodName']`。
  • クラス名とメソッド名の配列として指定された静的クラスメソッド(括弧なし)、例:`['ClassName', 'methodName']`。
  • 無名関数、例:`function ($event) { ... }`。

イベントハンドラのシグネチャは次のとおりです。

function ($event) {
    // $event is an object of yii\base\Event or a child class
}

`$event` パラメータを通じて、イベントハンドラは発生したイベントに関する次の情報を入手できます。

イベントハンドラの追加

yii\base\Component::on() メソッドを呼び出すことで、イベントにハンドラを追加できます。例:

$foo = new Foo();

// this handler is a global function
$foo->on(Foo::EVENT_HELLO, 'function_name');

// this handler is an object method
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);

// this handler is a static class method
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// this handler is an anonymous function
$foo->on(Foo::EVENT_HELLO, function ($event) {
    // event handling logic
});

イベントハンドラは、設定 を通じて追加することもできます。詳細については、設定 セクションを参照してください。

イベントハンドラを追加する際、yii\base\Component::on() に第三引数として追加のデータを提供できます。このデータは、イベントがトリガーされ、ハンドラが呼び出されたときにハンドラで使用できます。例:

// The following code will display "abc" when the event is triggered
// because $event->data contains the data passed as the 3rd argument to "on"
$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');

function function_name($event) {
    echo $event->data;
}

イベントハンドラの順序

1つのイベントに1つ以上のハンドラを追加できます。イベントがトリガーされると、追加されたハンドラはイベントに追加された順序で呼び出されます。ハンドラが後続のハンドラの呼び出しを停止する必要がある場合は、`$event` パラメータの yii\base\Event::$handled プロパティを `true` に設定できます。

$foo->on(Foo::EVENT_HELLO, function ($event) {
    $event->handled = true;
});

デフォルトでは、新しく追加されたハンドラはイベントの既存のハンドラキューに追加されます。その結果、イベントがトリガーされたときに、ハンドラは最後に呼び出されます。ハンドラをハンドラキューの先頭に挿入して、ハンドラを最初に呼び出すには、yii\base\Component::on()を呼び出し、4番目のパラメータ$appendfalseを渡します。

$foo->on(Foo::EVENT_HELLO, function ($event) {
    // ...
}, $data, false);

イベントのトリガー

イベントは、yii\base\Component::trigger()メソッドを呼び出すことによってトリガーされます。このメソッドには、イベント名と、イベントハンドラに渡されるパラメータを記述するイベントオブジェクト(オプション)が必要です。例:

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class Foo extends Component
{
    const EVENT_HELLO = 'hello';

    public function bar()
    {
        $this->trigger(self::EVENT_HELLO);
    }
}

上記のコードでは、bar()へのすべての呼び出しによって、helloという名前のイベントがトリガーされます。

ヒント: イベント名を表すには、クラス定数を使用することをお勧めします。上記の例では、定数EVENT_HELLOhelloイベントを表しています。このアプローチには3つの利点があります。まず、タイプミスを防ぎます。第二に、IDEのオートコンプリートサポートでイベントを認識しやすくします。第三に、クラスの定数宣言を確認するだけで、そのクラスでサポートされているイベントを把握できます。

イベントをトリガーする際に、イベントハンドラに追加情報を渡したい場合があります。たとえば、メーラーはmessageSentイベントのハンドラにメッセージ情報を渡して、ハンドラが送信されたメッセージの詳細を知ることができるようにしたい場合があります。yii\base\Component::trigger()メソッドの2番目のパラメータとしてイベントオブジェクトを提供することで、これを行うことができます。イベントオブジェクトは、yii\base\Eventクラスまたはその子クラスのインスタンスである必要があります。例:

namespace app\components;

use yii\base\Component;
use yii\base\Event;

class MessageEvent extends Event
{
    public $message;
}

class Mailer extends Component
{
    const EVENT_MESSAGE_SENT = 'messageSent';

    public function send($message)
    {
        // ...sending $message...

        $event = new MessageEvent;
        $event->message = $message;
        $this->trigger(self::EVENT_MESSAGE_SENT, $event);
    }
}

yii\base\Component::trigger()メソッドが呼び出されると、指定されたイベントにアタッチされているすべてのハンドラが呼び出されます。

イベントハンドラのデタッチ

イベントからハンドラをデタッチするには、yii\base\Component::off()メソッドを呼び出します。例:

// the handler is a global function
$foo->off(Foo::EVENT_HELLO, 'function_name');

// the handler is an object method
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);

// the handler is a static class method
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// the handler is an anonymous function
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);

一般的に、匿名関数をイベントにアタッチしたときにどこかで保存しない限り、匿名関数をデタッチしようとしてはならないことに注意してください。上記の例では、匿名関数が$anonymousFunctionという変数として保存されていると仮定しています。

イベントからすべてのハンドラをデタッチするには、2番目のパラメータなしでyii\base\Component::off()を呼び出すだけです。

$foo->off(Foo::EVENT_HELLO);

クラスレベルのイベントハンドラ

上記のセクションでは、インスタンスレベルでイベントにハンドラをアタッチする方法について説明しました。特定のインスタンスだけでなく、クラスのすべてのインスタンスによってトリガーされたイベントに応答したい場合があります。すべてのインスタンスにイベントハンドラをアタッチする代わりに、静的メソッドyii\base\Event::on()を呼び出すことで、クラスレベルでハンドラをアタッチできます。

たとえば、アクティブレコードオブジェクトは、データベースに新しいレコードを挿入するたびにEVENT_AFTER_INSERTイベントをトリガーします。すべてアクティブレコードオブジェクトによって行われた挿入を追跡するには、次のコードを使用します。

use Yii;
use yii\base\Event;
use yii\db\ActiveRecord;

Event::on(ActiveRecord::class, ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
    Yii::debug(get_class($event->sender) . ' is inserted');
});

このイベントハンドラは、ActiveRecordまたはその子クラスのインスタンスがEVENT_AFTER_INSERTイベントをトリガーするたびに呼び出されます。ハンドラでは、$event->senderを通じてイベントをトリガーしたオブジェクトを取得できます。

オブジェクトがイベントをトリガーすると、最初にインスタンスレベルのハンドラが呼び出され、次にクラスレベルのハンドラが呼び出されます。

静的メソッドyii\base\Event::trigger()を呼び出すことで、クラスレベルのイベントをトリガーできます。クラスレベルのイベントは特定のオブジェクトに関連付けられていません。そのため、クラスレベルのイベントハンドラのみが呼び出されます。例:

use yii\base\Event;

Event::on(Foo::class, Foo::EVENT_HELLO, function ($event) {
    var_dump($event->sender);  // displays "null"
});

Event::trigger(Foo::class, Foo::EVENT_HELLO);

この場合、$event->senderはオブジェクトインスタンスではなくnullであることに注意してください。

注意: クラスレベルのハンドラは、そのクラスまたはその子クラスの任意のインスタンスによってトリガーされたイベントに応答するため、特にyii\base\BaseObjectなどの低レベルのベースクラスである場合は、慎重に使用してください。

クラスレベルのイベントハンドラをデタッチするには、yii\base\Event::off()を呼び出します。例:

// detach $handler
Event::off(Foo::class, Foo::EVENT_HELLO, $handler);

// detach all handlers of Foo::EVENT_HELLO
Event::off(Foo::class, Foo::EVENT_HELLO);

インターフェースを使用したイベント

イベントを処理するさらに抽象的な方法があります。特別なイベント用のインターフェースを作成し、必要なクラスに実装することができます。

たとえば、次のインターフェースを作成できます。

namespace app\interfaces;

interface DanceEventInterface
{
    const EVENT_DANCE = 'dance';
}

そして、それを実装する2つのクラス。

class Dog extends Component implements DanceEventInterface
{
    public function meetBuddy()
    {
        echo "Woof!";
        $this->trigger(DanceEventInterface::EVENT_DANCE);
    }
}

class Developer extends Component implements DanceEventInterface
{
    public function testsPassed()
    {
        echo "Yay!";
        $this->trigger(DanceEventInterface::EVENT_DANCE);
    }
}

これらのクラスのいずれかによってトリガーされるEVENT_DANCEを処理するには、Event::on()を呼び出し、インターフェースのクラス名を最初の引数として渡します。

Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
    Yii::debug(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
});

これらのクラスのイベントをトリガーできます。

// trigger event for Dog class
Event::trigger(Dog::class, DanceEventInterface::EVENT_DANCE);

// trigger event for Developer class
Event::trigger(Developer::class, DanceEventInterface::EVENT_DANCE);

ただし、インターフェースを実装するすべてのクラスをトリガーすることはできないことに注意してください。

// DOES NOT WORK. Classes that implement this interface will NOT be triggered.
Event::trigger('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE);

イベントハンドラをデタッチするには、Event::off()を呼び出します。例:

// detaches $handler
Event::off('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);

// detaches all handlers of DanceEventInterface::EVENT_DANCE
Event::off('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE);

グローバルイベント

Yiiは、いわゆるグローバルイベントをサポートしています。これは実際には、上記で説明したイベントメカニズムに基づいたトリックです。グローバルイベントには、アプリケーションインスタンス自体など、グローバルにアクセス可能なシングルトンが必要です。

グローバルイベントを作成するには、イベント送信元が、送信元自身のtrigger()メソッドを呼び出す代わりに、シングルトンのtrigger()メソッドを呼び出してイベントをトリガーします。同様に、イベントハンドラはシングルトンにイベントをアタッチします。例:

use Yii;
use yii\base\Event;
use app\components\Foo;

Yii::$app->on('bar', function ($event) {
    echo get_class($event->sender);  // displays "app\components\Foo"
});

Yii::$app->trigger('bar', new Event(['sender' => new Foo]));

グローバルイベントを使用する利点は、オブジェクトによってトリガーされるイベントにハンドラをアタッチする際にオブジェクトを必要としないことです。代わりに、ハンドラの添付とイベントのトリガーの両方がシングルトン(たとえば、アプリケーションインスタンス)を介して行われます。

ただし、グローバルイベントの名前空間はすべての関係者で共有されるため、名前空間のある程度(例:「frontend.mail.sent」、「backend.mail.sent」)を導入するなど、グローバルイベントには適切な名前を付ける必要があります。

ワイルドカードイベント

2.0.14以降、ワイルドカードパターンに一致する複数のイベントのイベントハンドラを設定できます。例:

use Yii;

$foo = new Foo();

$foo->on('foo.event.*', function ($event) {
    // triggered for any event, which name starts on 'foo.event.'
    Yii::debug('trigger event: ' . $event->name);
});

ワイルドカードパターンは、クラスレベルのイベントにも使用できます。例:

use yii\base\Event;
use Yii;

Event::on('app\models\*', 'before*', function ($event) {
    // triggered for any class in namespace 'app\models' for any event, which name starts on 'before'
    Yii::debug('trigger event: ' . $event->name . ' for class: ' . get_class($event->sender));
});

これにより、次のコードを使用して単一のハンドラですべてのアプリケーションイベントをキャッチできます。

use yii\base\Event;
use Yii;

Event::on('*', '*', function ($event) {
    // triggered for any event at any class
    Yii::debug('trigger event: ' . $event->name);
});

注意: イベントハンドラの設定にワイルドカードを使用すると、アプリケーションのパフォーマンスが低下する可能性があります。可能な限り避けるのが最善です。

ワイルドカードパターンで指定されたイベントハンドラをデタッチするには、yii\base\Component::off()またはyii\base\Event::off()の呼び出しで同じパターンを繰り返す必要があります。イベントハンドラのデタッチ中にワイルドカードを渡すと、そのワイルドカードに対して指定されたハンドラのみがデタッチされ、通常のイベント名に対してアタッチされたハンドラは、パターンに一致する場合でも残ることに注意してください。例:

use Yii;

$foo = new Foo();

// attach regular handler
$foo->on('event.hello', function ($event) {
    echo 'direct-handler'
});

// attach wildcard handler
$foo->on('*', function ($event) {
    echo 'wildcard-handler'
});

// detach wildcard handler only!
$foo->off('*');

$foo->trigger('event.hello'); // outputs: 'direct-handler'

タイプミスを見つけた場合、またはこのページの改善が必要な場合は、
githubで編集してください。 !