benoliver999
benoliver999

Reputation: 165

How to create exception event listener for a specific controller action, to redirect to another action using its original argument?

I have an action which takes an argument, the route looks like this:

/cs/{id}

It's the individualAction in the Cassette controller.

Sometimes, there's an exception 500 Internal Server Error - NoResultException - this is expected behaviour.

I am looking to redirect to another controller action, editAction, when this happens, the route is as such:

/cs/{id}/edit

It needs to be controller-specific, since I want to repeat this with different controller actions. It also needs to keep the argument from the original action.

I've been looking into event listeners, but I'm not sure if that's overkill and I'm struggling to find out how to make them action-specific - I'm happy to keep the code in the controller if that's the better solution.

Upvotes: 4

Views: 1571

Answers (1)

chalasr
chalasr

Reputation: 13167

An EventListener is the most appropriated to change the comportment of your app when an exception is thrown.

Use something like :

<?php

namespace AppBundle\EventListener;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\Routing\RouterInterface;
use Doctrine\ORM\NoResultException;

class ExceptionResponseListener
{
    public function __construct(RouterInterface $router)
    {
        $this->router = $router;
    }

    /**
     * @param GetResponseForExceptionEvent $event
     */
    public function onKernelResponse(GetResponseForExceptionEvent $event)
    {
        $request = $event->getRequest();
        $routeName = $request->get('_route');
        $exception = $event->getException();

        // Restrict the route by adding a check on the route name
        // Or a pattern of route names (!strpos($routeName, '_show'))
        // Or for many routes: (!in_array($routeName, ['xxx_show', ...]
        // Or the _controller: $request->get('_controller')
        // Output something like: "AcmeBundle\\Controller\\DefaultController::showAction"
        if ('expected_route_name' !== $routeName) {
            return;
        }    

        // Retrieve your param
        $params = $request->get('_route_params');

        if ($exception instanceof NoResultException) {
            // Create a redirection to the edit route
            $response = new RedirectResponse(
                $this->router->generate('your_edit_route', array('id' => $params['id']))
            );

            $event->setResponse($response);
        }
    }
}

And register it as service :

services:
    # ...
    acme.kernel.listener.exception_listener:
        class: AppBundle\EventListener\ExceptionResponseListener
        tags:
            - {name: kernel.event_listener, event: kernel.exception, method: onKernelResponse}
        arguments: ['@router']

Another simple alternative could be to make a forward/redirection of the expected action directly in your method, instead of throw the exception.

Upvotes: 1

Related Questions