vinnylinux
vinnylinux

Reputation: 7024

Symfony2: Redirecting to last route and flash a message?

I'm having a small problem when trying to flash a message and redirect the user back to the previous page in Symfony 2.

I have a very simple CRUD. When new, or edit, i want to flash a message if something goes wrong in the respective create/update methods:

  1. User --GET--> new
  2. new --POST--> create (fails)
  3. --REDIRECT--> new (with flash message)

I'm doing the following:

  $this->container->get('session')->setFlash('error', 'myerror');
  $referer = $this->getRequest()->headers->get('referer');   
  return new RedirectResponse($referer);

However, it's not redirecting to the correct referrer! Even though the value of referrer is correct (eg.: http://localhost/demo/2/edit/) It redirects to the index. Why?

Upvotes: 47

Views: 102737

Answers (8)

Kisz Na
Kisz Na

Reputation: 482

For symfony 3.0,flash message with redirection back to previous page,this can be done in controller.

           $request->getSession()
                ->getFlashBag()
                ->add('notice', 'success');
            $referer = $request->headers->get('referer');
            return $this->redirect($referer);

Upvotes: 24

ex3v
ex3v

Reputation: 3566

Here you go, declare this as a service and it will return referer to you wherever and whenever you need it. No traits, no weird dependencies.

class Referer
{

    /** @var RequestStack */
    private $requestStack;

    /** @var RouterInterface */
    private $router;

    public function __construct(RequestStack $requestStack, RouterInterface $router)
    {
        $this->requestStack = $requestStack;
        $this->router       = $router;
    }

    public function getReferer() : string
    {
        $request = $this->requestStack->getMasterRequest();

        if (null === $request)
        {
            return '';
        }

        //if you're happy with URI (and most times you are), just return it
        $uri = (string)$request->headers->get('referer');

        //but if you want to return route, here you go
        try
        {
            $routeMatch = $this->router->match($uri);
        }
        catch (ResourceNotFoundException $e)
        {
            return '';
        }

        $route = $routeMatch['_route'];

        return $route;
    }

}

Upvotes: 1

moldcraft
moldcraft

Reputation: 448

I have similar functionality on my site. It is multilingual. Articles exists only in a single locale. When the user will try to switch to other locale, it should redirect back to previous page and flash message that that page/article doesn't exist on the requested locale.

/en/article/3 -> /fr/article/3 (404) -> Redirect(/en/article/3)

Here is my version of the script that works well on dev and prod environments:

$referer = $request->headers->get('referer')

// 'https://your-domain.com' or 'https://your-domain.com/app_dev.php'
$base = $request->getSchemeAndHttpHost() . $request->getBaseUrl();

// '/en/article/3'
$path = preg_replace('/^'. preg_quote($base, '/') .'/', '', $referer);

if ($path === $referer) {
    // nothing was replaced. referer is an external site
} elseif ($path === $request->getPathInfo()) {
    // current page and referer are the same (prevent redirect loop)
} else {
    try {
        // if this will throw an exception then the route doesn't exist
        $this->container->get('router')->match(
            // '/en/hello?foo=bar' -> '/en/hello'
            preg_replace('/\?.*$/', '', $path)
        );

        // '/app_dev.php' . '/en/article/3'
        $redirectUrl = $request->getBaseUrl() . $path;

        return new RedirectResponse($redirectUrl);
    } catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {}
}

Upvotes: 4

Santi
Santi

Reputation: 509

The message from Naitsirch presented in the next url: https://github.com/symfony/symfony/issues/2951

Seems a good solution for that you need:

public function getRefererRoute()
{
    $request = $this->getRequest();

    //look for the referer route
    $referer = $request->headers->get('referer');
    $lastPath = substr($referer, strpos($referer, $request->getBaseUrl()));
    $lastPath = str_replace($request->getBaseUrl(), '', $lastPath);

    $matcher = $this->get('router')->getMatcher();
    $parameters = $matcher->match($lastPath);
    $route = $parameters['_route'];

    return $route;
}

Then with a redirect:

public function yourFunctionAction()
{
    $ruta = $this->getRefererRoute();

    $locale = $request->get('_locale');
    $url = $this->get('router')->generate($ruta, array('_locale' => $locale));

    $this->getRequest()->getSession()->setFlash('notice', "your_message");   

    return $this->redirect($url);
}

Upvotes: 12

Victor Odiah
Victor Odiah

Reputation: 1081

This works for me:

$this->redirect($request->server->get('HTTP_REFERER'));

Upvotes: 10

Flip
Flip

Reputation: 4908

This is an alternative version of Naitsirch and Santi their code. I realized a trait would be perfect for this functionality. Also optimized the code somewhat. I preferred to give back all the parameters including the slugs, because you might need those when generating the route.

This code runs on PHP 5.4.0 and up. You can use the trait for multiple controllers of course. If you put the trait in a seperate file make sure you name it the same as the trait, following PSR-0.

<?php
trait Referer {
    private function getRefererParams() {
        $request = $this->getRequest();
        $referer = $request->headers->get('referer');
        $baseUrl = $request->getBaseUrl();
        $lastPath = substr($referer, strpos($referer, $baseUrl) + strlen($baseUrl));
        return $this->get('router')->getMatcher()->match($lastPath);
    }
}

class MyController extends Controller {
    use Referer;

    public function MyAction() {
        $params = $this->getRefererParams();

        return $this->redirect($this->generateUrl(
            $params['_route'],
            [
                'slug' => $params['slug']
            ]
        ));
    }
}

Upvotes: 41

Joseph Hightower
Joseph Hightower

Reputation: 1

seems like you need to have a payload for your redirect to point to. it seems like obscure concept code to me. I would also advise you to make sure your configuration files point to the correct redirect code snippet. Check your server access file to make sure it has redirects enabled also.

Upvotes: 0

Thomas Kelley
Thomas Kelley

Reputation: 10302

I just set up a simple app, and it seems to work fine. My createAction() looks like this:

public function createAction()
{
    $entity  = new Pokemon();
    $request = $this->getRequest();
    $form    = $this->createForm(new PokemonType(), $entity);
    $form->bindRequest($request);

    if ($entity->getName() == "pikachu")
    {
        $this->container->get("session")->setFlash("error", "Pikachu is not allowed");
        $url = $this->getRequest()->headers->get("referer");
        return new RedirectResponse($url);
    }

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getEntityManager();
        $em->persist($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('pokemon_show', array('id' => $entity->getId())));

    }

    return $this->render('BulbasaurBundle:Pokemon:new.html.twig', array(
        'entity' => $entity,
        'form'   => $form->createView()
    ));
}

The flow goes:

  • User navigates to /new
  • User enters invalid option of "pikachu"
  • User clicks submit (POSTs to /create)
  • Application rejects the entry, adds flash message, and redirects back to /new
  • User sees /new with the flash message

A few things to check:

  • Is your route for /demo/{entityId}/edit actually working? (i.e. if you enter it in the browser, does it actually go to where you expect it to?)

  • Are you chaining together different redirects/forwards? I've noticed that I get unexpected (but correct) behavior when I have a controller that redirects to a URL, and the controller responsible for that URL also redirects somewhere else. I've fixed this issue by using forwards instead.

That said, if all else fails, you could just use the controller's redirect() method to manage the redirect:

public function createAction()
{
    ...
    return $this->redirect($this->generateUrl("pokemon_new"));
    ...
}

Upvotes: 3

Related Questions