How to Redirect A Page
Redirect needs were always important when developing with C, we remember how we use the Goto() command, and with Drupal 7 we had the Drupalgoto() command, but these options were deprecated within Drupal9 which become a fully object-oriented and object-derived system.
so how do we create Redirection commands?
There are a few options to do so, I will try to overview them all.
When Drupal starts a page request it registers a few methods that are to be called as the page response is closed down. This includes session management methods, but can also include a cron handler (if cron has been configured like that).
Redirecting In Controllers
Redirecting in a controller is perhaps the easiest thing to do as you just need to return a new Symfony\Component\HttpFoundation\RedirectResponse object.
The redirect object accepts a string pointing to a page, and this can be created easily using a Drupal\Core\Url object.
use Symfony\Component\HttpFoundation\RedirectResponse;
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);
return new RedirectResponse($url->toString());
Drupal will see this object type is returned from the controller action and redirect the page accordingly.
This redirect takes place after it has finished processing the upstream code.
class TrustedRedirectResponse
Provides a redirect response that contains trusted URLs.
Use this class in case you know that you want to redirect to an external URL.
use Drupal\Core\Routing\TrustedRedirectResponse;
/**
* Handles a page request for our forced login route.
*/
public function forceLogin() {
$query_params = $this->requestStack->getCurrentRequest()->query->all();
$cas_login_url = $this->casHelper->getServerLoginUrl($query_params);
return TrustedRedirectResponse::create($cas_login_url, 302);
}
Redirecting In Forms
If you want to redirect a form submission when you need to use the setRedirect() method on the form state object during the form submission handler. This method takes the route you want to redirect to and can be used like this.
$form_state->setRedirect('entity.node.canonical', ['node' => 1]);
Alternatively, you can use the setRedirectUrl() method of the form state, which takes in a Drupal\Core\Url object as the argument.
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);
$form_state->setRedirectUrl($url);
The only caveat to this method is that you must not also use the setRebuild() method on the form state as this will cause the redirect to not be processed.
The above examples assume that you are writing a form class, but what if you wanted to perform a redirect on form you don't have control over? By using a hook_form_alter() hook we can inject a form submission handler into the form state and then add the redirection code to that handler.
Here is the hook_form_alter() hook.
function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if ($form_id === 'node_article_form') {
$form['actions']['submit']['#submit'][] = 'mymodule_article_form_submit';
}
}
The submission handler would look something like this.
function mymodule_article_form_submit($form, FormStateInterface $form_state) {
$form_state->setRedirect('entity.node.canonical', ['node' => 1]);
}
In my opinion, this is how the original offending code should have been written. By adding a form alter hook to the form and injecting a custom submission handler the redirect can be easily added to the form state, which can then be processed by the form submission handler. The redirect would then take place and it wouldn't have had to battle with any other redirects that Drupal had created.
Redirecting In Events
If you are in an Event then the approach is slightly different again. Here, you need to inject the RedirectResponse into the Event object using the setResponse() method. The following code will redirect if a condition has been met.
class MyEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
$events['kernel.request'] = ['someEvent', 28];
return $events;
}
private function someEvent(KernelEvent $event) {
if (/* some condition */) {
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);
$response = new RedirectResponse($url->toString());
$event->setResponse($response);
}
}
}
The creation of the RedirectResponse object is precisely the same as usual. In this case, Drupal will redirect once the event has been processed.
Ensure you include your redirect in a condition (especially when responding to the kernel.
request event) as this would otherwise lead to an infinite redirection loop.
Be warned that other events can also be triggered here and might override or change the response.
In this case, you need to change your event subscriber weight to ensure it occurs last in the event processing.
Redirecting In Hooks
It is also possible (although not recommended) to perform a redirect in a hook. It is best practice to generate a Url object from a route (rather than to hard code the node path into the redirect) as this will allow for the path to change in the future.
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
function mymodule_node_insert(EntityInterface $entity) {
if ($entity->getType() == 'article') {
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);
$redirect = new RedirectResponse($url->toString());
$redirect->send();
}
}
It is generally a bad idea to redirect within a hook. You should be either looking at redirecting in a form submission handler or subscribing to an event and redirecting from there.
It's interesting to note that Redirect module, which is designed around redirection, uses an event handler to create and return the redirect within an event object. Although the module contains a few hooks, none of them deal with the redirection of content.
I have used the Drupal\Core\Url class a lot in this article. If you want to know more about the Url class then you can read an article about it that goes into detail on how to generate URLs and links in Drupal.
As a final note. Don't, whatever you do, add functions like die() or exit() to your Drupal code or you will break Drupal in interesting ways. Drupal needs to finish running the code for the page correctly and adding functions like this will cause Drupal to break in interesting ways.
Redirect with storage option
If you want to store redirects in the database use the module Redirect.
This module provides a user interface to add redirects.
The redirects are stored in a content entity, which you can also create programmatically:
use Drupal\redirect\Entity\Redirect;
Redirect::create([
'redirect_source' => 'redirects/redirect1',
'redirect_redirect' => 'internal:/node/1',
'language' => 'und',
'status_code' => '301',
])->save();
Redirect within hook_cron with Redirect module
/**
* run cron
*/
function MODULE_cron(){
$nids = \Drupal::entityQuery('node')
->condition('type', 'article', '=')
->condition('status', 1)
->execute();
foreach ($nids as $nid) {
$alias = \Drupal::service('path.alias_manager')->getAliasByPath('/node/'.$nid);
$aliasOriginal = str_replace("/blog","blog",$alias);
$aliasOriginal = str_replace("/web/drupal","",$aliasOriginal);
Redirect::create([
'redirect_source' => $aliasOriginal,
'redirect_redirect' => "internal:/node/".$nid,
'status_code' => 301,
])->save();
}
}
Provides the ability to create manual redirects and maintain a canonical URL for all content, redirecting all other requests to that path.
Features
- Common API for loading, saving, and deleting redirects.
- Case-insensitive redirect matching with a hook to allow other modules to narrow down the candidate redirects.
- Redirect counter and last used timestamp, with automatic cleanup of inactive redirects. (Provided by Redirect Metrics in D8)
- Integration with Drupal's page cache to optimize redirects and performance.
- Complete individual redirects access API.
- Views API integration.
- D8: Maintaining a canonical path and redirecting alternative URLs like non-aliased paths, paths without language prefixes, and so on (Previously provided by Global redirect)
- D8: Separate Redirect 404 module that logs aggregated 404 requests (can suppress them in the default log) and allows to create redirects from them
- D8: Separate Redirect Domain module allows wildcard and domain redirects
Redirect within htaccess
#custom redirects
RewriteRule ^old/URL/path$ http://example.com/new/path [R=301,L]
#end custom redirects
Redirect when visiting a page
create file: Middleware.php and locate it under the folder:src
namespace Drupal\module_name;
use Symfony\Component\HttpFoundation\Request;
//use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Drupal\drupalvip_sentry\ManagerInterface;
/**
* Provides a HTTP middle-ware to implement IP based banning.
*/
class Middleware implements HttpKernelInterface {
/**
* The decorated kernel.
*
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
protected $httpKernel;
/**
* Constructs a AdvbanMiddleware object.
*
* @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
* The decorated kernel.
* @param \Drupal\drupalvip_sentry\ManagerInterface $manager
* The ban IP manager.
*/
public function __construct(HttpKernelInterface $http_kernel) {
$this->httpKernel = $http_kernel;
}
/**
* {@inheritdoc}
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
$ip = $request->getClientIp();
$isBanned = true;
if ($isBanned) {
$redirect = "https://www.fbi.gov/investigate/cyber";
return new RedirectResponse($redirect);
}
return $this->httpKernel->handle($request, $type, $catch);
}
}
create file: module_name.services.yml
services:
module_name.middleware:
class: Drupal\module_name\Middleware
tags:
# Ensure to come before page caching, so you don't serve cached pages to
# banned users.
- { name: http_middleware, priority: 250 }