Symfony redirecting from outside the controller

Symfony redirecting using a kernel exception listener...

Usually, when you want to redirect your users to a different URL, you use the redirect() or redirectToRoute() methods from the controller, but sometimes you need to redirect your users from outside of the controller, if certain conditions are met.

Unfortunately this cannot be done out of the box, but it's fairly easy to do.

I personally prefer to do this using exceptions and a kernel exception listener because they can bubble up and be caught by an event listener.

In Symfony, when you throw an exception, it gets caught internally by a try/catch which then creates an instance of Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent and passes it along to each of the registered kernel exception listeners until one of them returns a response.

With that in mind, we start by defining a simple RedirectException class that will carry an instance of Symfony\Component\HttpFoundation\RedirectResponse from any service to the listener that will then return the response to the user:

<?php

namespace AppBundle\Exception;

use Symfony\Component\HttpFoundation\RedirectResponse;

class RedirectException extends \Exception
{
    private $redirectResponse;

    public function __construct(
        RedirectResponse $redirectResponse,
        $message = '',
        $code = 0,
        \Exception $previousException = null
    ) {
        $this->redirectResponse = $redirectResponse;
        parent::__construct($message, $code, $previousException);
    }

    public function getRedirectResponse()
    {
        return $this->redirectResponse;
    }
}

The listener that will handle the redirecting is really simple, all it has to do, is to check that the exception carried by the Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent is an instance of RedirectException and if that's true, we call setResponse() with the redirect response.

<?php

namespace AppBundle\EventListener;

use AppBundle\Exception\RedirectException;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;

class RedirectExceptionListener
{
    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        if ($event->getException() instanceof RedirectException) {
            $event->setResponse($event->getException()->getRedirectResponse());
        }
    }
}

The config file for this listener looks something like this:

services:
    app.redirect_exception_listener:
        class: AppBundle\EventListener\RedirectExceptionListener
        tags:
            - { name: kernel.event_listener, event: kernel.exception }

In order for us to actually use this now, we only have to throw the exception from any place during a request with something like this:

throw new \AppBundle\Exception\RedirectException(
    new \Symfony\Component\HttpFoundation\RedirectResponse(
        $event->getRequest()->query->get('goTo')
    )
);

Happy coding!

Want more awesome stuff?

Check us out on our Medium Remote Symfony Team publication.