1

How can I set automatic URL aliases for taxonomy feeds?

Say I have a term with tid=1, named holidays, with a system URL of /taxonomy/term/1 that is accessible by the URL alias /holidays. Then I want the rss feed for that term to be accessible on the URL alias /holidays/feed or /holidays/rss.xml. That sounded rather easy when I first thought about it, but it seems that pathauto is not supporting that (or I missed it). I know I can create the alias manually, but given a number of X terms on the site, I'd rather use a pattern for automatic generation.

The only issues about this topic that I could find are both outdated:

Anything I missed or any contrib module I didn't see that provides this functionality?

11
  • 1
    When you have already aliases like /holidays you could write a custom path processor applying those aliases without the appendix /feed or /rss.xml. Extend api.drupal.org/api/drupal/… and then either replace the class in the service container or register your own tagged path processor services inbound and outbound.
    – 4uk4
    Aug 3, 2021 at 9:51
  • 1
    @4k4 Does your proposed solution also apply to outgoing urls? I want to show a link to the RSS feed on the term listing page too.
    – berliner
    Aug 9, 2021 at 11:13
  • 2
    Yes, in a custom path processor, based on the linked AliasPathProcessor, you would implement processOutbound() which removes /feed from the path, calls getAliasByPath() and then adds /feed again.
    – 4uk4
    Aug 9, 2021 at 11:21
  • 1
    @4k4 Ok, I tried it now and it works correctly. Are there any performance tradeoffs between this solution and actually creating path aliases?
    – berliner
    Aug 9, 2021 at 14:06
  • 2
    No, this rather simple path processing is not a performance issue, you won't notice these few cpu cycles and the result is also cached, both inbound as route match and outbound as rendered links. But you save a lot of space in the alias table.
    – 4uk4
    Aug 9, 2021 at 15:21

1 Answer 1

2

Based on suggestions by @4k4 in the comments section of the question I have solved this problem by implementing a custom PathProcessor.

I'll share the bare minimum code for making this work in form of a custom module (called term_feed_alias for the purpose of this example) for which you need 3 files.

term_feed_alias.info.yml

name: 'Term feed alias'
description: 'Provides automatic aliases for taxonomy term feeds'
version: '8.x-1.0'
core_version_requirement: ^8.8 || ^9
type: module
dependencies:
  - 'path_alias:path_alias'

term_feed_alias.services.yml

services:
  term_feed_alias.path_processor:
    class: Drupal\term_feed_alias\PathProcessor\PathProcessorFeedAlias
    arguments: ['@path_alias.manager']
    tags:
      - { name: path_processor_inbound }
      - { name: path_processor_outbound }

The service tags can also receive a priority, see https://www.drupal.org/docs/8/api/services-and-dependency-injection/service-tags This might be needed to integrate the alias logic correctly with other services like translations for example. Also see first comment by @4k4.

scr/PathProcessorFeedAlias.php

<?php

namespace Drupal\term_feed_alias\PathProcessor;

use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\path_alias\AliasManager;
use Symfony\Component\HttpFoundation\Request;

/**
 * Provide a path processor to handle aliases for taxonomy term feeds.
 */
class PathProcessorFeedAlias implements InboundPathProcessorInterface, OutboundPathProcessorInterface {

  /**
   * The alias manager that caches alias lookups based on the request.
   *
   * @var \Drupal\path_alias\AliasManager
   */
  protected $aliasManager;

  /**
   * Constructs a new PathProcessorFeedAlias instance.
   *
   * @param \Drupal\path_alias\AliasManager $alias_manager
   *   The alias manager.
   */
  public function __construct(AliasManager $alias_manager) {
    $this->aliasManager = $alias_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function processInbound($path, Request $request) {
    $args = explode('/', trim($path, '/'));
    if (end($args) == 'feed') {
      array_pop($args);
      $system_path = $this->aliasManager->getPathByAlias('/' . implode('/', $args));
      return $system_path && strpos($system_path, '/taxonomy/term/') === 0 ? $system_path . '/feed' : $path;
    }
    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    $args = explode('/', ltrim($path, '/'));
    if ($args[0] == 'taxonomy' && $args[1] == 'term' && end($args) == 'feed') {
      array_pop($args);
      $alias = $this->aliasManager->getAliasByPath('/' . implode('/', $args));
      return '/' . $alias . '/feed';
    }

    return $path;
  }
}
2
  • 1
    Great code example. A suggestion for multilingual sites. Set a priority { name: path_processor_outbound, priority: 200 }. The code works only when the language prefix is not added yet (with priority 100). In this case you need also the language option: api.drupal.org/api/drupal/…
    – 4uk4
    Aug 10, 2021 at 6:41
  • 2
    @4k4 I have updated the answer with a note about priorities and a link to the service tags documentation on drupal.org. I had initially left the priorities out on purpose, in order to keep the example to the strict minimum, but I suppose that this is the kind of problem that's more difficult to figure out when trying to re-use example code, so a note about it doesn't hurt.
    – berliner
    Aug 10, 2021 at 12:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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