qiuxis 2016-12-08 14:53:20 4249次浏览 0条回复 10 8 0

新手对YII2事件的理解有点难呀,话说我也是新手! 网上和YIICHINA的教程倒是很多,可惜不全或者不详细,所以对此一知半解!

  • 一、概念

yii2的事件机制能让我们在做某件事的时候(之前或者之后)触发做另外一件事务。 说的正式点就是我们把自定义的代码注入到某个特定的执行点,(可通过代码的阅读理解此话含义) 一旦触发就会执行我们自定义好的这段代码。 例如:浏览谋篇文章的同时会触发增加点击量事件(点击量事件会执行相关的数据库处理的操作),发布微博的时候(发布完成之后),告知你的粉丝,等等。

  • 二、事件执行过程

总体来讲,事件的执行需要先绑定 —— on ,然后执行 —— trigger 简单的理解: on 其实就是装填子弹的过程 $handler 这个勉强可以称之为子弹吧 trigger 才是扣动扳机开枪射击的时刻 而off很明显就是拆卸子弹的过程了 所以即使你已经装填了子弹但是没有射击,还是无法达到效果的!(这在项目实行过程中可能会遇到) 注意先后关系: 绑定(on)->触发(trigger) 绑定(on)->解绑(off)->无法触发

另外off 在解绑的时候可以所有都解绑,也可以根据某个 $handler 进行解绑,好比如卸下弹夹和卸下某颗子弹。

  • 三、事件处理器

事件处理器 —— handler ,其实就是我们自定义的代码,用来处理事件的。

事件处理器的类型: 1-- 全局函数,字符串形式指定的PHP全局函数(入口脚本中加载的我们自定义的函数) 2-- 对象方法,对象名和方法名数组 指定的对象方法 3-- 静态方法,类名和方法名指数组 定的类静态方法 4-- 匿名函数,如function ($event){...} 注:事件处理器在三种事件级别里面都有。

代码示例(实例级别事件):

<?php 
$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) {
    //事件处理逻辑
});
?>
  • 四、事件的绑定

1、通过配置来附属事件处理器 原生的事件处理器出现在配置的时候,通过调用 base\Component::__set 完成事件的绑定,其实也是用了on

<?php 
return [
    // ...
    'components' => [
        'db' => [
            // ...
            'on afterOpen' => function ($event) {
                // do something right after connected to database
            }
        ],
    ],
];
?>

2、用on来绑定事件也就是附加事件处理器,说白了就是给 $this->_events 属性赋值

<?php 
...
// base\Component 
    public function on($name, $handler, $data = null, $append = true)
    {
        $this->ensureBehaviors();
        if ($append || empty($this->_events[$name])) {
            $this->_events[$name][] = [$handler, $data];
        } else {
            array_unshift($this->_events[$name], [$handler, $data]);
        }
    }
// 实例级别事件	
// $name 事件名称
// $handler 事件处理程序
// $data 当事件被触发时,将传递给事件处理程序的数据。在事件处理程序被调用时,
// 这个数据可以通过Yii \base\Event:$data 访问
// $append 是否将新的事件处理程序添加到现有处理程序列表的末尾。
// 如果 false,新的处理程序将在现有处理程序列表的开始处插入。
...	
?>	
  • 五、事件处理顺序

看实例

  • 六、事件的解绑
<?php
// 实例级别事件	
 
// 事件处理器是一个全局函数
$foo->off(Foo::EVENT_HELLO, 'function_name');
// 事件处理器是一个对象
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);
// 事件处理器是一个静态方法
$foo->off(Foo::EVENT_HELLO, ['appcomponentsBar', 'methodName']);
// 事件处理器是一个匿名函数
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);
?>

注意:一般情况不能解绑一个匿名函数,除非当他们被绑定时,你将它存放在某个变量。在上例中,假定将匿名函数存放在了$anonymousFunction变量里。

  • 七、事件的级别

1、实例级别事件

2、类级别事件

3、全局级别事件

1、实例级别事件

看实例

2、类级别的事件

为什么需要类级别的事件? 首先需要明白类和类的实例化 类实例化后和类的直接绑定有什么区别呢? 实例化是当前的作为,比如X类实例化为 $A,$B,$C 那我们是否要一个个绑定呢? 为了不这样做,YII实现了类级别的事件绑定,也就是说无论X类有多少个实例,都会绑定这个事件 类级别的事件绑定用 yii\base\Event::on() 来实现绑定

<?php
// base\Event 
// public static function on($class, $name, $handler, $data = null, $append = true)
// $class 事件处理程序需要附加的类名称 
// $name 事件名称
// $handler 事件处理器
// $data 附加事件处理器时传入的数据,默认空。
// $append 处理顺序,如果是false 优先处理
 
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);
    }
}
?>

在这个处理器中,可以通过 $event->sender 获取触发事件的对象。 当对象触发事件时,它首先调用实例级别的处理器,然后才会调用类级别处理器。 可调用静态方法[[yii\base\Event::trigger()]]来触发一个类级别事件。类级别事件不与特定对象相关联。因此,它只会引起类级别事件处理器的调用。如:

<?php 

Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
    echo $event->sender;  // displays 'app\components\Foo'
});
Event::trigger(Foo::className(), Foo::EVENT_HELLO);
//yii\base\Event::sender| 调用trigger() 方法的对象
?>

注意:这种情况, $event->sender 指的是触发事件的类而不是一个实例对象。 另外,因为类级处理器能对该类或子类的所有触发器做出回应,所以我们要谨慎使用,尤其是基层类中,例如yii\base\Object 解绑一个类级事件处理器,可以调用 yii\base\Event::off() 来实现 。

<?php 
Event::off(Foo::className(),Foo::EVENT_HELLO,$handler);//移除 $handler 
Event::off(Foo::className(),Foo::EVENT_HELLO); // 移除 Foo::EVENT_HELLO 事件的全部处理器 
?>

3、全局事件

所谓全局事件实际上是一个基于以上叙述的事件机制的戏法。 他只是利用了Application实例在整个应用的生命周期中全局可访问的特性,来实现这个全局事件的。 当然,你也可以将他绑定在任意全局可访问的的Component上。 它需要一个全局可访问的单例,如应用实例。 事件触发者不调用其自身的 trigger() 方法,而是调用单例的 trigger() 方法来触发全局事件。类似地,事件处理器被附加到单例的事件。如:

<?php
// base\Component
// public function on($name, $handler, $data = null, $append = true)
// $name 事件名称
// $handler 事件处理器
// $data 附加事件处理器时传入的数据,默认空。
// $append 处理顺序,如果是false 优先处理
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]));
?>

全局事件一个最大优势在于:在任意需要的时候,都可以触发全局事件,也可以在任意必要的时候绑定,或解除一个事件: 然而,因为全局事件的命名空间由各方共享,应合理命名全局事件,如引入一些命名空间(例:"frontend.mail.sent", "backend.mail.sent")。 上面的 Yii::$app->on() 可以在任何地方调用,就可以完成事件的绑定。而 Yii::$app->trigger() 只要在绑定之后的任何时候调用就OK了。

在项目实施过程中可能会碰到的问题 : 建了一个类,实例化了几个对象,当这几个对象要执行某个动作的时候,都要触发某个事件,该如何做? 比如,工头需要了解所有工人的下班时间, 那么,对于数百个工人,即数百个Worker实例,工头难道要一个一个去绑定自己的handler么? 触发的动作有时候可能要先执行有时候可能要后执行,这个逻辑要如何实现? 比如要网站要发布一个通知,要让所有人都知道,这个事件如何去实行?(布局一个全局事件) 实例说明:

觉得很赞
    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册