login event handling in laravel 5
In laravel 5.2; auth.login won't work... the following will have to be used:
protected $listen = [
'Illuminate\Auth\Events\Attempting' => [
'App\Listeners\LogAuthenticationAttempt',
],
'Illuminate\Auth\Events\Login' => [
'App\Listeners\LogSuccessfulLogin',
],
'Illuminate\Auth\Events\Logout' => [
'App\Listeners\LogSuccessfulLogout',
],
'Illuminate\Auth\Events\Lockout' => [
'App\Listeners\LogLockout',
],
];
As stated in the documentation here
Be careful about asking what the best way to do X is, because Laravel, in particular, provides many ways of accomplishing the same task -- some are better than others in certain situations.
Taking a look at the Laravel documentation, personally I would go with the "Basic Usage" as it seems to match the use case you have stated.
If we run the following Artisan command we can generate a template for the UserLoggedIn event.
$ php artisan make:event UserLoggedIn
(note the past tense, because events happen, and then the subscribers are notified of the event having taken place)
(note 2: the app
string in namespaces is what Laravel uses out of the box, it is likely different for you if you have executed the php artisan app:name
command)
The following class is generated for us:
<?php namespace app\Events;
use app\Events\Event;
use Illuminate\Queue\SerializesModels;
class UserLoggedIn extends Event {
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}
If we add a userId
parameter to the constructor, then the event doesn't need to know about the Auth Facade/Guard Contract. This means our UserLoggedIn
event code is not tightly coupled to Eloquent or which ever authentication framework you decide to utilize in your app. Anyways, let's add that userId
parameter.
<?php namespace app\Events;
use app\Events\Event;
use app\User;
use Illuminate\Queue\SerializesModels;
class UserLoggedIn extends Event {
use SerializesModels;
public $userId;
/**
* Create a new event instance.
*
* @param int userId the primary key of the user who was just authenticated.
*
* @return void
*/
public function __construct($userId)
{
$this->userId = $userId;
}
}
Now you're probably wondering, well that's great and all, but how to we act on this event? Great question! We need to create an event handler to handle when this event is fired. Let's do that now using Artisan:
$ php artisan handler:event UpdateUserMetaData --event=UserLoggedIn
We name our new event handler UpdateUserMetaData
and tell Artisan that the event we want to handle is the UserLoggedIn
event.
Now we have some code that looks like this inside of app/Handlers/Events/UpdateUserMetaData.php
:
<?php namespace app\Handlers\Events;
use app\Events\UserLoggedIn;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
class UpdateUserMetaData {
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param UserLoggedIn $event
* @return void
*/
public function handle(UserLoggedIn $event)
{
//
}
}
We can update the handle method to be able to handle this event like you specified above quite easily:
<?php namespace app\Handlers\Events;
use app\Events\UserLoggedIn;
use Illuminate\Http\Request;
class UpdateUserMetaData {
protected $request;
/**
* Create the event handler.
*
* @param Request $request
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Handle the event.
*
* @param UserLoggedIn $event
*/
public function handle(UserLoggedIn $event)
{
$user = User::find($event->userId); // find the user associated with this event
$user->last_login = new DateTime;
$user->last_login_ip = $this->request->getClientIp();
$user->save();
}
}
As a side note, if you're not familiar with Carbon, you might want to look into using it so you can take advantage of its fantastic API like you can with Eloquent's created_at
and updated_at
timestamp fields on most models. Here's a link for how to tell Eloquent which fields should be used with Carbon: http://laravel.com/docs/master/eloquent#date-mutators.
There are two final steps we have to perform before this code will work in your Laravel app.
We need to map the event to the event handler in the
EventServiceProvider
class under theapp/Providers
directory.We need to fire the event after login.
To complete the first step, we just need to add our event classes to the $listeners
property in app/Providers/EventServiceProvder.php
like so:
UserLoggedIn::class => [
UpdateUserMetaData::class
]
The above will work provided you import the classes inside the EventServiceProvider
class, and you are using PHP 5.5. If you're using a lower PHP version, you'll need to provide the full path to each class as a string like this: 'app/Events/UserLoggedIn'
and 'app/Handlers/Events/UpdateUserMetaData'
.
The $listeners
array maps events to their respective handlers.
Okay, now for the final step! In your code base, find the place where the user is authenticated and add the following:
event(new \app\Events\UserLoggedIn(Auth::user()->id));
And we're done! I tested this code as I wrote this answer, feel free to ask follow up questions if you have any.
EDIT: this only works in 5.0.* and 5.1.*.
For the 5.2.* solution see JuLiAnc response below.
after working with both proposed answers, and some more research i finally figured out how to do this the way i was trying at first.
i ran the following artisan command
$ php artisan handler:event AuthLoginEventHandler
Then i altered the generated class removing the import of the Event class and and imported the user model. I also passed User $user
and $remember
to the handle method since when the auth.login event is fired, thats what is passed.
<?php namespace App\Handlers\Events;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
use App\User;
class AuthLoginEventHandler {
/**
* Create the event handler.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param User $user
* @param $remember
* @return void
*/
public function handle(User $user, $remember)
{
dd("login fired and handled by class with User instance and remember variable");
}
}
now i opened EventServiceProvided.php and modified the $listen
array as follows:
protected $listen = [
'auth.login' => [
'App\Handlers\Events\AuthLoginEventHandler',
],
];
i realized if this doesn't work at first, you may need to
$ php artisan clear-compiled
There we go! we can now respond to the user logging in via the auth.login event using an event handler class!