说明:TP6的官方文档很啰嗦,而且给出了若干并不良好的实践方式。
先上答案:
1. 假设有自定义的事件如aaa,在event.php文件中listen数组里,添加一项:键名为'aaa',键值为一个一维数组,里面是若干类名称如BB::class,CC:;class,DD::class。
2. BB,CC,DD 都有一个handle方法,在需要执行BB,CC,DD中handle方法的地方,写上Event::trigger('aaa')
----------------------------------------------------------------------------------------------------
再说原因:
事件的概念,可以理解为数组的一个key,该key对应多个函数。类似于['eventName' => ['fun1','fun2']]
监听就是一直“监视”某个事件是否完成(程序是否执行到标记位置),完成则执行若干函数,如上面的fun1和fun2。
触发,就是使事件变为完成,执行事件上所绑定的函数(监听器)。
即一个完整的事件监听流程分为: 定义事件,绑定监听器,触发事件三个部分。
一个典型的事件,就是浏览器的文档加载完毕。当文档加载完毕后,弹出广告,欢迎语之类的。
这里的事件是 document.ready,监听器是弹广告欢迎,触发动作是浏览器完成的,加载完文档就触发。
举一个常见的例子:填写表单,提交表单,注册完毕都是标记(事件,钩子), 注册完毕后,给新帐号增加金币,积分,登录次数。
这里的事件,就是注册完毕
这里的监听器,就是+金币,+积分,+次数三个功能。
事件触发就是,【注册完毕】这个事件发生了,执行上述三个功能。
上述场景在TP6中的实现方式是:
1. 定义一个事件register_ok
2. 将指定的类如AfterRegister绑定到register_ok事件,
绑定方法为:在app\event.php中的listen数组中添加:
return [
'bind' => [
],
'listen' => [
// 事件名称为 register_ok,对应的监听类为AfterRegister
'register_ok' => ['app\listener\AfterRegister::class'],
],
];
3. 当register_ok触发时,执行AfterRegister中的方法(默认执行handle方法,在handle中去依次执行三个动作)
触发方式为:Event::trigger('register_ok',$user); 触发函数是可以传参的,如这里的$user,对应的AfterRegister中的handle方法,也具有对应的形参
class AfterRegister
{
function handle($user){... ...}
}
那么事件监听的实现方式,跟传统的函数调用有何优势?
事件使用场景一:
A,B,C三个人分别负责会员登录后增加金币,增加积分,增加登录次数记录的功能书写。
传统写法:
// 注册
。。。
// 注册完毕增加金币积分次数
addGold();
addScore();
addTimes();
很显然,此时如果三个人修改同一段代码必然导致冲突。
使用事件则可以如下方式完成:
①,约定事件名称为register_ok,分别绑定三个监听类(目的是将上例中AfterRegister中的handle方法中+金币+积分+次数拆开,分别放到AddGold,AddScore,AddTimes的handle函数中):
return [
'bind' => [
],
'listen' => [
// 给register_ok事件,绑定三个监听类,分别为AddGold,AddScore,AddTimes
'register_ok' => ['app\listener\AddGold::class','app\listener\AddScore::class','app\listener\AddTimes::class']
],
];
②,在app\listener中加三个类,分别为AddGold,AddScore,AddTimes
③,在AddGold的handle方法中写增加金币的业务,AddScore的handle方法中写增加积分的业务,AddTimes的handle方法中写增加次数的业务
④,在注册代码的下方,加入事件触发,即用一个Event::trigger('register_ok',$user);取代了原本 addGold(); addScore(); addTimes();这种若干个函数的写法。
至此则完成了,N个监听类去监听一个事件的功能。或许有人说,传统写法一样可以用,三个人分别实现addGold(); addScore(); addTimes(); 就可以了,那么看下面这个场景
事件使用场景二:
某系统会员注册成功后,增加金币。
过了几周,要求增加金币的同时增加积分。
又过了几周,要求增加金币和积分的同时增加登录次数。
又过了几周,要求只增加金币和登录次数,不加积分了。
又过了几周,要求注册成功后增加金币和登录次数的同时,记录下会员的登录IP。
如果是使用传统方式,需要对源代码进行不断的修改和注释,事件则方便的多,只追加监听器和修改配置文件event.php,而不必改注册部分的业务代码。可见,事件监听的实现方式,在频繁出现业务变更的地方,有着传统方式不能比的优势。
---------------------------------------------------------------------------------------------------
后记:
1. TP6的文档中给出了事件类event的定义,实际中几乎没有使用的必要,徒增理解成本。
2. 对于事件的监听也给出了Event::listen的动态绑定方式,远不如在event.php中指定,更为方便和灵活可控。
3. 通常我们理解的绑定是 将监听器绑定到事件,这也是JS中的addEventListener的语义,TP6文档给出的绑定定义则是,将事件名称绑定到对应的事件类。
4. 基于以上三点,个人推荐采用 event.php配置文件进行监听, 用Event::trigger的方式进行触发事件,而不要用Event::bind和Event::listen。