abei1982 2017-05-23 00:10:21 10623次浏览 12条回复 15 1 0

书接上回,我将代码提交到服务器后被小X经理一顿批,她是这样说的。

小X: 你丫不知道yii2自己就有登陆后的事件么?
我:不知道啊,咋的吧?
小X:改!然后统计个yii2预定义事件表给我。
我:哦,好的。

好吧,算咱技术不到家,那就赶紧改吧,先回顾下上一篇我都干了啥,我是在会员登陆后触发了一个事件,既然说有内置的,那就找找吧。

我知道登陆功能是使用yii\web\User这个类,那应该去那里去找它们。它在@app/vendor/yiisoft/yii2/web/User.php

@@nai8@@

5分钟以后~

果然,我找到了那个事件,还用了3分钟分析了如何使用,现在把我的研究成果和大家分享下。

对于yii2系统的登陆,yii\web\User类一共提供了4个事件,如下

const EVENT_BEFORE_LOGIN = 'beforeLogin';
const EVENT_AFTER_LOGIN = 'afterLogin';
const EVENT_BEFORE_LOGOUT = 'beforeLogout';
const EVENT_AFTER_LOGOUT = 'afterLogout';

分别是登陆前后、注销前后,我要使用的是EVENT_AFTER_LOGIN事件,那么如何去使用那?

既然是登陆,还是先看下yii\web\User中的login方法,看看是否有蛛丝马迹

public function login(IdentityInterface $identity, $duration = 0){

    if ($this->beforeLogin($identity, false, $duration)) {
        $this->switchIdentity($identity, $duration);
        $id = $identity->getId();
        $ip = Yii::$app->getRequest()->getUserIP();
        if ($this->enableSession) {
            $log = "User '$id' logged in from $ip with duration $duration.";
        } else {
            $log = "User '$id' logged in from $ip. Session not enabled.";
        }
        Yii::info($log, __METHOD__);
        $this->afterLogin($identity, false, $duration);
    }

    return !$this->getIsGuest();
}    

我看到了 $this->afterLogin($identity, false, $duration),似乎这个方法是登陆后做了一些事情,那就继续看吧

protected function afterLogin($identity, $cookieBased, $duration){

    $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent([
        'identity' => $identity,
        'cookieBased' => $cookieBased,
        'duration' => $duration,
    ]));
}    

OMG,真的发现了,原来这个方法触发了EVENT_AFTER_LOGIN事件,并且还很贴心的将identity等信息传递给了绑定EVENT_AFTER_LOGIN事件的每一位观察者。

于是思路来了

我只需要在login前绑定EVENT_AFTER_LOGIN事件,然后afterLogin会自动触发所有订阅者。

重构开始

use yii\web\User;
class UserController extends Controller {
	
	public function __construct(){
		//	绑定事件
		$this->on(User::EVENT_AFTER_LOGIN,['app\models\OLog','add']); 
		$this->on(User::EVENT_AFTER_LOGIN,['app\models\Admin','sendMail']); 
		$this->on(User::EVENT_AFTER_LOGIN,['app\models\User','notifyFirend']); 
	}
	
	public function actionIndex(){
		.....
		//	login				
	}
}

我之前定义的EVENT_USER_LOGIN自然可以删除了,上面看到afterLogin方法触发事件的时候已经传递了一个UserEvent,里面含有登陆的详细信息了,我上一篇自己定义的那个UserLoginEvent也就可以删除了。

使用系统自带的事件,真心省了太多代码了,感谢yii2开发团队,感谢你们八倍祖主。

对于登陆主题就重构完了,现在我还需要改下订阅者的代码,毕竟传递给订阅者的$event不一样了,拿一个订阅者举例吧

// User app\models\User.php
class User {
    static public function notifyFirend($event){
        $userId = $event->userId;
        echo "告诉了朋友们我登陆了";
    }
}

我要对其进行小手术,diu一下,手术后的样子如下

// User app\models\User.php
class User {
    static public function notifyFirend($event){
        $userId = $event->identity->id;
        echo "告诉了朋友们我登陆了";
    }
}

ok~ 到此为止我们对登陆逻辑的事件处理就完成了,去掉了自定义的事件,绑定了 User::EVENT_AFTER_LOGIN 内置事件。

还没完~

是的还没完,小X经理还让我提交一个yii2预定义事件表给她,我深刻体会到这个表格的重要性,有了它,便可以通晓yii所有事件,正所谓就算成功,也不一定自宫。

但是,yii2这么大,我如何一个不落的得到它们那?

看来要借助于我们的神器PHPSTORM了。

首先我们知道yii2对于事件名的命名规则,常量大写、观察下发现内置的这些事件都是EVENT_开头的,有了这些特征,那开始吧。

方法如下:我们对vendor/yiisoft目录进行查找(find in path),找到所有const EVENT_开头的行。

Snip20170522_34.png

表之event内置一览表 http://nai8.me/tool-sc.html

建议你背下这些事件,至少在做事件的时候查一遍速查表,能用系统的优先使用系统的。

学到了一些

很高兴完成了小x经理关于登陆事件的需求,通过这些让我对yii的事件有了一个大体了解,知道了事件是观察者模式的一种实现、知道yii2内置了很多事件,我们应该优先使用它们。

begin git。

一阵QQ震动,带着坏笑的小x经理发了一句消息给我:“来,小北同学!”

天,又咋了,下篇告诉你。

觉得很赞
  • 回复于 2017-05-24 12:22 举报

    按你说的报错啊,

    $this->on(User::EVENT_AFTER_LOGIN,['app\models\OLog','add']); 
    $this->on(User::EVENT_AFTER_LOGIN,['app\models\Admin','sendMail']); 
    $this->on(User::EVENT_AFTER_LOGIN,['app\models\User','notifyFirend']); 
    

    可以这样吗

    1 条回复
    回复于 2017-05-24 12:26 回复

    use yii\web\User; 了么,什么报错信息

  • 回复于 2017-05-24 15:45 举报

    不报错了,但是怎么知道这个方法执行了呢,

    static public function notifyFirend($event){
        $userId = $event->userId;
        echo "告诉了朋友们我登陆了";
    }
    

    我也自定义了这个方法,就是不执行啊

    3 条回复
    回复于 2017-05-24 22:25 回复

    运行触发事件的控制器,就会执行

    回复于 2017-05-24 22:27 回复

    在方法中 $this->trigger(),这样触发时间

    回复于 2017-05-24 22:30 回复

    要先绑定,建议先看我上一篇文章,先搞懂观察者模式,然后在看事件机制,搞懂原理。
    我的yii视频小站 nai8.me

  • 回复于 2017-05-24 22:29 举报

    对了,你怎么晓得他是观察者模式呢 ????

    1 条回复
    回复于 2017-05-24 22:31 回复

    给强哥发了一个facebook留言

  • 回复于 2017-08-09 10:46 举报
    $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent([
        'identity' => $identity,
        'cookieBased' => $cookieBased,
        'duration' => $duration,
    ]));
    

    请问你这个登录后触发 EVENT_AFTER_LOGIN 事件, new UserEvent 这个又是什么? 你文章没有这个类啊
    绑定的事件触发 User 下的 notifyFirend 的方法 这个 new UserEvent 是带入到 notifyFirend($event)的实例化参数吗

    class User {
        static public function notifyFirend($event){
            $userId = $event->identity->id;
            echo "告诉了朋友们我登陆了";
        }
    }
    
  • 回复于 2017-11-13 16:48 举报

    请教:使用basic模板时,User.php是继承了\yii\base\Object,这些没法弄?

    觉得很赞
  • 回复于 2017-11-16 09:15 举报
    • 请教: afterLogin触发时,事件的订阅者(绑定的事件处理handle)并不会被通知到,此时,绑定了handle的_events数组在identity里面的_events中,$this已经变化了,$this中的events(没有包含我们绑定的handle)才会被触发到,请问如何解决呢?
    • 如果用自定义事件来触发
    • $this->trigger(self::EVENT_USER_LOGIN);
    • $this触发时和绑定时的$this是一样的,此时绑定的handle正好在$this的_events数组中,当然可以触发的,没有问题。
    • 如果用内置的事件来触发
    • Yii::$app->user->login($user);
    • 触发afterLogin时,$this已经变成了yii\web\Use
    • 999.png
  • 回复于 2017-11-16 10:09 举报

    搞定了~~

    觉得很赞
  • 回复于 2017-12-12 15:42 举报

    哥们,你有写故事的潜质啊!哈哈,看着挺乐呵的。

  • 回复于 2017-12-26 18:12 举报
  • 回复于 2019-11-29 15:14 举报

    在controller的Action中写的绑定事件,之后事件不会执行,

    请问题主的controller继承的是哪个呢?我继承的是yii\web\Controller。
    /**

     * @param string $id the ID of this controller.
     * @param Module $module the module that this controller belongs to.
     * @param array $config name-value pairs that will be used to initialize the object properties.
     */
    public function __construct($id, $module, $config = [])
    {
        $this->id = $id;
        $this->module = $module;
        parent::__construct($config);
    }
    
    2 条回复
    回复于 2019-11-29 15:16 回复

    难道必须要把on写到构造函数里?没道理啊

    回复于 2020-04-08 17:51 回复

    根据我自己实测的经验,使用$this->on绑定并不会触发绑定事件,使用yii::$app->user->on的绑定可以正常触发登录事件

  • 回复于 2020-04-08 17:51 举报

    根据我自己实测的经验,使用$this->on绑定并不会触发绑定事件,使用yii::$app->user->on的绑定可以正常触发登录事件

    觉得很赞
  • 回复于 2020-04-23 13:42 举报

    这样每个事件的数据就没法共用,要么靠传递 要么就几个事件都查询一次,这样也还都是同步的,丢队列处理或许更好

您需要登录后才可以回复。登录 | 立即注册