Yii2的递进学习–事件伊芙nt

我们先来看下事件在 Yii2中的使用,如下内容摘自 Yii2汉语文书档案

事件能够将自定义代码“注入”到现有代码中的特定执行点。附加自定义代码到有个别事件,当那个事件被触发时,这个代码就会活动执行。例如,邮件程序对象成功发生消息时可触发 messageSent 事件。如想追踪成功发送的音信,能够叠加相应追踪代码到 messageSent 事件。

Yii 引入了名为 [[yii\base\Component]]
的基类以帮忙事件。假设三个类要求接触事件就应当继承
[[yii\base\Component]] 或其子类。

事件处理器(伊夫nt Handlers)

事件处理器是二个PHP
回调函数
,当它所附加到的风云被触发时它就会执行。能够利用以下回调函数之一:

  • 字符串格局钦点的 PHP 全局函数,如 'trim' ;
  • 对象名和章程名数组情势钦定的对象方法,如 [$object, $method] ;
  • 类名和格局名数组方式钦赐的静态类方法,如 [$class, $method] ;
  • 匿名函数,如 function ($event) { ... } 。

事件处理器的格式是:

function ($event) {
    // $event 是 yii\base\Event 或其子类的对象
}

通过 $event 参数,事件处理器就赢得了以下关于事件的消息:

  • [[yii\base\Event::name|event name]]:事件名
  • [[yii\base\Event::sender|event
    sender]]:调用 trigger() 方法的对象
  • [[yii\base\Event::data|custom
    data]]:附加事件处理器时传入的数码,默许为空,后文详述

外加事件处理器

调用 [[yii\base\Component::on()]] 方法来附加处理器到事件上。如:

$foo = new Foo;

// 处理器是全局函数
$foo->on(Foo::EVENT_HELLO, 'function_name');

// 处理器是对象方法
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);

// 处理器是静态类方法
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// 处理器是匿名函数
$foo->on(Foo::EVENT_HELLO, function ($event) {
    //事件处理逻辑
});

外加事件处理器时能够提供额外数据作为
[[yii\base\Component::on()]]
方法的第多个参数。数据在事变被触发和处理器被调用时能被电脑使用。如:

// 当事件被触发时以下代码显示 "abc"
// 因为 $event->data 包括被传递到 "on" 方法的数据
$foo->on(Foo::EVENT_HELLO, function ($event) {
    echo $event->data;
}, 'abc');

事件处理器顺序

能够增大学一年级个或多少个计算机到三个风浪。当事件被触发,已附加的微机将按附加次序依次调用。如若有些处理器必要结束现在的处理器调用,可以安装 $event 参数的
[yii\base\Event::handled]] 属性为真,如下:

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

暗中同意新附加的风云处理器排在已存在处理器队列的最后。因而,那一个处理器将在事件被触发时最终一个调用。在处理器队列最前方插入新电脑将使该处理器初始调用,能够传递第多少个参数 $append 为假并调用 [[yii\base\Component::on()]]
方法实现:

$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 的事件。

Tip:
推荐使用类常量来表示事件名。上例中,常量 EVENT_HELLO 用来代表 hello 。那有七个好处。第壹,它能够防范拼写错误并协助IDE 的机动达成。第叁,只要不难检查常量证明就能领会三个类帮助什么事件。

奇迹想要在触及事件时同时传递一些外加音信到事件处理器。例如,邮件程序要传递信息消息到 messageSent 事件的处理器以便处理器精通哪些消息被发送了。为此,能够提供叁个事件目的作为
[[yii\base\Component::trigger()]]
方法的第二个参数。那些事件指标必须是 [[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)
    {
        // ...发送 $message 的逻辑...

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


[[yii\base\Component::trigger()]]
方法被调用时,它将调用全体附加到命名事件(trigger
方法第二个参数)的事件处理器。

移除事件处理器

从事件移除处理器,调用 [[yii\base\Component::off()]] 方法。如:

// 处理器是全局函数
$foo->off(Foo::EVENT_HELLO, 'function_name');

// 处理器是对象方法
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);

// 处理器是静态类方法
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// 处理器是匿名函数
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);

小心当匿名函数附加到事件后一般不要品味移除匿名函数,除非您在某处存款和储蓄了它。以上示例中,假若匿名函数存款和储蓄为变量$anonymousFunction 。

移除事件的一切处理器,不难调用 [[yii\base\Component::off()]]
即可,不须要第三个参数:

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

类级其他轩然大波处理器

如上部分,我们描述了在实例级别怎么样附加处理器到事件。有时想要一个类的富有实例而不是二个点名的实例都响应二个被触发的风云,并不是一个个增大事件处理器到种种实例,而是通过调用静态方法
[[yii\base\Event::on()]] 在类级别叠加处理器。

例如,运动记录指标要在历次往数据库新增一条新记录时接触一个
[[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]
事件。要追踪每一种活动记录对象的新增记录完结景况,应如下写代码:

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

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

每当
[[yii\db\BaseActiveRecord|ActiveRecord]] 或其子类的实例触发
[[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]
事件时,这么些事件处理器都会举办。在那些处理器中,能够经过 $event->sender 获取触发事件的靶子。

当指标触发事件时,它首先调用实例级别的微处理器,然后才会调用类级别处理器。

可调用静态方法[[yii\base\Event::trigger()]]来触发叁个类级别事件。类级别事件不与特定对象相关联。由此,它只会唤起类级别事件处理器的调用。如:

use yii\base\Event;

Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
    echo $event->sender;  // 显示 "app\models\Foo"
});

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

留神那种情景下 $event->sender 指向触发事件的类名而不是目的实例。

Note:
因为类级其他拍卖器响应类和其子类的持有实例触发的风浪,必须小心翼翼运用,越发是底层的基类,如
[[yii\base\Object]]。

移除类级其余事件处理器只需调用[[yii\base\Event::off()]],如:

// 移除 $handler
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler);

// 移除 Foo::EVENT_HELLO 事件的全部处理器
Event::off(Foo::className(), Foo::EVENT_HELLO);

全局事件

所谓大局事件骨子里是3个依据以上叙述的风浪机制的魔术。它要求一个大局可访问的单例,如应用实例。

事件触发者不调用其自笔者的 trigger() 方法,而是调用单例的 trigger() 方法来触发全局事件。类似地,事件处理器被增大到单例的轩然大波。如:

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

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

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

大局事件的三个功利是当附加处理器到1个对象要接触的轩然大波时,不要求发出该对象。相反,处理器附加和事件触发都经过单例(如应用实例)达成。

可是,因为全局事件的命名空间由各方共享,应客观命名全局事件,如引入一些命名空间(例:”frontend.mail.sent”,
“backend.mail.sent”)。

 

相关文章