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?
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?
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;
}
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
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;
}
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']]];
}
}
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:
In a controller:
return $this->redirect('user.page');
In a form submit:
$form_state->setRedirect('<front>');
When Drupal starts, in a KernelEvents::REQUEST
subscriber:
public function checkForRedirection(RequestEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse('http://example.com/'));
}
}
For the complete code see the change record https://www.drupal.org/node/2013014
When Drupal finishes, in a KernelEvents::RESPONSE
subscriber. See for example
How to redirect after node insert (not form redirect)?
Or set the redirect response in a KernelEvents::EXCEPTION
subscriber if you want to
redirect on an exception like access denied or not found. See
How do I redirect in a class sub-classing the EventSubscriber class?
Last but not least, consider a cacheable redirect response. See for example Redirection cached? and Disabling Page Cache for Redirect Response.
Or, if nothing else works, you can override any response Drupal returns in a middleware. See this great answer https://drupal.stackexchange.com/a/303653/47547 (use this only for uncacheable requests like submitting forms, otherwise the overridden response might be delivered in the next request from cache, without that your code has a chance to set the middleware)
return $this->redirect('module.route_name', ['method'=>'nojs','page_number'=>'1', 'category'=>1406]);
Commented
Oct 29 at 19:17
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
\Drupal::url('route.name')
with your url or perhaps absolute url.
Commented
Dec 3, 2014 at 6:03
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();
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!
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'],
]);
$catch = TRUE): Response {
should work.