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