Jonathan Scheiber

EasyAdmin bundle : message flash après sauvegarde

Temps de lecture : 3 minutes 25 commentaires
EasyAdmin bundle : message flash après sauvegarde

Par défaut, en créant du contenu, EasyAdmin n'affiche aucun message après la création, la modification ou la suppression d'une entité. Voyons comment faire en sorte d'en avoir :)

Vous pouvez voir un exemple dans l'image de présentation de l'article.

Le but de cet article va être d'automatiser ces messages flash avec le minimum de code possible.

Quelques explications avant de commencer

Le code a été testé avec PHP 8.0, Symfony 5.2 et EasyAdmin bundle 3.2.8. Il se base sur les faits suivants :

Le code

<?php

declare(strict_types=1);

namespace App\Listener;

use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityDeletedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityPersistedEvent;
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityUpdatedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Translation\TranslatableMessage;

final class EasyAdminListener implements EventSubscriberInterface
{
    public function __construct(private SessionInterface $session)
    {
    }

    public static function getSubscribedEvents(): array
    {
        return [
            AfterEntityPersistedEvent::class => ['flashMessageAfterPersist'],
            AfterEntityUpdatedEvent::class => ['flashMessageAfterUpdate'],
            AfterEntityDeletedEvent::class => ['flashMessageAfterDelete'],
        ];
    }

    public function flashMessageAfterPersist(AfterEntityPersistedEvent $event): void
    {
        $this->session->getFlashBag()->add('success', new TranslatableMessage('content_admin.flash_message.create', [
            '%name%' => (string) $event->getEntityInstance(),
        ], 'admin'));
    }

    public function flashMessageAfterUpdate(AfterEntityUpdatedEvent $event): void
    {
        $this->session->getFlashBag()->add('success', new TranslatableMessage('content_admin.flash_message.update', [
            '%name%' => (string) $event->getEntityInstance(),
        ], 'admin'));
    }

    public function flashMessageAfterDelete(AfterEntityDeletedEvent $event): void
    {
        $this->session->getFlashBag()->add('success', new TranslatableMessage('content_admin.flash_message.delete', [
            '%name%' => (string) $event->getEntityInstance(),
        ], 'admin'));
    }
}
# Traductions, placées dans translations/admin.fr.yaml
content_admin:
    flash_message:
        create: Le contenu "%name%" a été créé avec succès.
        update: Le contenu "%name%" a été mis à jour avec succès.
        delete: Le contenu "%name%" a été supprimé avec succès.

Comment ça fonctionne ?

Le listener va écouter sur les trois événements d'EasyAdmin prévenant qu'une entité Doctrine a été créée, mise à jour ou supprimée. Chacun de ces événements a accès à l'entité impactée via la méthode getEntityInstance() disponible sur l'événement.

A partir de là, un message flash est enregistré en session, en passant en paramètre du message flash la valeur renvoyée par la méthode __toString() de l'entité.

A partir du moment où l'autoconfigure est activé dans votre projet, c'est fini ! Vous avez vos beaux messages flash qui s'affichent quand vous sauvegardez une entité.

Et voilà, c'est terminé ! :) Si vous avez des questions, remarques, suggestions ... N'hésitez pas à poster un commentaire ci-dessous :)

Commentaires

Posté par Robin Bastiaan le 25/04/2021 à 13:32.
Elegant solution I will use for sure. Thanks for sharing!
Posté par wietsedc le 26/04/2021 à 09:57.
thanks! details like this make the user experience so much better, I implemented it straight away.
Posté par ced le 08/05/2021 à 16:12.
Bonjour,

J'ai une erreur concernant e constructeur
public function __construct(private SessionInterface $session)
{
}

private me donne une erreur
Posté par jmsche le 09/05/2021 à 18:02.
Bonjour ced, si tu pouvais donner l'erreur ça pourrait être intéressant pour t'aider :)
Posté par Robin Bastiaan le 10/05/2021 à 10:08.
@ced, please let me help you although I only speak English. I suspect you are not running PHP version 8. A new way of writing a constructor was introduced in PHP8 and is called "constructor property promotion". Please try the following code instead:

private SessionInterface $session;

public function __construct(SessionInterface $session)
{
$this->session = $session;
}
Posté par ced le 24/05/2021 à 23:17.
Merci @Robin Bastiaan pour ton indication. J'utilise un switch checkbox et l'ors de la modification sur on ou off je ne reçois pas de message flash car je ne change pas de page.Comment faire pour résoudre ce problème et affiché directement le message?
Posté par DooM le 27/05/2021 à 14:00.
I tried this with php 7.4.9, used the tip from @Robin Bastiaan and ended with this error:
Expected to find class "App\EventSubscriber\EasyAdminListener" in file "$$$$\src\EventSubscriber\EasyAdminListener.php" while importing services from resource "../src/", but it was not found! Check the namespace prefix used with the resource in $$$$\src\../config/services.yaml (which is being imported from "$$$$\src\Kernel.php").
I'm kind of beginner with symfony5
Posté par jmsche le 27/05/2021 à 14:07.
Hi DooM, could you please create a Gist with the code of your listener? Also, make sure the namespace+classname match the filepath (case sensitive).
Posté par DooM le 27/05/2021 à 14:56.
Hi jmsche, it's just the same as your. I managed to "solve" the path problem, no more error, but still no flash message displayed. I guess i should take more time reading doc...
Posté par jmsche le 27/05/2021 à 14:56.
Feel free to contact me on Symfony's Slack by DM :)
Posté par Paul ALBERT le 09/08/2021 à 16:46.
Hello @Jonathan Scheiber et @ced !

Déjà @Jonathan Scheiber merci pour ce petit snippet qui fait gagner du temps.

Et @ced, j'ai eu à me pencher sur le même problème que toi et ai, il me semble, trouvé une solution :
Il semblerait qu'un attribut fieldName désignant le champ à mettre à jour apparaisse dans la query string lors d'un appel AJAX fait par un bouton switch depuis le listing.

J'ai donc modifié le code comme ceci et cela semble fonctionner (j'espère que ça sera assez lisible dans les commentaires) :

(for english speakers, here is a complementary snippet to prevent the flash message on the next page loading when a switch button is clicked from a listing page)

public static function getSubscribedEvents()
{
return [
AfterEntityPersistedEvent::class => ['flashMessageAfterPersist'],
AfterEntityUpdatedEvent::class => ['flashMessageAfterUpdate'],
AfterEntityDeletedEvent::class => ['flashMessageAfterDelete'],
AfterCrudActionEvent::class => ['ignoreFollowingFlashMsgIfXhrUpdate'],
];
}

public function ignoreFollowingFlashMsgIfXhrUpdate($e)
{
if (!is_null($e->getAdminContext()->getRequest()->query->get('fieldName'))) {
$e->getAdminContext()->getRequest()->getSession()->set('ignoreFollowingFlashMsg', true);
}
}

...

public function flashMessageAfterUpdate(AfterEntityUpdatedEvent $event): void
{
if ($this->session->get('ignoreFollowingFlashMsg')) {
$this->session->set('ignoreFollowingFlashMsg', false);
$this->session->getFlashBag()->add('success', new TranslatableMessage('flash_messages.update', [
'%name%' => (string) $event->getEntityInstance(),
], 'admin'));
}
}
Posté par Nedvajz le 09/09/2021 à 08:43.
Elegant solution. Thank you !
Posté par Hicham le 06/01/2022 à 21:59.
How can we handle erros based on this method ? i'm trying to something like that

try {

//Guzzle http request

}catch (\Exception $exception){
$this->session->getFlashBag()->add('error', new TranslatableMessage('content_admin.flash_message.create', [
'%name%' => $exception->getMessage(),
], 'admin'));
}
Posté par ToshY le 25/01/2022 à 23:21.
I'm using Symfony 6, PHP 8.1 and EasyAdmin 4.0.2 and I just get the following error message:

Cannot autowire service "App\EventSubscriber\EasyAdminListener": argument "$session" of method "__construct()" references interface "Symfony\Component\HttpFoundation\Session\SessionInterface" but no such service exists. Did you create a class that implements this interface?

Anyone got the solution?
Posté par jmsche le 26/01/2022 à 08:01.
Hi ToshY, you can inject RequestStack instead of SessionInterface & get the session from there.
Posté par ToshY le 26/01/2022 à 19:58.
@jmsche Thanks for the quick response. Works like a charm :)
Posté par Tony le 02/02/2022 à 14:27.
Bonjour @jmsche, le TranslatableMessage ne renvoit pas le message traduit mais seulement l'objet TranslatableMessage. J'obtiens l'erreur

Symfony\Bridge\Twig\Extension\TranslationExtension::trans(): Argument #1 ($message) must be of type Stringable|Symfony\Contracts\Translation\TranslatableInterface|string|null, array given
Posté par Tony le 02/02/2022 à 14:29.
Erreur de ma part, le fichier flash_messages.html.twig avait besoin de correction
Posté par jmsche le 02/02/2022 à 14:30.
Heureux que tu aies pu régler ton problème :)

Pour information, le code ci-dessus fonctionne a priori sans modification avec Symfony 6 et EasyAdmin 4.
Posté par Danilo le 05/07/2022 à 07:00.
In Symfony 6, SessionInterface has been removed.
You have to change your code. Import Symfony\Component\HttpFoundation\RequestStack and change __costructor() method like this:
-----
public function __construct(private RequestStack $requestStack)
{
}
-----
then, instead of calling $this->session() use $this->requestStack->getSession()

HTH
Danilo
Posté par STELMACH le 03/08/2023 à 12:22.
Merci beaucoup pour partager cette solution, Jonathan.

Si vous utilisez le Symfony 6, il faut faire l'autowiring de RequestStack au lieu de SessionInterface :

public function __construct(private RequestStack $requestStack)
{
}

Dans les fonctions, il faut appeler le getter getSession() avant le getFlashBag() :

public function flashMessageAfterPersist(AfterEntityPersistedEvent $event): void
{

$this->requestStack->getSession()->getFlashBag()->add('success', new
TranslatableMessage('content_admin.flash_message.create', [
'%name%' => (string) $event->getEntityInstance(),
], 'admin'));

}
Posté par ZoThyx le 06/10/2023 à 09:13.
Hello, merci pour a solution que tu proposes.
Le seul problème que j'ai avec, est que les FlashMessage du delete ne fonctionne pas, mais seulement celle là.
Je suis sous PHP 8.1.2, Symfony 6.3.1 et EasyAdmin 4.7.0.
Posté par ZoThyx le 06/10/2023 à 09:30.
Je viens de m'appercevoir que c'est parce que j'ai custom l'action du delete de toutes mes entités :/
Si je trouve une solution et que quelqu'un la veut, envoyez un message
Posté par theTimmy le 01/07/2024 à 18:20.
Worked like a charm with Symfony 6.4 & php 8.2 and getting the session with RequestStack - thx!
Posté par Solomon le 15/07/2024 à 09:01.
In Symfony 7, SessionInterface is excluded from Autowiring. So, you need to update few things
services:
session:
class: Symfony\Component\HttpFoundation\Session\SessionInterface
factory: ['@session.factory', 'createSession']
App\EventListener\EasyAdminListener:
arguments:
- '@session'

Ajouter un commentaire