User redirection is often required on a website, and Drupal-based websites are no different. For instance, after successful authentication, users are taken to their account profile page.
In Drupal 7, drupal_goto()
is the API function for this. It accepts an optional internal $path
argument to redirect to. If none is provided, it redirects to the same page. This was later discouraged and $path
should always be provided. Two things to note. Firstly, external destinations are not supported. Secondly, drupal_alter
allows a developer to have the final say through an implementation of hook_drupal_goto_alter()
.
Like a good number of well-known API functions, drupal_goto()
has been removed in Drupal 8 due to the changes in architecture. How do we do redirection now?
Creating a redirection
Drupal 8 uses a number of components from Symfony. Redirection is handled in the HttpFoundation Component by the Symfony\Component\HttpFoundation\RedirectResponse
class.
When you want to redirect a user, create and return a new RedirectResponse
object with the following constructor arguments: $url
, $status
(optional), and $headers
(optional).
URL
The URL to redirect to should be a string with the scheme or protocol, path, fragment, query string, or any other acceptable part of a Universal Resource Identifier (URI).
This may be from one of the following ways:
-
same page
- If you have access to the Request object (
$request
) and you want to redirect to the same page, you can get the URL like this:\Drupal\Core\Url::createFromRequest($request);
- If you have access to the Request object (
-
homepage
-
The static service container has a
url()
method which generates a URL from the name of a route and other parameters. In Drupal,<front>
is understood as the homepage and this method also accepts it as the route name. This will give you a well-structured URL to the homepage - \Drupal::url('<front>', [], ['absolute' => TRUE]); -
Another option is to have
$url
as an empty string.RedirectResponse
understands that as the homepage too.
-
-
route name
- As pointed out earlier, if you know the name of the route (e.g.
user.login
) you can generate the URL in the following two ways:$url = \Drupal\Core\Url::fromRoute('user.login')->setAbsolute()->toString();
$url = \Drupal::url('user.login', [], ['absolute' => TRUE]);
- As pointed out earlier, if you know the name of the route (e.g.
-
URL string
- Either an absolute (
http://www.example.com/
) or relative URL (/the-page
) is acceptable.
- Either an absolute (
Status
This is optional and the default is 302
which means "Found". RFC 2616 has this to say about this status:
The requested resource resides temporarily under a different URI.
Since the redirection might be altered on occasion, the client SHOULD
continue to use the Request-URI for future requests.
See vendor/symfony/http-foundation/Response.php
or List of HTTP status codes for further details on standard status codes.
Headers
Response headers may also be provided as an array. For example, when a request is received and it is detected that Drupal has not been installed yet, we are redirected to the installer. This is temporary so there is no need to cache it:
<?php
$headers = ['Cache-Control' => 'no-cache'];
$response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, $headers);
Usage
We have the following examples:
return new RedirectResponse(\Drupal::url('locale.translate_status', [], ['absolute' => TRUE]));
return new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE]));
return new RedirectResponse(Url::fromRoute('system.modules_uninstall')->setAbsolute()->toString());
If you know that the destination is an external URL then you are encouraged to use TrustedRedirectResponse
instead:
return new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE]));
return new TrustedRedirectResponse('http://www.bbc.com/');
There is another way to do redirection if you know the route name and you have extended ControllerBase
class. You can call the redirect()
method from your class:
return $this->redirect('user.login');
Even when your class does not have to extend the ControllerBase
class, you may still do something similar, because the redirect()
method is from the UrlGeneratorTrait
which you can also use in your classes. For example:
<?php
use Drupal\Core\Routing\UrlGeneratorTrait;
class MyClass {
use UrlGeneratorTrait;
public function doSomething() {
// ....
return $this->redirect('user.login');
}
}
As the class name suggests, your route name will first be converted to a URL, and then a RedirectResponse
is created with the generated URL. However, this trait has been deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. So, it is always advisable to generate a URL with \Drupal\Core\Url
and do the redirection yourself.
Altering a redirection
Inside Drupal 8 Events introduced the use of the EventDispatcher component in Drupal 8. This is an example of its application in a contrived scenario:
- Subscribe to the
KernelEvents::RESPONSE
event - Check if the response matches a specific
RedirectResponse
tohttp://www.example.com/user/login
- Change the target URL to
http://www.example.com/user/new-login
Here is the EventSubscriber
class:
<?php
// src/custom_redirection/EventSubscriber/CustomRedirectionSubscriber.php
namespace Drupal\custom_redirection\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class CustomRedirectionSubscriber implements EventSubscriberInterface {
public function alterRedirection(FilterResponseEvent $event) {
$response = $event->getResponse();
if ($response instanceOf RedirectResponse && $response->getTargetUrl() == 'http://www.example.com/user/login') {
$response->setTargetUrl('http://www.example.com/user/new-login');
}
}
static function getSubscribedEvents() {
$events[KernelEvents::RESPONSE][] = ['alterRedirection'];
return $events;
}
}
For Drupal to know about it, configure a service for the class above.
services:
custom_redirection.subscriber:
class: Drupal\ custom_redirection\EventSubscriber\ CustomRedirectionSubscriber
tags:
- { name: event_subscriber }
This requires more coding than just implementing hook_drupal_goto_alter()
, but it is part of the new direction Drupal 8 has followed.