简体   繁体   中英

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?

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']
            ]
        ));
    }
}

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);
}

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);

这对我有用:

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

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) {}
}

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? (ie 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"));
    ...
}

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;
    }

}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM