20

In Drupal 7, I use the following code.

function my_goto($path) { 
  drupal_goto($path, array(), 301);
}

What code should I use in Drupal 8?

1
  • 3
    drupal.org/node/2023537 is the change record for drupal_goto(), whenever you are looking for a replacement for a specific function, look there first.
    – Berdir
    Commented Dec 3, 2014 at 8:24

9 Answers 9

35

This is the code that should be used in Drupal 8. See change Record for more info.

use Symfony\Component\HttpFoundation\RedirectResponse;

function my_goto($path) { 
  $response = new RedirectResponse($path);
  $response->send();
  return;
}
1
32
+50

First (but read on for the correct, better aproach) it's important to note that Drupal 9.2 broke the simple way of doing it:

$response = new RedirectResponse($path);
$response->send();

You have to use the full code from the drupal_goto() changelog, namely:

$response = new RedirectResponse($url->toString());
$request = \Drupal::request();
// Save the session so things like messages get saved.
$request->getSession()->save();
$response->prepare($request);
// Make sure to trigger kernel events.
\Drupal::service('kernel')->terminate($request, $response);
$response->send();

Still, this solution is not OK in many cases. One prominent case is using it in hooks like eg. many modules that provide redirection after user login. Redirecting inside a hook means that the chain of hooks gets broken, there will be other hooks that don't get a chance to run after yours. This is wrong. The proper way to do it has been different for some time.

You create a middleware service that can be used for the purpose. Put this into src/Middleware/Redirect.php:

namespace Drupal\yourmodule\Middleware;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

class Redirect implements HttpKernelInterface {
  protected $httpKernel;
  protected $redirectResponse;

  public function __construct(HttpKernelInterface $http_kernel) {
    $this->httpKernel = $http_kernel;
  }

  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
    $response = $this->httpKernel->handle($request, $type, $catch);
    return $this->redirectResponse ?: $response;
  }

  public function setRedirectResponse(?RedirectResponse $redirectResponse) {
    $this->redirectResponse = $redirectResponse;
  }

}

refer to it in your services.yml:

services:
  http_middleware.yourmodule_redirect:
    class: Drupal\yourmodule\Middleware\Redirect
    tags:
      - { name: http_middleware }

and use it like any other service:

$url = Url::fromRoute('<front>', [], ['absolute' => 'true']);
$response = new RedirectResponse($url->toString());
\Drupal::service('http_middleware.yourmodule_redirect')->setRedirectResponse($response);

Yes, slightly more code, although the service can be reused any time, of course, calling it is simple enough. But this solution will not immediately halt execution, it will note your redirect request, do the proper thing and redirect on the next turn. A solution like that might get added to the core itself because there are places that would benefit from it, but until then, use your own, doing it correctly.

Credit goes to: https://www.drupal.org/project/redirect_after_login/issues/3214949

3
  • Just a note that if you do your redirect in Rules, Rules takes care of all the code for you so you don't have to worry about writing and maintaining your own service, you don't have to worry about redirecting too early and causing hooks not to run, or any of that other stuff. The redirect can be configured in the Rules UI without writing any code. And the best part is that the way Rules does things didn't break in 9.2 - it works on all versions of Drupal.
    – anonymous
    Commented Jul 11, 2021 at 0:46
  • 6
    Could well be. However, most of us who ask and answer on this site are programmers who are more interested in solutions in code rather than mousing around in the UI. :-) If the question of how to do something in your own code comes up, installing a complete module instead is not the ususal solution we look for.
    – Gábor
    Commented Jul 12, 2021 at 7:32
  • That's why I wrote a comment, not an answer.
    – anonymous
    Commented May 28, 2022 at 17:43
9

To build on Anu Mathew's response;

To add a status code, its just the second param in the RedirectResponse class;

use Symfony\Component\HttpFoundation\RedirectResponse;

function my_goto($path) { 
  $response = new RedirectResponse($path, 302);
  $response->send();
  return;
}
2
  • 1
    For me this only seems to work once, and has to have to cache cleared everytime you want it to work again. Is there a way around that?
    – Amy
    Commented Feb 6, 2017 at 22:51
  • @Matt See drupal.stackexchange.com/a/222700/14151 Commented Jul 23, 2018 at 16:10
9

This can be achieved by leveraging built-in symphonies EventDispatcher Component. All you have to do is create a custom module. Add your services.yml file and provide appropriate service config.

services:
  mymodue.subscriber:
    class: Drupal\my_module\EventSubscriber
    tags:
      - { name: event_subscriber }

in Your modules src directory add the EventSubscriber.php class and describe you methods here.

<?php
namespace Drupal\my_module;

use Drupal\Core\Url;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class EventSubscriber implements EventSubscriberInterface {

  public function checkForCustomRedirect(RequestEvent $event) {    
    $request = $event->getRequest(); 
    $route_name = $request->attributes->get('_route');
    if($route_name === 'module.testPage') {
      $url = Url::fromRoute('<front>')->toString();
      $event->setResponse(new RedirectResponse($url));
    }
  }
      
  /**
  * {@inheritdoc}
  */
  public static function getSubscribedEvents() {
    return [KernelEvents::REQUEST => [['checkForCustomRedirect']]];
  }
}
1
  • $url is not defined. Commented Dec 27, 2019 at 18:59
9

Never use $response->send() under any circumstances. This results in unpredictable behavior and might fail in different environments, when moving from dev to prod, for example, or in future updates, like it did for Drupal 9.2. (Edit: Recent example Site stuck in 'headers have already been sent' login loop after D9 upgrade)

The correct method depends on where you want to use it:

2
  • Just to clarify, from a controller $this->redirect() needs to go to an internal Drupal route. You can pass arguments in the second parameter as an associative array. Commented Feb 7, 2022 at 20:06
  • @wheelercreek, for external URLs use a TrustedRedirectResponse. See drupal.stackexchange.com/questions/233948/…
    – 4uk4
    Commented Feb 7, 2022 at 20:29
6

I didn't work in drupal 8 yet but as per the documentation drupal_goto is removed from Drupal 8.

In place of drupal_goto you need to write:

return new RedirectResponse(\Drupal::url('route.name'));

and something like this with parameters :

return new RedirectResponse(\Drupal::url('route.name', [], ['absolute' => TRUE]));

Check here https://www.drupal.org/node/2023537 and class RedirectResponse

15
  • 1
    Thanks for your reply.But how can i get the route name from the current url(Because url is setting using configuration form)
    – Anu Mathew
    Commented Dec 3, 2014 at 5:48
  • 1
    You mean redirect path is dynamic??? Commented Dec 3, 2014 at 5:52
  • 1
    Yes, your right its dynamic..
    – Anu Mathew
    Commented Dec 3, 2014 at 5:55
  • 1
    Ok try to replace \Drupal::url('route.name') with your url or perhaps absolute url. Commented Dec 3, 2014 at 6:03
  • 1
    return new RedirectResponse($absolute_url); is worked for me :) But its showing "redirect to example.com/absolute_url " message on the screen
    – Anu Mathew
    Commented Dec 3, 2014 at 6:15
2

this works for internal or external redirection:

use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Url;

   $url = Url::fromUri('internal:/node/27'); // choose a path
   // $url =  Url::fromUri('https://external_site.com/');
    $destination = $url->toString();
    $response = new RedirectResponse($destination, 301);
    $response->send();
1

Perfectly working redirect code for me is the following:

$response = new RedirectResponse($path);
return $response->send();

In any other cases I'm getting some kind of exceptions or errors, for example: LogicException: The controller must return a response...

OR

https://www.drupal.org/project/drupal/issues/2852657

There is already a discussion about it, hope that helps!

1

Gàbor's answer with middleware implementation is perfect, thanks to him !

But I encountered the following error, introduced by PHP 7.4 properties type-hinting :

Error: Typed property Drupal\your_module\Middleware\Redirect::$redirectResponse must not be accessed before initialization.

I first tried to add RedirectResponse as an argument to the service and initialize it in the __construct, but this resulted much simpler : declaring $redirectResponse as nullable with default null :

protected ?RedirectResponse $redirectResponse = null;

Full middleware below, credits again to Gàbor.

<?php
namespace Drupal\mail_forward_management\Middleware;

use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;

class Redirect implements HttpKernelInterface {

  protected HttpKernelInterface $httpKernel;
  protected ?RedirectResponse $redirectResponse = null;

  public function __construct(HttpKernelInterface $httpKernel) {
    $this->httpKernel = $httpKernel;
  }

  /**
   * @inheritDoc
   */
  public function handle(
    Request $request,
    $type = self::MASTER_REQUEST,
    $catch = TRUE
  ) {
    $response = $this->httpKernel->handle($request, $type, $catch);
    return $this->redirectResponse ?: $response;
  }

  protected function setRedirectResponse(?RedirectResponse $redirectResponse) {
    $this->redirectResponse = $redirectResponse;
  }

  /**
   * Creates a new Url object for a URL that has a Drupal route,
   * and redirects to it.
   *
   * See \Drupal\Core\Url::fromRoute() for params details.
   *
   * @param string $route
   *    A Drupal route.
   * @param array $route_params
   *    (optional) An associative array of route parameter names and values.
   * @param array $opts
   *    (optional) An associative array of options.
   */
  public function redirectToRoute(string $route, array $route_params, array $opts ) {
    $destination = URL::fromRoute($route, $route_params, $opts);
    $response = new RedirectResponse($destination->toString());
    $this->setRedirectResponse($response);
  }

  /**
   * Creates a new Url object from a URI,
   * and redirects to it.
   *
   * See \Drupal\Core\Url::fromUri() for params details.
   *
   * @param string $uri
   *    The URI of the resource including the scheme.
   * @param array $opts
   *    (optional) An associative array of additional URL options.
   */
  public function redirectToUri(string $uri, array $opts ) {
    $destination = URL::fromUri($uri, $opts);
    $response = new RedirectResponse($destination->toString());
    $this->setRedirectResponse($response);
  }

}

some_module.services.yml

services:
  http_middleware.some_module_redirect:
    class: Drupal\some_module\Middleware\Redirect
    tags:
      - { name: http_middleware }

reusable service call

 Drupal::service('http_middleware.some_module_redirect')
   ->redirectToRoute('user.page', [], ['absolute' => 'true']);
 
 // OR
 Drupal::service('http_middleware.some_module_redirect')
   ->redirectToUri('https://example.com/foo', [
      'query' => ['bar' => 'baz'],
    ]);
1
  • Pls note that this isn't compatible anymore where it inherits the handle function. Declaration with $catch = TRUE): Response { should work.
    – c1u31355
    Commented Dec 2, 2023 at 10:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.