Yii2的事件

Yii2的事件

一、事件

事件是什么?

     事件可以将自定义代码“注入”到现有代码中的特定执行点。 附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行。事件既是代码解耦的一种方式,也是设计业务流程的一种模式。

     在Yii2中可以很好的支持事件,在执行一个操作后,可以触发一个事件,实现不同的功能。比如用户在网站注册成功后,接下来网站要给用户邮箱发送通知的邮件。这就是两个相当独立的功能,我们就可以定义好这些独立的事件,在发布成功后,按顺序触发这些事件,一方面可以解耦代码复杂的关系,另一方面利于维护事件相对于硬编码的方式来说也增加了服务器资源开销,所以比较建议在任务较为复杂时使用事件!

     Yii2 引入了名为 yii\base\Component 的基类以支持事件。 如果一个类需要触发事件就应该继承 yii\base\Component 或其子类。同时,Yii2中还有一个与事件紧密相关的 yii\base\Event ,他封装了与事件相关的有关数据,并提供一些功能函数作为辅助。

     在命名上,yii2 借用了 jquery 事件系统的那一套,on,off,trigger。

事件的绑定

     在yii2中,事件的绑定是通过yii\base\Component的 on 方法进行操作的,很显然,同js操作一样,我们在定义事件的同时,需要为其绑定一个回调函数,用于事件触发。

 1 <?php
 2 
 3 namespace backend\controllers;
 4 
 5 use Yii;
 6 use yii\web\Controller;
 7 
 8 class EventTestController extends Controller
 9 {
10     const EVENT_TEST = 'event_test';
11 
12     public function init ()
13     {
14         parent::init();
15         // 这里写了一个function作为回调函数
16         $this->on(self::EVENT_TEST, function () {
17             echo "I`m a test event.";
18         });
19     }
20 }

    注意:推荐使用类常量来表示事件名。上例中,常量 EVENT_TEST 用来表示 test 。 这有两个好处。第一,它可以防止拼写错误并支持 IDE 的自动完成。 第二,只要简单检查常量声明就能了解一个类支持哪些事件

    关于回调函数的写法,有以下几种方式:

 1 $foo = new Foo;
 2 
 3 // 处理器是全局函数
 4 $foo->on(Foo::EVENT_HELLO, 'function_name');
 5 
 6 // 处理器是对象方法
 7 $foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);
 8 
 9 // 处理器是静态类方法
10 $foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);
11 
12 // 处理器是匿名函数
13 $foo->on(Foo::EVENT_HELLO, function ($event) {
14     //事件处理逻辑
15 });

事件的触发

    如果一个事件只有定义,但是却没有被触发,那这个事件要之何用?事件的触发也很简单,只需要调用 yii\base\Component类的trigger方法,指定事件名即可。

 1 <?php
 2 
 3 namespace backend\controllers;
 4 
 5 use Yii;
 6 use yii\web\Controller;
 7 
 8 class EventTestController extends Controller
 9 {
10     const EVENT_TEST = 'event_test';
11 
12     // ...
13     public function actionIndex (){
14        $this->trigger(self::EVENT_TEST);
15     }
16 
17 }

   有些时候,我们想要在触发事件时同时传递一些额外信息到事件处理器,比如下面:

 1 <?php
 2 
 3 namespace app\components;
 4 
 5 use yii\base\Component;
 6 use yii\base\Event;
 7 
 8 class MessageEvent extends Event
 9 {
10     public $message;
11 }
12 
13 class Mailer extends Component
14 {
15     const EVENT_MESSAGE_SENT = 'messageSent';
16 
17     public function send($message)
18     {
19         // ...发送 $message 的逻辑...
20 
21         $event = new MessageEvent;
22         $event->message = $message;
23         $this->trigger(self::EVENT_MESSAGE_SENT, $event);
24     }
25 }

事件的移除

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

 1 // 处理器是全局函数
 2 $foo->off(Foo::EVENT_HELLO, 'function_name');
 3 
 4 // 处理器是对象方法
 5 $foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);
 6 
 7 // 处理器是静态类方法
 8 $foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);
 9 
10 // 处理器是匿名函数
11 $foo->off(Foo::EVENT_HELLO, $anonymousFunction);

二、预定义事件

     预定义事件就是yii在源码内提前定义好的事件,注意这个定义好并不是其提前on好的,而是提前trigger好的。yii内其实还是定义了很多事件的,我们主要介绍三种事件的使用,包括跟应用相关的,跟user组件相关以及最常用的model相关事件。

1、应用相关事件

    在yii中,我们知道,一个项目,就是一个应用。在程序中这个应用用Yii::$app来表示,既然是应用级别的事件,那自然就跟Yii::$app有关。
    yii\base\Application类中定义了下面这两个事件:

 1 <?php
 2 namespace yii\base;
 3 
 4 use Yii;
 5 abstract class Application extends Module
 6 {
 7     const EVENT_BEFORE_REQUEST = 'beforeRequest';
 8 
 9     const EVENT_AFTER_REQUEST = 'afterRequest';
10     

   下面通过一段代码来测试下:

 1 <?php
 2 
 3 defined('YII_DEBUG') or define('YII_DEBUG', true);
 4 defined('YII_ENV') or define('YII_ENV', 'dev');
 5 
 6 //...
 7 
 8 // (new yii\web\Application($config))->run();
 9 $application=new \yii\web\Application($config);
10 Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST,function ($event){
11     yii::info('请求前:This is beforeRequest event.');
12 });
13 Yii::$app->on(\yii\base\Application::EVENT_AFTER_REQUEST,function ($event){
14     yii::info('请求后:This is afterRequest event.');
15 });
16 $application->run();

 

   刷新网页后,通过yii2-debug我们看到下面信息(为了方便查看,中间省略了一些其他信息):

  

 

2、user组件相关事件

     user组件类yii\web\User定义了4个事件,包括登陆前后和退出前后各两个事件,打开该文件看下:

 1 <?php
 2 namespace yii\web;
 3 
 4 use Yii;
 5 use yii\base\Component;
 6 use yii\base\InvalidConfigException;
 7 use yii\base\InvalidValueException;
 8 use yii\rbac\CheckAccessInterface;
 9 
10 class User extends Component
11 {
12     const EVENT_BEFORE_LOGIN = 'beforeLogin';
13     const EVENT_AFTER_LOGIN = 'afterLogin';
14     const EVENT_BEFORE_LOGOUT = 'beforeLogout';
15     const EVENT_AFTER_LOGOUT = 'afterLogout';

    开发中可能会有这样的需求:用户登录成功后,更新用户最后的登录时间,ip等信息,比如下面:

 1 <?php
 2 namespace common\models;
 3 
 4 use Yii;
 5 use yii\base\Model;
 6 
 7 class AdminLoginForm extends Model
 8 {
 9     public $username;
10     public $password;
11     public $rememberMe = true;
12 
13     private $_user;
14     // ...
15 
16     public function login()
17     {
18         if ($this->validate()) {
19             Yii::$app->user->on(yii\web\User::EVENT_AFTER_LOGIN, [$this, 'onAfterLogin']);
20             // 校验成功后,session保存用户信息
21             return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
22         } else {
23             return false;
24         }
25     }
26 
27     public function onAfterLogin ($event)
28     {
29       $identity = $event->identity;
30       $date = date('Y-m-d H:i:s');
31       yii::info("id={$identity->id}的用户最后一次登录系统的时间是{$date}");
32     }
33 }

3、model层相关事件

     既然跟model相关,那自然就离不开yii\db\BaseActiveRecord了,打开该文件看下,果然里面定义了9个事件:

 1 <?php
 2 
 3 namespace yii\web;
 4 
 5 namespace yii\db;
 6 
 7 use yii\base\Event;
 8 use yii\base\InvalidCallException;
 9 use yii\base\InvalidConfigException;
10 use yii\base\InvalidParamException;
11 use yii\base\Model;
12 use yii\base\ModelEvent;
13 use yii\base\NotSupportedException;
14 use yii\base\UnknownMethodException;
15 use yii\helpers\ArrayHelper;
16 
17 abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
18 {
19 
20     const EVENT_INIT = 'init';
21 
22     const EVENT_AFTER_FIND = 'afterFind';
23 
24     const EVENT_BEFORE_INSERT = 'beforeInsert';
25 
26     const EVENT_AFTER_INSERT = 'afterInsert';
27 
28     const EVENT_BEFORE_UPDATE = 'beforeUpdate';
29 
30     const EVENT_AFTER_UPDATE = 'afterUpdate';
31 
32     const EVENT_BEFORE_DELETE = 'beforeDelete';
33 
34     const EVENT_AFTER_DELETE = 'afterDelete';
35 
36     const EVENT_AFTER_REFRESH = 'afterRefresh';
37     

  若想要触发yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT事件,第一步我们先要在model类中绑定yii\db\BaseActiveRecord::EVENT_BEFORE_INSERT

  下面我们打开common\models\Post类,增加init方法如下:

 1 <?php
 2 namespace common\models;
 3 
 4 use Yii;
 5 use yii\bootstrap\Html;
 6 
 7 class Post extends \yii\db\ActiveRecord
 8 {
 9     public function init ()
10     {
11        parent::init();
12        $this->on(self::EVENT_BEFORE_INSERT, [$this, 'onBeforeInsert']);
13        $this->on(self::EVENT_AFTER_INSERT, [$this, 'onAfterInsert']);
14     }
15     
16     // ...
17 
18     public function onBeforeInsert ($event)
19     {
20        yii::info('This is beforeInsert event.');
21     }
22 
23     public function onAfterInsert ($event)
24     {
25        yii::info('This is alterInsert event.');
26     }
27 }

 

参考链接:

http://www.manks.top/document/yii2-event-predefine.html

posted @ 2021-05-05 11:30  欢乐豆123  阅读(367)  评论(0编辑  收藏  举报