yii2项目实战-预定义事件的使用

前面我们讲解了事件的基本操作,这节课我们主要讲一下yii2预定义事件的使用。

什么是预定义的事件呢?

就是yii在源码内提前定义好的,注意这个定义好并不是其提前on好的,而是提前trigger好的。当然你可能听不懂,带着疑问继续看下去。

yii内其实还是定义了很多事件的,我们主要介绍三种事件的使用,包括跟应用相关的,跟user组件相关以及最常用的model相关事件,另外在文中案例的,我们多次提及yii2-debug,也希望各位能把yii2-debug掌握了,实际开发中更可以充分利用yii2-debug进行调试优化。

前方预警:本篇文章含金量高且内容知识点较多,请注意阶段性揣摩休息再继续。

老师你别BB了,我都等不及了,快开始吧

应用相关事件

在yii中,我们知道,一个项目,就是一个应用。在程序中这个应用用Yii::$app来表示,既然是应用级别的事件,那自然就跟Yii::$app摆脱不了干系!

正如我们在事件初识一文所说,当你尝试去分析yii执行过程的时候,yii\base\Application类中定义了下面这两个事件

1
2
const EVENT_BEFORE_REQUEST = 'beforeRequest';
const EVENT_AFTER_REQUEST = 'afterRequest';

并在yii\base\Application的run方法中触发,即分别在处理请求的前后触发。

通过前面的学习我们知道,要想使trigger起作用,只需要在trigger之前通过on绑定好事件就可以了。也就是说我们现在只需要在执行yii\base\Application的run方法之前通过on绑定该事件并指定回调函数就能够被触发。来看下我们的例子

打开backend\web\index.php文件,我们写点什么

1
2
3
4
5
6
7
8
$application = new yii\web\Application($config);
Yii::$app->on(yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) {
yii::info('This is beforeRequest event.');
});
Yii::$app->on(yii\base\Application::EVENT_AFTER_REQUEST, function ($event) {
yii::info('This is afterRequest event.');
});
$application->run();

为了证明是否是在处理请求前后触发,请务必打开yii自带的debug后再访问/index.php。

在debug中找到Log相关,为了说明问题,我这里简要复制下日志信息,当然你的条数可能跟我这里不一样,大概会差不多。

1
2
3
4
5
6
7
#	Time	Level	Category	Message
1 22:42:33.159 trace yii\base\Application::bootstrap Bootstrap with yii\log\Dispatcher
2 22:42:33.159 trace yii\base\Module::getModule Loading module: debug
// 此处省略第 3-5条日志信息
6 22:42:33.194 info application This is beforeRequest event.(省略具体文件路径)
// 此处省略第 7-21条日志信息
22 22:42:33.487 info application This is afterRequest event.(省略具体文件路径)

说明:上面的第一列 1 2 6 22 指的是执行的先后顺序,第二列是执行的时间,第三列是日志级别,我们在index.php中用的是yii::info,因此这里的级别是info,第四列是分类,最后一列便是我们记录的消息了。

通过debug分析,我们很轻松的证明了官方的代码没有错,我们的理解也没有错!

友情提醒:yii的debug那是相当的好用,还不用使用的同学抓紧时间尝鲜啦。熟练使用debug,妈妈再也不用担心开发中的调试优化了。

user组件相关事件

广告:可能很多小伙伴下面的精彩内容就无法享受了,我还是希望感兴趣的同学花一份午饭钱购买后再继续看下去,一个系列一顿饭,“好吃不贵”。正所谓不花钱不心疼不珍惜嘛。

广告回来,来来我们继续。

时至今日,yii2的user组件不知道各位还有没有印象了?实际开发中我们往往有这么一个需求:

用户登录成功后,更新用户最后的登录时间,ip等信息,这个怎么去实现呢?

user组件类yii\web\User定义了4个事件,包括登陆前后和退出前后各两个事件。考虑到修改表字段相对麻烦,再加之上面刚刚接触debug,我们以登陆后在debug中记录用户最后登录系统的时间为例进行说明

以当前项目的登录为例,我们找到common\models\LoginForm的login方法比并稍作修改

1
2
3
4
5
6
7
8
9
10
11
public function login()
{
// 调用validate方法 进行rule的校验,其中包括用户是否存在和密码是否正确的校验
if ($this->validate()) {
Yii::$app->user->on(yii\web\User::EVENT_AFTER_LOGIN, [$this, 'onAfterLogin']);
// 校验成功后,session保存用户信息
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
} else {
return false;
}
}

上例中,我们在user组件调用login方法之前绑定了user组件的EVENT_AFTER_LOGIN事件,并指定事件触发的回调处理程序是当前类的onAfterLogin方法,我们来对该方法进行一下补充

1
2
3
4
5
6
public function onAfterLogin ($event)
{
$identity = $event->identity;
$date = date('Y-m-d H:i:s');
yii::info("id={$identity->id}的用户最后一次登录系统的时间是{$date}");
}

此时我们退出系统并重新登录,登陆成功后,打开debug,在Log那里找我们刚刚post提交的那个操作的日志信息,这里一定要注意,不要看错日志信息了,不然找不到还以为自己又写错了!

如果没猜错的话,大概会在倒数最后几条日志那里,仔细看看是不是有记录我们刚刚登录的日志信息?

user组件的其他3种自己动手尝试吧,我们还要继续接着说更重要的model事件操作!

model层相关事件

既然跟model相关,那自然就离不开yii\db\BaseActiveRecord了,找到该文件并打开瞧上一瞧,你会发现这家伙定义了下面9个事件

1
2
3
4
5
6
7
8
9
const EVENT_INIT = 'init';
const EVENT_AFTER_FIND = 'afterFind';
const EVENT_BEFORE_INSERT = 'beforeInsert';
const EVENT_AFTER_INSERT = 'afterInsert';
const EVENT_BEFORE_UPDATE = 'beforeUpdate';
const EVENT_AFTER_UPDATE = 'afterUpdate';
const EVENT_BEFORE_DELETE = 'beforeDelete';
const EVENT_AFTER_DELETE = 'afterDelete';
const EVENT_AFTER_REFRESH = 'afterRefresh';

word天,还挺多!给我们预留那么多的事件,无非证明一件事,官方装X!既然预留了,那我们用上一用,也来装个X呗。

我们这里分别以EVENT_BEFORE_INSERT和EVENT_AFTER_INSERT即执行insert前后触发的事件为例进行说明。

我们来继续扯一扯insert前后触发的事件,注意我们说的是insert前后哦,save新数据也能触发,不过save一条新数据最终也是调用yii\db\ActiveRecord方法的insert啦。

你TM又叨逼叨,叨逼叨,这个到底要怎么用!

好吧,来继续。

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

打开common\models\Blog类,增加init方法如下

1
2
3
4
5
6
public function init ()
{
parent::init();
$this->on(self::EVENT_BEFORE_INSERT, [$this, 'onBeforeInsert']);
$this->on(self::EVENT_AFTER_INSERT, [$this, 'onAfterInsert']);
}

这里指定回调处理函数是当前类的onBeforeInsert和onAfterInsert方法,因此我们还要再实现一下这两个方法

1
2
3
4
5
6
7
8
public function onBeforeInsert ($event)
{
yii::info('This is beforeInsert event.');
}
public function onAfterInsert ($event)
{
yii::info('This is alterInsert event.');
}

此刻,你知道该怎么验证我们绑定的事件是否触发了吗?

猜的真对,我们只需要访问 /index.php?r=blog/create 创建一篇文章便可证明

之后,通过debug,还是在Log那里,找到我们刚刚post操作的日志,发现有这三条,贴出来一起看下

1
2
3
4
#	Time	Level	Category	Message
15 13:10:56.419 info application This is beforeInsert event.
16 13:10:56.428 info yii\db\Command::execute INSERT INTO `blog` (`title`, `content`, `is_delete`, `created_at`, `updated_at`) VALUES ('333', '3333333333333333', 1, '2016-11-19 13:10:56', '2016-11-19 13:10:56')
17 13:10:56.441 info application This is alterInsert event.

显而易见,model层面的触发就是这么玩的,你看懂了吗?其他model层面的事件,大家也都可以自己手动尝试尝试

以上3种,算是我们针对yii预定义事件做的一个全面的实例讲解。