Skip to content

Instantly share code, notes, and snippets.

@javiereguiluz
Last active August 27, 2016 17:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save javiereguiluz/a66f084a4f2cf11cf0ee to your computer and use it in GitHub Desktop.
Save javiereguiluz/a66f084a4f2cf11cf0ee to your computer and use it in GitHub Desktop.
Discussion about the different solutions to display a flash message

The problem to solve

We want to show a flash message as the result of executing some controller. This message will only last for the next request.

Proposed Solution #1

I propose to use the new addFlash() method available in the base controller of Symfony 2.6:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class MyController extends Controller
{
    public function myAction()
    {
        // ... 3 or 4 lines of code

        $this->addFlash('success', 'The product was successfully inserted.');

        return $this->render(...);
    }
}

Requirements of the solution

  • 0 new custom classes
  • 0 new custom methods or functions (addFlash() is provided by the framework)
  • 0 new custom listeners, services or parameters

Total time required by the solution is about 3 seconds, the time needed to type $this->addFlash()

Drawbacks of the solution

Your controller code is coupled with the base controller provided with Symfony. It won't work outside a Symfony application.

Final comments

I don't mind about the coupling in this case because:

  • Controllers are a thin layer of glue-code provided by the framework. Coupling to it allows me to be more productive.
  • I advocate for agressively decoupling your business logic from the underlying framework. But at the same time, I recommend to agressively couple your controllers to the framework, to take advantage of its features. Otherwise, what is the point of using a framework?
  • Spending a lot of time decupling your controllers from the framework is not worth it. In case you change your framework, it's 100% sure that you'll have to make lots of changes in your code. Updating 4 or 5 lines of controller code will be fast and easy.

Proposed Solution #2

Proposed by Carles Climent in this comment

class MyController
{
    public function myAction()
    {
        // ... 3 or 4 lines of code

        $this->get('flash_handler')->add('success', 'The product was successfully inserted.');

        return $this->container->get('templating')->renderResponse(...);
    }
}

Requirements of the solution

First, you have to define these two classes:

interface FlashHandler
{
    public function add($severity, $message);
}
class SymfonyFlashHandler implements FlashHandler
{
    private $session;

    public function __construct(SessionInterface $session)
    {
        $this->session = $session;
    }

    public function add($severity, $message)
    {
        $this->session->getFlashBag()->add($severity, $message);

        return $this;
    }
}

Then, you have to define a new service for the flash handler:

parameters:
    flash_handler.class: [...]\FlashHandler

services:
    flash_handler:
        class:           "%flash_handler.class%"
        arguments:       [@session]

The main advantage of this solution is that Controller is no longer coupled with Symfony. It will work outside a Symfony application.

Other Proposed Solutions

Please propose other solutions to this problem and don't forget to provide the full code needed by your solution. This way we'll be able to compare them all. Thanks!

@markitosgv
Copy link

Another interesting solution is using event dispatcher and listener like FOSUserBundle:

In controller:

class ProfileController extends Controller
{
    ...

    /**
     * Edit the user
     */
    public function editAction(Request $request)
    {
        ...

        if ($form->isValid()) {
             ...

            $dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

            return $response;
        }

        ...
    }
}

In Listener (Event Subscriber):

<?php

/*
 * This file is part of the FOSUserBundle package.
 *
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FOS\UserBundle\EventListener;

use FOS\UserBundle\FOSUserEvents;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Translation\TranslatorInterface;

class FlashListener implements EventSubscriberInterface
{
    private static $successMessages = array(
        FOSUserEvents::CHANGE_PASSWORD_COMPLETED => 'change_password.flash.success',
        FOSUserEvents::GROUP_CREATE_COMPLETED => 'group.flash.created',
        FOSUserEvents::GROUP_DELETE_COMPLETED => 'group.flash.deleted',
        FOSUserEvents::GROUP_EDIT_COMPLETED => 'group.flash.updated',
        FOSUserEvents::PROFILE_EDIT_COMPLETED => 'profile.flash.updated',
        FOSUserEvents::REGISTRATION_COMPLETED => 'registration.flash.user_created',
        FOSUserEvents::RESETTING_RESET_COMPLETED => 'resetting.flash.success',
    );

    private $session;
    private $translator;

    public function __construct(Session $session, TranslatorInterface $translator)
    {
        $this->session = $session;
        $this->translator = $translator;
    }

    public static function getSubscribedEvents()
    {
        return array(
            FOSUserEvents::CHANGE_PASSWORD_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::GROUP_CREATE_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::GROUP_DELETE_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::GROUP_EDIT_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::PROFILE_EDIT_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::REGISTRATION_COMPLETED => 'addSuccessFlash',
            FOSUserEvents::RESETTING_RESET_COMPLETED => 'addSuccessFlash',
        );
    }

    public function addSuccessFlash(Event $event)
    {
        if (!isset(self::$successMessages[$event->getName()])) {
            throw new \InvalidArgumentException('This event does not correspond to a known flash message');
        }

        $this->session->getFlashBag()->add('success', $this->trans(self::$successMessages[$event->getName()]));
    }

    private function trans($message, array $params = array())
    {
        return $this->translator->trans($message, $params, 'FOSUserBundle');
    }
}

@jorgelbg
Copy link

In this case I think I'm ok with coupling with the Symfony2 framework in this case, nevertheless for decoupling I would use a custom FlashHandler as described on Solution # 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment