Oct 30 2011

Symfony2: how redirect user to a previous page correctly

Category: Articles,Symfony2FractalizeR @ 6:09 pm

I was implementing simple menu, that would allow user to change site language.  The site uses standard symfony2 localized urls like http://www.example.com/en/contact. The problem was to return user to a page, where he previously was, but rendered in a selected language.

Let me show you my solution. I’ve written kernel event listener and I registered it at my bundle’s Resources/config/services.xml file:

<?xml version="1.0" ?>
 
<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
        <service id="twig.extension.getlangarray.expedio_simple" public="false">
            <tag name="twig.extension" />
            <argument type="service" id="service_container" />
        </service>
        <service id="acme.demo.listener">
              <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
              <argument type="service" id="router" />
          </service>
    </services>
</container>

This file is merged into Symfony2 configuration from DependencyInjection/ExpedioSimpleExtension.php:

class ExpedioSimpleExtension extends Extension {
    /**
     * {@inheritDoc}
     * @param array $configs
     * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
     */
    public function load(array $configs, ContainerBuilder $container) {
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
        $loader->load('services.xml');
    }
 
    public function getAlias() {
        return 'expedio_simple';
    }
}

Listener implementation:

namespace Expedio\SimpleBundle\Listener;
 
    use Symfony\Component\DependencyInjection\ContainerInterface;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 
    class Kernel {
 
        /**
         * @var \Symfony\Component\DependencyInjection\ContainerInterface
         */
        private $router;
 
        public function __construct(\Symfony\Component\Routing\Router $router) {
            $this->router = $router;
        }
 
        public function onKernelRequest(GetResponseEvent $event) {
            if ($event->getRequestType() !== \Symfony\Component\HttpKernel\HttpKernel::MASTER_REQUEST) {
                return;
            }
 
            /** @var \Symfony\Component\HttpFoundation\Request $request  */
            $request = $event->getRequest();
            /** @var \Symfony\Component\HttpFoundation\Session $session  */
            $session = $request->getSession();
 
            $routeParams = $this->router->match($request->getPathInfo());
            $routeName = $routeParams['_route'];
            if ($routeName[0] == '_') {
                return;
            }
            unset($routeParams['_route']);
            $routeData = array('name' => $routeName, 'params' => $routeParams);
 
            //Skipping duplicates
            $thisRoute = $session->get('this_route', array());
            if ($thisRoute == $routeData) {
                return;
            }
            $session->set('last_route', $thisRoute);
            $session->set('this_route', $routeData);
        }
    }

It just saves last request route data each time user opens a page. And in controller when user wants to change locale I do this:

/**
     * @Route("/setlocale/{locale}", name="set_locale")
     * @param string $locale
     * @return array
     */
    public function setLocaleAction($locale) {
        /** @var \Symfony\Component\HttpFoundation\Session $session  */
        $session = $this->get('session');
        $session->setLocale($locale);
        $last_route = $session->get('last_route', array('name' => 'index'));
        $last_route['params']['_locale'] = $locale;
        return ($this->redirect($this->generateUrl($last_route['name'], $last_route['params'])));
    }

Well, this is how it works ;)