Automatic Redirect by Path in Drupal 8
This example shows how to intercept a Drupal page request, determine if the desired path matches the one we wish to redirect, find the “latest” node of a particular content type, and redirect to that node instead of the original page.
I have a page on my site called Updates. This landing page exists only so that there is a menu item and proper breadcrumb trail; users never get to this landing page. Rather, they’re redirected to the latest update when they attempt to visit the landing page. Updates are nodes of the type update. Each update node (when viewed as a page) shows a Recent Updates block in the sidebar with links to of all of the most recent updates.
The landing page is a Views-based page (so it doesn’t get accidentally deleted). There is also an All Views-based page that lists links to all updates. If the system cannot find the latest update the user will be redirected to the All page.
File Structure
- mymodule
- src
- EventSubscriber
- MyModuleSubscriber.php
- EventSubscriber
- mymodule.services.yml
- src
MyModuleSubscriber.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
<?php namespace Drupal\mymodule\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; use Drupal\Core\Url; /** * Class MyModuleSubscriber. * * @package Drupal\mymodule\EventSubscriber */ class MyModuleSubscriber implements EventSubscriberInterface { /** * Registers the methods in this class that should be listeners. * * @return array * An array of event listener definitions. */ public static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = ['onRequest']; return $events; } /** * Manipulates the request object. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The Event to process. */ public function onRequest(GetResponseEvent $event) { $response = $event->getRequest(); if ($response->getRequestUri() == '/updates') { if ($nid = $this->singleRecentUpdate()) { $alias = \Drupal::service('path.alias_manager')->getAliasByPath('/node/' . $nid); $this->eventRedirectToPath($event, $alias); } else { $this->eventRedirectToPath($event, '/updates/all'); } } } /** * Sets a redirect response on a GetResponseEvent. * * Redirection may not work if the originally-requested * path does not exist (404). In this case you may be redirected * to the front page. * * @param GetResponseEvent $event * Event to update. * @param string $path * Drupal path. */ private function eventRedirectToPath(GetResponseEvent &$event, $path) { $redirect_target_url = Url::fromUserInput($path); $response = new RedirectResponse($redirect_target_url->setAbsolute()->toString()); $event->setResponse($response); } /** * Return the NID of the latest Update node (by field_update_date). * * @return int * Returns the node ID or 0 if no result. */ private function singleRecentUpdate() { $query = \Drupal::entityQuery('node') ->sort('field_update_date', 'DESC') ->condition('type', 'update') ->range(0, 1); if ($nids = $query->execute()) { return reset($nids); } return 0; } } |
mymodule.services.yml
1 2 3 4 5 |
services: mymodule.subscriber: class: Drupal\mymodule\EventSubscriber\MyModuleSubscriber tags: - { name: event_subscriber } |
Handling Invalid URLs
If you try to use the above code to redirect paths that aren’t valid Drupal resources you will find that the onRequest() method doesn’t get called. You can fix this by changing the weight of your method like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/** * Registers the methods in this class that should be listeners. * * @return array * An array of event listener definitions. */ public static function getSubscribedEvents() { // This needs to run before RouterListener::onKernelRequest(), which has // a priority of 32. Otherwise, that aborts the request if no matching // route is found. Also, 33 is what the Redirect module uses, so we want // to run before that as well. $events[KernelEvents::REQUEST][] = ['onRequestRewriteOldProtocolUrls', 34]; return $events; } /** * Manipulates the request object. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The Event to process. */ public function onRequestRewriteOldProtocolUrls(GetResponseEvent $event) { |
2 Comments
Mart
This article was great help to me. Thanks!
Manoj
Hi,
Very nice and simple tutorial. Can you please help me with 404 redirection. I wanted to Redirect the URLs returning a 404 status to the defined closest alternative, or parent category, to reclaim link equity from external domains.
Example: Suppose I am using URL http://www.mysite.com/about/xyz/ and it return 404 so I wanted to create any module or system that smartly redirect to nearest parent path like “http://www.mysite.com/about/”