Jonathan Scheiber

EasyAdmin bundle: flash message after save

Read time: 3 minutes 25 comments
EasyAdmin bundle: flash message after save

By default, when creating content, EasyAdmin displays no message after creating, updating or removing entities. Let's see how to manage that :)

You can see an example in the article overview image.

The goal of this article will be to automate these flash messages with as little code as possible.

Some explanations before starting

The code has been tested with PHP 8.0, Symfony 5.2 and EasyAdmin bundle 3.2.8. It is based on the following facts:

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'));
    }
}
# Translations, put in translations/admin.en.yaml
content_admin:
    flash_message:
        create: Content "%name%" has been created.
        update: Content "%name%" has been updated.
        delete: Content "%name%" has been removed.

How does it work?

The listener will listen to the three EasyAdmin events warning that a Doctrine entity has been created, updated or deleted. Each of these events has access to the impacted entity via the getEntityInstance() method available on the event.

From there, a flash message is saved in session, passing as a parameter of the flash message the value returned by the __toString() method of the entity.

As soon as autoconfigure is activated in your project, it's over! You have your beautiful flash messages that appear when you save an entity.

Et voilà! :) If you have any questions, comments, suggestions... Feel free to post a comment below :)

Comments

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

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

private me donne une erreur
Posted by jmsche on 09/05/2021 at 18:02.
Bonjour ced, si tu pouvais donner l'erreur ça pourrait être intéressant pour t'aider :)
Posted by Robin Bastiaan on 10/05/2021 at 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;
}
Posted by ced on 24/05/2021 at 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?
Posted by DooM on 27/05/2021 at 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
Posted by jmsche on 27/05/2021 at 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).
Posted by DooM on 27/05/2021 at 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...
Posted by jmsche on 27/05/2021 at 14:56.
Feel free to contact me on Symfony's Slack by DM :)
Posted by Paul ALBERT on 09/08/2021 at 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'));
}
}
Posted by Nedvajz on 09/09/2021 at 08:43.
Elegant solution. Thank you !
Posted by Hicham on 06/01/2022 at 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'));
}
Posted by ToshY on 25/01/2022 at 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?
Posted by jmsche on 26/01/2022 at 08:01.
Hi ToshY, you can inject RequestStack instead of SessionInterface & get the session from there.
Posted by ToshY on 26/01/2022 at 19:58.
@jmsche Thanks for the quick response. Works like a charm :)
Posted by Tony on 02/02/2022 at 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
Posted by Tony on 02/02/2022 at 14:29.
Erreur de ma part, le fichier flash_messages.html.twig avait besoin de correction
Posted by jmsche on 02/02/2022 at 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.
Posted by Danilo on 05/07/2022 at 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
Posted by STELMACH on 03/08/2023 at 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'));

}
Posted by ZoThyx on 06/10/2023 at 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.
Posted by ZoThyx on 06/10/2023 at 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
Posted by theTimmy on 01/07/2024 at 18:20.
Worked like a charm with Symfony 6.4 & php 8.2 and getting the session with RequestStack - thx!
Posted by Solomon on 15/07/2024 at 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'

Add a comment