So far I did the following:

  1. I've installed Redirect and Token modules.
  2. I've applied the patch for Redirect module to support wildcards.
  3. I've applied the patch for Redirect module to implement hook_redirect_response_alter.
  4. I've defined the following hook_redirect_response_alter to support and parse the tokens.

    use Drupal\redirect\Entity\Redirect;
    use Symfony\Component\HttpFoundation\RedirectResponse;
    use Drupal\Component\Utility\UrlHelper;
    
    /**
     * Implements hook_redirect_response_alter().
     */
    function MYMODULE_redirect_response_alter(RedirectResponse $response, Redirect $redirect) {
      $uri = $redirect->getRedirect()['uri']; // "internal:/bar/[current-page:url:args:last]"
      if (!empty($uri) && strpos($uri, '[') !== FALSE) {
        $uri = \Drupal::token()->replace($uri); // "internal:/bar/"
        $external = UrlHelper::isValid($uri, TRUE); // bool
        $redirect->setRedirect($external ? $uri : explode('/', $uri, 2)[1]);
        $response->setTargetUrl($redirect->getRedirectUrl()->setAbsolute()->toString()); // "http://localhost/bar/"
      }
    }
    
  5. I've added the following redirect at /admin/config/search/redirect:

    • From: foo/*
    • To: /bar/[current-page:url:args:last]

Based on the above, I'm expecting that typing foo/X will redirect to bar/X, and foo/Y to bar/Y accordingly, and so on. However, the above token is replaced with an empty string. Similar thing when using token such as [current-page:url:args:value:2], it's not replaced at all. I've also learned here that using [redirect:url:args:last] won't work either, as the token is generated based on the entity type, so Redirect module isn't aware of it.

I've tried a few more tokens found at /admin/help/token which can potentially extract the second argument from the path, but I haven't found any suitable or working as expected for this task.

For example:

Redirect tokens in Drupal 8 at /admin/help/token Current page tokens in Drupal 8 at /admin/help/token


More details

Applying $redirect context for token's replace() method, as suggested in the comments, didn't work as expected.

So the following line:

$uri = \Drupal::token()->replace($uri, ['redirect' => $redirect]);

populates some token with redirect prefix, however it wrongly replaces the current path, e.g. [redirect:url:args:join:/] becomes admin/config/search/redirect/edit/25 for every page.

I've also tried to populate context for the current-page tokens, but it was requiring the current path exists, however during redirect the current path doesn't exist in the menu routing table, so it can't be loaded (I've tried Url::createFromRequest() and \Drupal::request()).


Question

What I'm missing? Is there any easier way to achieve the same thing?

  • You might need to explicitly replace the token : $redirect->setRedirect(token_replace($uri, array('redirect' => $redirect))); – EricLavault Dec 4 at 22:07
  • Yes, sorry, I've missed one line: $uri = \Drupal::token()->replace($uri); (copy & paste error), I had it in the original code. I can check if adding the argument can make the difference. – kenorb Dec 4 at 22:15
  • @EricLavault Yes, I think something started to behave differently (the [redirect:...] token being populated). I've used [redirect:url:args:join:,] to test it out, and at /foo/X page it's populated as internal:/bar/|admin,config,search,redirect,edit,25, it's like it's on the edit page, when it's not, weird. Altered line: $uri = \Drupal::token()->replace($uri, ['redirect' => $redirect]);. Could it be a bug? – kenorb Dec 4 at 22:24
  • 1
    You can redirect in a custom event subscriber, see drupal.org/node/2013014 – 4k4 Dec 5 at 8:32
  • 1
    Just a wild guess. Because you're doing a redirect the current-page tokens do not get a chance to fire. Because /foo/X does not belong to the Redirect entity, those tokens do not get fired either. You'll have to debug and see if anything in my wild guess is true. – No Sssweat Dec 5 at 14:33
up vote 1 down vote accepted

Since passing context for token's replace method didn't work as expected, I've defined a new token in my custom module to return the specific components of the Drupal path (using the following guide).

my_redirect.tokens.inc

<?php

/**
 * @file
 * An include file defining custom tokens.
 */

use Drupal\Core\Render\BubbleableMetadata;

/**
 * Implements hook_token_info().
 */
function my_redirect_token_info() {
  $type = [
    'name' => t('Custom URL token for redirect'),
    'description' => t('Tokens helpful for redirect pages.'),
  ];

  $node['url-arg'] = [
    'name' => t("URL Argument"),
    'dynamic' => TRUE,
    'description' => t('Returns a component of the current Drupal path.'),
  ];

  return [
    'types' => ['my_redirect' => $type],
    'tokens' => ['my_redirect' => $node],
  ];
}

/**
 * Implements hook_tokens().
 */
function my_redirect_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {

  $replacements = [];

  if ($type == 'my_redirect') {
    if ($argTokens = \Drupal::token()->findWithPrefix($tokens, 'url-arg')) {
      foreach ($argTokens as $arg => $original) {
        $current_path = \Drupal::service('path.current')->getPath();
        if ($arg === "first") {
          $value = explode('/', $current_path)[1];
        }
        elseif ($arg === "last") {
          $value = end(explode('/', $current_path));
        }
        else {
          $value = explode('/', $current_path)[$arg ?: 0];
        }
        $replacements[$original] = $value;
      }
    }
  }

  return $replacements;
}

So after applying already mentioned patches, and above code, I can use the following Redirect rule:

  • From: foo/*
  • To: /bar/[my_redirect:url-arg:2]

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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