Laravel 11

Cours Laravel 11 – les événements

Il existe différentes approches de programmation. La plus simple et lisible consiste généralement à adopter une approche séquentielle. Cela signifie qu’une suite d’instructions, modulables par des conditions, est exécutée de manière prédéfinie et linéaire. Ce flux séquentiel est facile à suivre et à comprendre.

Une autre approche importante est le paradigme événementiel. Au lieu de suivre un chemin linéaire, l’application réagit à des événements qui peuvent se produire. Ce type de programmation permet de séparer la détection des événements de leur gestion, créant ainsi une architecture claire et organisée.

Laravel est un framework PHP qui simplifie et facilite la gestion des événements, tout en offrant une structure solide pour créer des applications web performantes. Dans ce chapitre, nous allons découvrir comment Laravel permet de gérer efficacement les événements, en explorant les concepts, les outils et les meilleures pratiques associés à cette approche de programmation. Nous aborderons également des exemples pratiques pour vous aider à comprendre comment implémenter des événements dans vos propres applications Laravel.

Le design pattern Observateur

Le design pattern Observateur (observer) établit une relation de type 1:n entre des objets.  Si l’objet côté 1 change d’état il en informe les objets côté n pour qu’ils puissent agir en conséquence. Ça implique une intendance et les objets doivent pouvoir s’inscrire et se mettre à l’écoute des événements du sujet.

Voici une visualisation de ce design pattern :

On a un sujet et des observateurs. Les observateurs doivent s’inscrire (on dit aussi s’abonner) auprès du sujet. Lorsque le sujet change d’état il notifie tous ses abonnés. Un observateur peut aussi se désinscrire, il ne recevra alors plus les notifications.

Vous pouvez trouver une description détaillée de ce design pattern ici.

Organisation du code

Laravel implémente ce design pattern et le rend très simple d’utilisation. On va avoir :

  • des classes Event placées dans le dossier app/Events :

  • des classes Listener placées dans le dossier app/Listeners :

Ces dossiers n’existent pas dans l’installation de base, ils sont ajoutés dès qu’on crée la première classe avec Artisan qui dispose de commandes pour le faire :

Les événements déjà présents dans Laravel

Il n’y a pas que les événements que vous allez créer. Vous pouvez utiliser les événements existants déjà dans le framework qui en propose au niveau de l’authentification :

  • Registered
  • CurrentDeviceLogout
  • OtherDeviceLogout
  • Attempting
  • Authenticated
  • Login
  • Failed
  • Logout
  • Lockout
  • PasswordReset
  • Validated
  • Verified

On trouve aussi des événements avec Eloquent :

  • retrieved
  • creating
  • created
  • updating
  • updated
  • saving
  • saved
  • deleting
  • deleted
  • restoring
  • restored

Pour tous ces cas vous n’avez donc pas à vous inquiéter du déclencheur qui existe déjà.

Une application de test

Pour nos essais, créez une nouvelle application Laravel 11 :

composer create-project laravel/laravel laravel11

Créez une base, renseignez correctement le fichier .env. Installez Breeze (avec blade) et lancez les migrations :

composer require laravel/breeze --dev
php artisan breeze:install
php artisan migrate

Créez un utilisateur à partir du formulaire d’enregistrement :

On crée une écoute

On va utiliser l’événement intégré à Laravel qui se déclenche lorsqu’un utilisateur se connecte. La classe correspondante est Illuminate\Auth\Events\Login. Puisque l’événement existe, on n’a pas à créer une classe Event. Par contre, il nous faut une écoute (Listener) :

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

On a bien précisé l’événement déclencheur.

La classe UserLogin se crée ainsi que le dossier :

Voici le code généré :

<?php

namespace App\Listeners;

use Illuminate\Auth\Events\Login;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class UserLogin
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(Login $event): void
    {
        //
    }
}

Donc maintenant, dès qu’un utilisateur va se connecter, la méthode handle de notre écoute va être appelée. Mais qu’y a-t-il dans la variable $event ? On va faire un petit test :

public function handle(object $event): void
{
    dd($event);
}

Là, dès qu’on se connecte, on obtient :

Illuminate\Auth\Events\Login {#399 ▼
  +guard: "web"
  +user: App\Models\User {#1370 ▶}
  +remember: false
}

Une instance de la classe Illuminate/Auth/Events/Login avec plusieurs propriétés, en particulier une instance du modèle de l’utilisateur et un booléen pour le remember. On aurait aussi pu trouver ces informations en allant voir directement la classe.

Changez ainsi le code :

public function handle(object $event): void
{
    dd($event->user->name . " s'est connecté.");
}

Cette fois, à la connexion, on obtient quelque chose dans ce genre :

"Durand s'est connecté."

On peut donc ici effectuer tous les traitements qu’on veut, comme comptabiliser les connexions.

On crée un événement

Maintenant, on va créer un événement et aussi son écoute. On va imaginer qu’on veut savoir quand quelqu’un affiche la page d’accueil et mémoriser son IP ainsi que le moment du chargement de la page, en gros un peu de statistique.

On va commencer par créer une migration :

php artisan make:migration create_visits_table --create=visits

Avec ces créations de colonnes :

public function up(): void
{
    Schema::create('visits', function (Blueprint $table) {
        $table->id();
        $table->string('ip', 45);
        $table->timestamp('created_at');
    });
}

Et on lance la migration :

php artisan migrate

On crée l’événement :

php artisan make:event Accueil

On le trouve ici avec création du dossier :

Avec ce code par défaut :

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class Accueil
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('channel-name'),
        ];
    }
}

On ne va pas toucher à ce code.

On crée aussi l’écoute :

php artisan make:listener Accueil --event=Accueil

Avec ce code :

<?php

namespace App\Listeners;

use App\Events\Accueil;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class Accueil
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(Accueil $event): void
    {
        //
    }
}

Comme on a un conflit de nommage, on va arranger ça et compléter :

<?php

namespace App\Listeners;

use App\Events\Accueil as AcceuilEvent;;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class Accueil
{
    /**
     * Create the event listener.
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     */
    public function handle(AcceuilEvent $event): void
    {
        DB::table('visits')->insert([
            'ip' => request()->ip(),
            'created_at' => Carbon::now(),
        ]);
    }
}

J’utilise directement le Query Builder sans passer par Eloquent.

Il ne reste plus qu’à déclencher cet événement dans la route :

use App\Events\Accueil;

Route::get('/', function () {
    event(new Accueil);
    return view('welcome');
});

Maintenant, chaque fois que la page d’accueil est ouverte, l’événement Accueil est déclenché et donc l’écoute Accueil en est informée. On sauvegarde alors dans la base l’IP et le moment du chargement :

Si le traitement de vos événements est long (envoie d’email, requête…) il est conseillé d’utiliser un système de file d’attente. Vous pouvez consulter la documentation correspondante.

En résumé

  • Laravel permet d’utiliser la programmation événementielle.
  • Laravel comporte des événements préexistants au niveau de l’authentification et d’Eloquent.
  • On peut créer des événements (Events) et des écoutes (Listeners) et les mettre en relation automatiquement.
Print Friendly, PDF & Email

Laisser un commentaire