Listening events

https://laravel.com/api/5.6/Illuminate/Auth/Events/Login.html

Table des matières

1. Listeners

Laravel use a Provider for knowing who is listening events.

Check the file /app/Providers/EventServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        'App\Events\Event' => ['App\Listeners\EventListener'],
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();
    }
}

For adding a listener, we just need to add our listener in $listen so Laravel can manage who is listening which event.

We’ll need to extend the $listen array and provide to information’s:

  1. The namespace of the listened event. If we want to listen to the Login event, the name is Illuminate\Auth\Events\Login (defined in /vendor/laravel/framework/src/Illuminate/Auth/Events/Login.php)
  2. The namespace of the listener. Since the default location is folder /app/Listeners the listener can be /app/Listeners/MyListeningClass.

For instance:

protected $listen = [
    'App\Events\Event' => ['App\Listeners\EventListener'],
    'Illuminate\Auth\Events\Login' => ['App\Listeners\LoginSuccess']
];

2. A few examples

2.1. Queries

Each time a query is fired, an event is triggered with the SQL statement of the query just ran against the database.

We can catch this for, f.i., displaying the list of queries during our optimization process:

2.1.1. Spy the list of fired queries

A very fast way is to add this function in our /app/routes.php file. Just add this bloc:

if (env('APP_DEBUG', false)) {
    DB::listen(function ($query) {
        echo '<pre style="background-color:yellow;' .
        'font-size:x-small;">' .
        'Query fired ' .
        '"' . $query->sql . '" ' .
        '<small>(' . __FILE__ . ' - ' . __LINE__ . ')</small>' .
        '</pre>';
    });
}

This will output the list of queries when APP_DEBUG is set.

Listen queries

2.2. Listening Login event

2.2.1. Why ?

Once someone is making a login, we can:

2.2.2. Attach a listener

Edit /app/Providers/EventServiceProvider.php and update the $listen property:

protected $listen = [
    'App\Events\Event' => ['App\Listeners\EventListener'],
    'Illuminate\Auth\Events\Login' => ['App\Listeners\LoginSuccess']
];

We inform Laravel that, when the event Illuminate\Auth\Events\Login is fired, he should call App\Listeners\LoginSuccess i.e. file /app/Listeners/LoginSuccess.php.

The example here above adds a listener for the login event and the code is located in folder /app/Listeners/LoginSuccess.php.

2.2.3. Create the listener

To create such file, there is an Artisan command:

php artisan make:listener LoginSuccess

Tip: we can immediately specify the name of the event to observe

php artisan make:listener LoginSuccess --event=Illuminate\Auth\Events\Login

The /app/Listeners/LoginSuccess.php file will be generated and will contains:

<?php

namespace App\Listeners;

class LoginSuccess
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
    }

    /**
     * Handle the event.
     *
     * @param  object $event
     * @return void
     */
    public function handle($event)
    {
    }
}

To do something when an event is raised, we just need to add our code to the handle() function. Here, since our event is attached to the Login feature, we can better typecast the parameter; not just $event but it’s a login:

use Illuminate\Auth\Events\Login;

public function handle(Login $login)
{
    Session::flash('message', 'Hi ' . $login->user->name . ', nice to see you again');
}

The idea is just to retrieve the username and store in the message Session variable a Hi Christophe, nice to see you again sentence (or anything else).

2.2.4. Display our message

When the login has been made, Laravel will, by default, display the home view i.e. /resources/views/home.blade.php so, if we want to display the message, we’ll add this directive in the file:

@if(Session::has('message'))
  <p class="alert alert-success">{{ Session::get('message') }}</p>
@endif

2.2.5. Test Login events

Just go to your http://127.0.0.1:8000/login page.

login

Fill in your credentials and you should see

message

2.2.6. Code improvement

The code that is proposed here above has a problem: if we’ve a second, a third, ... listener where a message is stored in the message Session, the last fired listener will crush what the others have done.

The first listener will have:

Session::flash('message', 'Hi ' . $login->user->name . ', nice to see you again');

And a second:

Session::flash('message', 'Don\'t forget to buy some milk ?');

So the message will be Don’t forget to buy some milk ? and no more Hi Christophe.

Writing event-driven-software implies that you never know how many listeners you’ve.

Refactored, we’ll have:

$arr = Session::get('message');
$arr[] = [
    'type' => 'success',
    'message' => 'Hi ' . $login->user->name . ', nice to see you again'
];
Session::flash('message', $arr);
$arr = Session::get('message');
$arr[] = [
    'type' => 'warning',
    'message' => 'Don\'t forget to buy some milk ?'
];
Session::flash('message', $arr);

And our display:

@section('content')

<?php

  if (Session::has('message')) {
    $alert = '<div class="alert alert-%s alert-dismissible ' .
      'fade show" role="alert">%s' .
      '<button type="button" class="close" data-dismiss="alert" ' .
      'aria-label="Close"><span aria-hidden="true">&times;</span> ' .
      '</button></div>';

    foreach (Session::get('message') as $msg) {
        echo sprintf($alert, $msg['type'], $msg['message']);
    }
  }

?>

[...]

@endsection

The output will then display as many information we’ve in our Session: