Question

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?

Was it helpful?

Solution

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]
Licensed under: CC-BY-SA with attribution
Not affiliated with drupal.stackexchange
scroll top