You are here

class RedirectPathTranslatorSubscriber in Decoupled Router 8

Same name and namespace in other branches
  1. 2.x src/EventSubscriber/RedirectPathTranslatorSubscriber.php \Drupal\decoupled_router\EventSubscriber\RedirectPathTranslatorSubscriber

Event subscriber that processes a path translation with the redirect info.

Hierarchy

Expanded class hierarchy of RedirectPathTranslatorSubscriber

1 string reference to 'RedirectPathTranslatorSubscriber'
decoupled_router.services.yml in ./decoupled_router.services.yml
decoupled_router.services.yml
1 service uses RedirectPathTranslatorSubscriber
decoupled_router.redirect_path_translator.subscriber in ./decoupled_router.services.yml
Drupal\decoupled_router\EventSubscriber\RedirectPathTranslatorSubscriber

File

src/EventSubscriber/RedirectPathTranslatorSubscriber.php, line 14

Namespace

Drupal\decoupled_router\EventSubscriber
View source
class RedirectPathTranslatorSubscriber extends RouterPathTranslatorSubscriber {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {

    // We wanna run before the router-based path translator because redirects
    // naturally act before routing subsystem in Drupal HTTP kernel.
    $events[PathTranslatorEvent::TRANSLATE][] = [
      'onPathTranslation',
      10,
    ];
    return $events;
  }

  /**
   * {@inheritdoc}
   */
  public function onPathTranslation(PathTranslatorEvent $event) {
    $response = $event
      ->getResponse();
    if (!$response instanceof CacheableJsonResponse) {
      $this->logger
        ->error('Unable to get the response object for the decoupled router event.');
      return;
    }
    if (!$this->moduleHandler
      ->moduleExists('redirect')) {
      return;
    }

    // Find the redirected path. Bear in mind that we need to go through several
    // redirection levels before handing off to the route translator.
    $entity_type_manager = $this->container
      ->get('entity_type.manager');
    $redirect_storage = $entity_type_manager
      ->getStorage('redirect');
    $destination = parse_url($event
      ->getPath(), PHP_URL_PATH);
    $original_query_string = parse_url($event
      ->getPath(), PHP_URL_QUERY);
    $traced_urls = [];
    $redirect = NULL;
    $redirects_trace = [];
    while (TRUE) {
      $destination = $this
        ->cleanSubdirInPath($destination, $event
        ->getRequest());

      // Find if there is a redirect for this path.
      $results = $redirect_storage
        ->getQuery()
        ->condition('redirect_source.path', ltrim($destination, '/'))
        ->execute();
      $rid = reset($results);
      if (!$rid) {
        break;
      }

      /** @var \Drupal\redirect\Entity\Redirect $redirect */
      $redirect = $redirect_storage
        ->load($rid);
      $response
        ->addCacheableDependency($redirect);
      $uri = $redirect
        ->get('redirect_redirect')->uri;
      $url = Url::fromUri($uri)
        ->toString(TRUE);
      $redirects_trace[] = [
        'from' => $this
          ->makeRedirectUrl($destination, $original_query_string),
        'to' => $this
          ->makeRedirectUrl($url
          ->getGeneratedUrl(), $original_query_string),
        'status' => $redirect
          ->getStatusCode(),
      ];
      $destination = $url
        ->getGeneratedUrl();

      // Detect infinite loops and break if there is one.
      $infinite_loop = in_array($destination, array_map(function (GeneratedUrl $url) {
        return $url
          ->getGeneratedUrl();
      }, $traced_urls));

      // Accumulate all the URLs we go through to add the necessary cacheability
      // metadata at the end.
      $traced_urls[] = $url;
      if ($infinite_loop) {
        break;
      }
    }
    if (!$redirect) {
      return;
    }

    // At this point we should be pointing to a system route or path alias.
    $event
      ->setPath($this
      ->makeRedirectUrl($destination, $original_query_string));

    // Now call the route level.
    parent::onPathTranslation($event);
    if (!$response
      ->isSuccessful()) {
      return;
    }

    // Set the content in the response.
    $content = Json::decode($response
      ->getContent());
    $response
      ->setData(array_merge($content, [
      'redirect' => $redirects_trace,
    ]));

    // If there is a response object, add the cacheability metadata necessary
    // for the traced URLs.
    array_walk($traced_urls, function ($traced_url) use ($response) {
      $response
        ->addCacheableDependency($traced_url);
    });
    $event
      ->stopPropagation();
  }

  /**
   * Generates URL for the redirect, based on redirect module configurations.
   *
   * @param string $path URL to redirect to.
   * @param string $query Original query string on the requested path.
   *
   * @return string Redirect URL to use.
   */
  private function makeRedirectUrl($path, $query) {
    return $query && $this->configFactory
      ->get('redirect.settings')
      ->get('passthrough_querystring') ? "{$path}?{$query}" : $path;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
RedirectPathTranslatorSubscriber::getSubscribedEvents public static function Returns an array of event names this subscriber wants to listen to. Overrides RouterPathTranslatorSubscriber::getSubscribedEvents
RedirectPathTranslatorSubscriber::makeRedirectUrl private function Generates URL for the redirect, based on redirect module configurations.
RedirectPathTranslatorSubscriber::onPathTranslation public function Processes a path translation request. Overrides RouterPathTranslatorSubscriber::onPathTranslation
RouterPathTranslatorSubscriber::$aliasManager protected property The alias manager
RouterPathTranslatorSubscriber::$configFactory protected property The config factory
RouterPathTranslatorSubscriber::$container protected property The service container.
RouterPathTranslatorSubscriber::$logger protected property The logger.
RouterPathTranslatorSubscriber::$moduleHandler protected property The module handler.
RouterPathTranslatorSubscriber::$router protected property The router.
RouterPathTranslatorSubscriber::cleanSubdirInPath protected function Removes the subdir prefix from the path.
RouterPathTranslatorSubscriber::findEntityAndKeys protected function Get the underlying entity and the type of ID param enhancer for the routes.
RouterPathTranslatorSubscriber::findEntityTypeFromRoute protected function Extracts the entity type for the route parameters.
RouterPathTranslatorSubscriber::getEntityRouteParameterName protected static function Computes the name of the entity route parameter for JSON API routes.
RouterPathTranslatorSubscriber::resolvedPathIsHomePath protected function Checks if the resolved path is the home path.
RouterPathTranslatorSubscriber::__construct public function RouterPathTranslatorSubscriber constructor.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.