Listening for logout event in Yii 2

2019-06-24 08:53发布

问题:

So I know that Yii::$app->user triggers these events in the logout process.

const EVENT_BEFORE_LOGIN = 'beforeLogin';
const EVENT_AFTER_LOGIN = 'afterLogin';

But where is the right place to add a listener at the beginning of each request?

回答1:

The best place would be in the class which handles the identity of yii/web/User; usually, this is common/models/User.

In that class, you can have the following initialization.

public function registerLogoutHook()
{
    $this->on(\yii\web\User::EVENT_AFTER_LOGOUT, function ($e) {
        Yii::$app->controller->goHome();
    });
}

You may choose to register this on the web User object [of identity `common/models/User] who successfully logged in. The logic could be as follows

// [ ... omitted code ... ]
// $model is an instance of LoginForm
if ($model->load(Yii::$app->request->post()) && $model->login()) {
    Yii::$app->user->identity->registerLogoutHook();
    return $this->redirect(['customers/view', 'id' => Yii::$app->user->identity->id]);
}
// [ ... omitted code ... ]

EDIT (:

Another way to get this working as of now ("yiisoft/yii2":"2.0.7") is to add the event handler to your config file. This way all handlers are setup early on in the application life cycle.

So in your main.php you would be returning...

[
    // ... some other things ...
    'components' => [
        // ... some other components ...
        'user' => [
            // ... some configuration for yii\web\User ...
            'on beforeLogout' => function ($e) {
                Yii::trace('Logout events are working!!!');
            },
            // ... more configurations for yii\web\User ...
        ],
        // ... some more components ...
    ],
    // ... some more things
]

For the handler, you could either use a callback, the name of a function or an array specifying the object and its method to be called. These methods of registering an event are enumerated here.

beforeLogout can be changed to any of the events raised by yii\web\User



回答2:

After a lot of digging through the code, I finally found whats the problem. You might think that since you are passing your User model as an identity to the logout function and assigning events to it it will work, but actually the events of current Component are being triggered.

So that means, if you do what was suggested above and assign the event to your User model, it will not get triggered. Instead you need to assign the event to the User component itself.

My solution (that actually works)

In your User model:

public static function beforeLogoutHook(){
    // Do whatever
}

Somewhere in your code I recommend doing this in some global place In my case my master controller that is being extended by other controllers.

public function auth(){
    $user = new User();
    $user->setAttributes($attributes,false);

    Yii::$app->user->login($user);

    Yii::$app->user->on(\yii\web\User::EVENT_BEFORE_LOGOUT,function($e){
        User::beforeLogout();
    });
}

Now you can simply call Yii::$app->user->logout(); whereever you need and the beforeLogout event will get executed.

I have chosen to declare the function that does the work before logout in my User model to make it easier to find and edit.