Votre contrôleur Symfony est devenu une usine à gaz ? Après l’inscription d’un utilisateur, vous devez envoyer un email, générer une notification, mettre à jour des stats… et tout ça au même endroit ? Vous sentez que votre code devient difficile à lire et à maintenir ?
La solution est d’utiliser le système d’événements de Symfony. Ce tutoriel complet vous montre étape par étape comment créer un Event Listener pour avoir un code découplé, propre et facile à faire évoluer.
Qu’est-ce qu’un Event Listener et Pourquoi est-ce Essentiel en Symfony ?
Imaginez un interrupteur. Quand vous appuyez dessus (l’événement), la lumière s’allume (l’action). Un Event Listener dans une application Symfony, c’est exactement ça. Le principe est simple : « quand X se produit, fais Y ». X peut être n’importe quoi : un utilisateur s’inscrit, une commande est payée, un article est publié.
Utiliser ce système change la vie d’un développeur. Au lieu de tout entasser dans un contrôleur, vous séparez chaque logique dans sa propre classe. C’est une bonne pratique qui suit le Single Responsibility Principle : chaque classe fait une seule chose, mais elle la fait bien. Le but est d’avoir un code propre et bien rangé.
Les avantages concrets sont nombreux :
- Découplage : Votre contrôleur se contente de dire « Hey, un nouvel utilisateur est là ! ». Il ne se préoccupe pas de savoir s’il faut envoyer un email ou une notification. Chaque partie de l’application est indépendante.
- Extensibilité : Demain, vous voulez aussi envoyer un SMS de bienvenue ? Pas de problème. Il suffit de créer un nouveau listener, sans jamais toucher au code du contrôleur qui existe déjà.
- Organisation : Chaque logique est isolée. Un fichier pour l’email de bienvenue, un autre pour la notification admin, etc. C’est beaucoup plus simple de s’y retrouver.
- Réutilisabilité : Le même événement, comme la création d’un utilisateur, peut être déclenché depuis le formulaire d’inscription, une commande d’import ou une interface d’admin. Tous les listeners associés s’exécuteront automatiquement.
Tutoriel : Créer votre Premier Événement et Listener (Étape par Étape)
On passe à la pratique. On va prendre un cas simple que tout le monde connaît : envoyer un email de bienvenue quand un nouvel utilisateur s’inscrit sur le site. C’est le cas pratique parfait pour débuter.
Prérequis
Pour suivre ce tutoriel, vous avez simplement besoin d’un projet Symfony fonctionnel. N’importe quelle version récente (6 ou 7) fera l’affaire.
Étape 1 : Créer la Classe d’Événement (Event)
La première chose à faire est de créer l’événement lui-même. C’est une simple classe PHP, un objet qui ne sert qu’à une chose : transporter des informations. Dans notre cas, il transportera l’objet `User` qui vient d’être créé.
Créez un fichier `UserRegisteredEvent.php` dans le dossier `src/Event/` de votre application.
<?php
// src/Event/UserRegisteredEvent.php
namespace App\Event;
use App\Entity\User;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegisteredEvent extends Event
{
// Nom public de l'événement pour l'identifier
public const NAME = 'user.registered';
private User $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function getUser(): User
{
return $this->user;
}
}
Étape 2 : Déclencher l’Événement (Dispatch)
Maintenant que l’événement existe, il faut le « déclencher » au bon moment. Ce moment, c’est dans votre contrôleur, juste après avoir sauvegardé le nouvel utilisateur en base de données.
Pour ça, on utilise le service `EventDispatcherInterface` de Symfony. On l’injecte simplement dans la méthode du contrôleur. Ensuite, on appelle sa méthode `dispatch()` pour envoyer le signal à toute l’application.
<?php
// src/Controller/RegistrationController.php
// ... autres use statements
use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class RegistrationController extends AbstractController
{
// ...
public function register(Request $request, /* ... autres services */, EventDispatcherInterface $eventDispatcher): Response
{
// ... (création du formulaire, validation, etc.)
if ($form->isSubmitted() && $form->isValid()) {
// ... (hash du mot de passe, etc.)
$entityManager->persist($user);
$entityManager->flush();
// C'est ici qu'on déclenche l'événement !
$event = new UserRegisteredEvent($user);
$eventDispatcher->dispatch($event, UserRegisteredEvent::NAME);
// ... (redirection, message flash, etc.)
}
// ...
}
}
À ce stade, l’événement est déclenché, mais il ne se passe rien. C’est normal. Personne n’écoute encore ce signal. C’est ce qu’on va faire maintenant.
Étape 3 : Créer l’Écouteur (Listener)
L’écouteur (le listener) est la classe qui contient la logique à exécuter quand l’événement est déclenché. Dans notre cas, c’est la logique pour envoyer un email de bienvenue.
Avec les versions récentes de Symfony, c’est très simple grâce à l’attribut PHP 8 `#[AsEventListener]`. Il suffit de l’ajouter au-dessus de la classe pour que Symfony comprenne tout seul ce qu’il doit faire. On va créer un listener et utiliser la méthode magique `__invoke`, qui est appelée automatiquement.
Créez le fichier `WelcomeEmailListener.php` dans le dossier `src/EventListener/`.
<?php
// src/EventListener/WelcomeEmailListener.php
namespace App\EventListener;
use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
#[AsEventListener(event: UserRegisteredEvent::NAME, method: '__invoke')]
final class WelcomeEmailListener
{
private MailerInterface $mailer;
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
public function __invoke(UserRegisteredEvent $event): void
{
$user = $event->getUser();
$email = (new Email())
->from('welcome@monsite.com')
->to($user->getEmail())
->subject('Bienvenue sur notre site !')
->text('Merci de votre inscription.');
$this->mailer->send($email);
}
}
Et c’est tout ! Maintenant, à chaque fois qu’un utilisateur s’inscrira, le contrôleur déclenchera l’événement `user.registered`, et Symfony appellera automatiquement votre `WelcomeEmailListener` pour envoyer l’email. Votre contrôleur reste propre et simple.
Event Listener vs Event Subscriber : Lequel Choisir ?
En cherchant des informations sur les événements Symfony, vous avez sûrement entendu parler de l’Event Subscriber. C’est une alternative au Listener. La différence est simple, mais importante à comprendre pour choisir le bon outil.
| Caractéristique | Event Listener | Event Subscriber |
|---|---|---|
| Cas d’usage | Une classe pour une seule action sur un seul événement. | Une classe qui regroupe plusieurs logiques sur plusieurs événements différents. |
| Configuration | Simple avec l’attribut `#[AsEventListener]`. | Implémente `EventSubscriberInterface` et la méthode `getSubscribedEvents()`. |
| Nombre d’événements | Un seul par classe. | Autant que nécessaire dans une seule classe. |
| Simplicité | Très simple. Idéal pour une logique isolée. | Un peu plus complexe, mais très puissant pour organiser le code. |
- Utilisez un Event Listener quand une classe doit réagir à un seul événement. C’est le cas le plus courant et le plus direct.
- Utilisez un Event Subscriber quand une classe doit réagir à plusieurs événements. Par exemple, une classe `UserActivitySubscriber` qui écoute `user.registered`, `user.logged_in` et `user.profile_updated` pour mettre à jour des logs.
Un Subscriber doit implémenter l’interface `EventSubscriberInterface` et une méthode `public static function getSubscribedEvents()`. Cette méthode retourne un tableau qui dit à Symfony « quand tel événement se produit, appelle telle méthode de ma classe ».
Aller plus loin : Les Événements du Noyau (Kernel Events)
Créer vos propres événements, c’est bien. Mais la vraie puissance du système, c’est que Symfony lui-même déclenche des dizaines d’événements tout au long du traitement d’une requête. On les appelle les `KernelEvents`. Ils vous permettent de vous brancher au cœur du cycle de vie d’une page.
C’est très utile pour des tâches globales qui doivent s’appliquer à toute votre application.
Voici quelques exemples courants :
- `kernel.request` : Déclenché tout au début de la requête. Parfait pour vérifier des droits d’accès, charger des données pour l’utilisateur connecté ou changer la langue du site.
- `kernel.exception` : Déclenché quand une erreur non gérée survient. Idéal pour créer un système de log d’erreurs personnalisé ou pour afficher une page d’erreur en JSON pour une API.
- `kernel.response` : Déclenché juste avant que la réponse soit envoyée au navigateur. Pratique pour ajouter des en-têtes HTTP (headers) à toutes vos pages.
Imaginons que vous voulez être notifié par email à chaque fois qu’une erreur 500 se produit. Vous pouvez créer un listener sur l’événement `kernel.exception`.
<?php
// src/EventListener/ExceptionListener.php
namespace App\EventListener;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
#[AsEventListener(event: KernelEvents::EXCEPTION, method: 'onKernelException')]
final class ExceptionListener
{
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
// Ici, votre logique :
// - Envoyer un email à l'administrateur
// - Logger l'erreur dans un service externe comme Sentry
// - etc.
}
}
Pour des besoins plus avancés, vous pouvez consulter la liste complète des Kernel Events dans la documentation officielle de Symfony.
FAQ – Tout sur les Event Listeners Symfony
Quelle est la différence principale entre un Listener et un Subscriber ?
C’est simple : un Listener gère un seul événement par classe. Un Subscriber peut en gérer plusieurs dans la même classe. Le Subscriber est fait pour regrouper des logiques qui vont ensemble.
Comment définir la priorité d’un listener ?
Il est possible de définir une priorité si plusieurs listeners écoutent le même événement. Dans l’attribut, il suffit d’ajouter le paramètre `priority`. Par exemple : `#[AsEventListener(event: ‘user.registered’, priority: 10)]`. Plus le nombre est élevé, plus le listener est exécuté tôt.
Est-il possible d’arrêter la propagation d’un événement ?
Oui. L’objet Event passé à votre listener a une méthode `stopPropagation()`. Si vous l’appelez, Symfony arrêtera d’exécuter les autres listeners qui devaient suivre (ceux avec une priorité plus faible). C’est utile si un listener a complètement traité l’événement et que les autres ne doivent plus rien faire.
Comment passer des données à mon listener ?
Les données sont passées via l’objet Event lui-même. C’est pour ça qu’on a créé la classe `UserRegisteredEvent`. On lui a ajouté une propriété `$user` dans son constructeur (`public function __construct`) et une méthode `getUser()` pour que le listener puisse récupérer l’information dont il a besoin.
