Qu’est-ce qu’un événement
Le système d’événements de Laravel implémente le pattern observateur.
Vous déclenchez des « événements » représentant des faits survenus dans l’application, et des listeners y réagissent, ce qui minimise les dépendances entre composants.
Par exemple, un événement « commande validée » peut déclencher indépendamment « envoyer l’e-mail de confirmation », « décrémenter le stock » et « notifier Slack ».
Le code de la commande ignore complètement l’implémentation de l’envoi de mails ou de Slack.
Les classes d’événement vont dans app/Events, les listeners dans app/Listeners.
Ces dossiers sont créés automatiquement par les commandes Artisan.
Générer événements et listeners
Utilisez make:event et make:listener.
php artisan make:event UserRegistered
php artisan make:listener SendWelcomeEmail --event=UserRegistered
Sans arguments, la commande est interactive.
php artisan make:event
php artisan make:listener
Enregistrement des listeners
Auto-discovery
Par défaut, Laravel scanne app/Listeners et enregistre les listeners.
La classe/méthode handle ou __invoke est inspectée : le type de son argument détermine l’événement écouté.
use App\Events\UserRegistered;
class SendWelcomeEmail
{
public function handle(UserRegistered $event): void
{
// Envoi de l'e-mail de bienvenue
}
}
Un type union PHP permet de gérer plusieurs événements dans la même méthode.
public function handle(UserRegistered|UserUpdated $event): void
{
// ...
}
Pour des listeners hors de ce dossier, ajoutez des chemins de scan dans bootstrap/app.php.
->withEvents(discover: [
__DIR__.'/../app/Domain/Orders/Listeners',
])
Wildcards autorisés :
->withEvents(discover: [
__DIR__.'/../app/Domain/*/Listeners',
])
Liste des listeners enregistrés :
En production, mettez le manifest en cache : php artisan optimize ou php artisan event:cache. Videz-le avec php artisan event:clear.
Enregistrement manuel
Via Event dans AppServiceProvider::boot.
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(
UserRegistered::class,
SendWelcomeEmail::class,
);
}
Ou via closure.
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(function (UserRegistered $event) {
// ...
});
}
Définir un événement
Une classe d’événement n’est qu’un conteneur de données. Elle porte les informations liées à l’événement, sans logique.
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
Le trait SerializesModels garantit une bonne sérialisation des modèles Eloquent quand le listener est en file.
Déclencher un événement
Utilisez la méthode statique dispatch ou le helper event().
use App\Events\UserRegistered;
// Statique
UserRegistered::dispatch($user);
// Helper
event(new UserRegistered($user));
Il existe aussi les variantes conditionnelles.
UserRegistered::dispatchIf($condition, $user);
UserRegistered::dispatchUnless($condition, $user);
Après la commit d’une transaction
Pour ne dispatcher qu’après commit d’une transaction, implémentez ShouldDispatchAfterCommit. En cas de rollback, l’événement est ignoré.
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered implements ShouldDispatchAfterCommit
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
Implémenter un listener
La méthode handle reçoit l’événement.
Le constructeur peut recevoir des dépendances via le conteneur.
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail
{
public function __construct() {}
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeMail($event->user));
}
}
Retourner false depuis handle interrompt la propagation aux listeners suivants.
Listeners en file (queued)
Pour les traitements longs (mail, HTTP…), exécutez le listener en asynchrone.
Implémentez ShouldQueue : Laravel enfile automatiquement le listener lors du dispatch.
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// Exécuté en asynchrone par le worker
}
}
Nécessite la configuration de la queue et le démarrage d’un worker. Voir Queues et jobs.
Personnaliser connexion, queue et délai
Utilisez les attributs PHP.
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Delay;
use Illuminate\Queue\Attributes\Queue;
#[Connection('redis')]
#[Queue('emails')]
#[Delay(10)]
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// ...
}
}
Ou via des méthodes dynamiques.
public function viaConnection(): string
{
return 'redis';
}
public function viaQueue(): string
{
return 'emails';
}
public function withDelay(UserRegistered $event): int
{
return 10;
}
Nombre max d’essais et timeout
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;
#[Tries(3)]
#[Timeout(30)]
class SendWelcomeEmail implements ShouldQueue
{
// ...
}
Gestion des échecs
La méthode failed est appelée après épuisement des tentatives.
use Throwable;
public function failed(UserRegistered $event, Throwable $exception): void
{
// Notifier un administrateur, par exemple
}
Souscripteurs d’événements
Un souscripteur regroupe plusieurs handlers dans une même classe.
Créer un souscripteur
subscribe renvoie la table événement → handler.
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
class UserActivitySubscriber
{
public function handleLogin(Login $event): void
{
// Login
}
public function handleLogout(Logout $event): void
{
// Logout
}
/**
* @return array<string, string>
*/
public function subscribe(Dispatcher $events): array
{
return [
Login::class => 'handleLogin',
Logout::class => 'handleLogout',
];
}
}
Enregistrement
Avec l’auto-discovery, les souscripteurs retournant un tableau sont enregistrés automatiquement.
Sinon, dans AppServiceProvider::boot, appelez Event::subscribe.
use App\Listeners\UserActivitySubscriber;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::subscribe(UserActivitySubscriber::class);
}
Exemple : e-mail de bienvenue à l’inscription
Créer l'événement
php artisan make:event UserRegistered
Ajoutez une propriété pour l’utilisateur.<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public User $user,
) {}
}
Créer le listener
php artisan make:listener SendWelcomeEmail --event=UserRegistered
Implémentez ShouldQueue pour un envoi asynchrone.<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeMail($event->user));
}
}
Dispatcher l'événement depuis le contrôleur
Après la création de l’utilisateur.<?php
namespace App\Http\Controllers\Auth;
use App\Events\UserRegistered;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class RegisterController extends Controller
{
public function store(Request $request): RedirectResponse
{
$user = User::create($request->validated());
UserRegistered::dispatch($user);
return redirect('/dashboard');
}
}
RegisterController ignore l’implémentation de l’envoi de mail.
Ajouter plus tard un « notifier Slack » ne modifie pas le contrôleur. Démarrer un worker
Pour traiter les listeners en file.
Avec l’auto-discovery, aucun enregistrement manuel n’est requis dans AppServiceProvider.
Vérifiez régulièrement php artisan event:list pour détecter d’éventuels listeners inattendus.