![[Symfony 4.4] Créer un Event Subscriber en réponse à une connexion Thumbnail](/uploads/thumbnails/event_subscriber250p-60c1dd26d152a.png)
Memo
[Symfony 4.4] Créer un Event Subscriber en réponse à une connexion
Symfony dispose d'un système d'events avec lequel on peut intéragir. Au cours de sa vie, l'application déclenche régulièrement des évènements, nous verrons donc ici au travers d'un exemple comment mettre à jour une date de "dernière connexion" en base de données après qu'un utilisateur se soit connecté avec succès.


Auteur : Guillaume M.
Créé le : 10 Jun 2021
Dernière mise à jour : 10 Jun 2021
Event Listener et Event Subscriber
Ils existent 2 manières d'écouter les events, avec un Listener
ou avec un Subscriber
. Les 2 peuvent être utilisés dans la même application indistinctement, finalement c'est avant tout une question de goût. Mais alors, laquelle de ces 2 méthodes choisir ? Et bien voici un petit descriptif de chacun :
- Les
Subscribers
sont plus faciles à réutiliser du fait qu'unSubscriber
garde les events directement dans sa classe plutôt que dans la definition du service - Les
Listeners
sont plus fexibles car les bundles peuvent les activer/désactiver à leur guise en fonctions de certaines valeurs de configuration
Ici j'ai donc fait le choix d'utiliser le Subscriber
car plus simple d'utilisation et je n'ai pas besoin de la flexibilité d'un Listener
. Voyons maintenant comment mettre cela en place pour capter l'event relatif à une connexion utlisateur réalisée avec succès.
Choisir un event
Dans un premier temps il nous faut savoir précisément quel event nous souhaitons "capter" et comme indiqué dans la description nous cherchons à réagir lorsqu'un utilisateur se connecte avec succès. Une simple recherche dans la doc de Symfony nous indique ceci : https://symfony.com/doc/4.4/components/security/authentication.html#authentication-events.
On notera la constante d'event ainsi que l'argument passé au listener, cela sera utile lors de la création de notre classe.
Comme nous pouvons le constater il existe plusieurs types events relatifs à l'authentification et une petite lecture rapide nous indique que l'event que nous cherchons est security.interactive_login
. En effet, c'est cet event qui est dispatché lorsqu'un utlisateur se connecte de lui-même et l'event security.authentication.success
quand à lui ne nous intéresse pas car celui-ci ne fait pas de distinction entre une connexion manuelle ou automatique (via le système de session ou un provider) or ici nous souhaitons capter la connexion manuelle.
Création du Subscriber
Un Subscriber
est une classe héritant de l'interface EventSubscriberInterface
et dans laquelle nous devons écrire une fonction statique pour indiquer au système d'events à quel(s) type(s) d'event notre subscriber va-t-il "souscrire", ce qui nous donne dans sa forme la plus simple :
//src/EventSubscriber/LoginSuccessSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LoginSuccessSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [];
}
}
À noter qu'ici la classe se situe dans un dossier EventSubscriber
créé au préalable dans le dossier src
. C'est une norme de la documentation.
La fonction getSubscribedEvents()
doit renvoyer les events auxquels nous voulons souscrire (car oui nous pouvons souscrire à autant d'events que nous le souhaitons) et pour cela nous avons besoin de la constante d'un event et d'indiquer le nom de la fonction qui doit être appelé lorsque que l'event est dispatché, ce qui nous donne :
//src/EventSubscriber/LoginSuccessSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class LoginSuccessSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => "onLoginSuccess"
];
}
public function onLoginSuccess(InteractiveLoginEvent $event)
{
var_dump("Login Success !");
die();
}
}
La fonction renvoie donc maintenant un tableau associatif contenant en clé la constante de l'event et en valeur le nom de la fonction qui doit être appelée et que nous avons également ajoutée avec en paramètre l'objet passé lors de son appel. Toutes ces informations se situe dans le tableau vu précédemment.
À partir d'ici, nous pouvons vérifier que notre Subscriber est bien enregistré avec la commande :
php bin/console debug:event-dispatcher security.interactive_login
Pour afficher une liste complète des Listeners/Subscribers, taper la commande ci-dessus sans préciser le nom de l'event.
Si nous ne voyez pas votre Subscriber
dans la liste, c'est peut-être que votre application n'est pas configuré de manière automatique pour les enregistrements/injections, dans ce cas voir du côté du fichier de config services.yaml
.
Prendre soin aussi de vérifier que le nom du fichier et de la classe soit identiques.
Bien ! Si l'application dispose du système de login, nous pouvons tester notre Subscriber, il est fonctionnel !
Modifier la date de dernière connexion en base de données
Ici je suppose que l'application contient une table User
contenant une colonne last_connection
! Je ne vais pas détailler le nouveau code, il est plutôt simple à comprendre, ce n'est ni plus ni moins qu'une mise à jour en BDD en réponse à l'event :
<?php
//src/EventSubscriber/LoginSuccessSubscriber.php
namespace App\EventSubscriber;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
class LoginSuccessSubscriber implements EventSubscriberInterface
{
private $entityManager;
public function __construct(EntityManagerInterface $em)
{
$this->entityManager = $em;
}
public static function getSubscribedEvents()
{
return [
SecurityEvents::INTERACTIVE_LOGIN => "onLoginSuccess"
];
}
public function onLoginSuccess(InteractiveLoginEvent $event)
{
// Récupération de l'id utilisateur
$userId = $event->getAuthenticationToken()->getUser()->getId();
// Récupération en base de l'utilisateur concerné + mise à jour
$user = $this->entityManager->getRepository(User::class)->find($userId);
$user->setLastConnection(new \DateTime());
$this->entityManager->flush();
// Ici nous pourrions, par exemple, ajouter également une notification flash etc...
}
}
Cet exemple est vraiment simpliste mais fonctionnel, on pourrait envisager d'avoir plusieurs fonctions avec un ordre de priorité différent pour un même event ou bien ajouter d'autres events à notre subscriber etc. Pour plus d'informations sur le sujet voir la doc ;).