Score:1

How to create automatic url alias for taxonomy/term/%tid/feed

bd flag

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?

hotwebmatter avatar
nr flag
Pathauto dues not play nice with the default taxonomy term view. I don't have the answer yet but I'll link to my own question (similar to yours but more complex because I am dealing with hierarchical taxonomy
hotwebmatter avatar
nr flag
https://drupal.stackexchange.com/a/304279/80164
4uk4 avatar
cn flag
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 https://api.drupal.org/api/drupal/core%21modules%21path_alias%21src%21PathProcessor%21AliasPathProcessor.php/class/AliasPathProcessor/9.2.x and then either replace the class in the service container or register your own tagged path processor services inbound and outbound.
berliner avatar
bd flag
@hotwebmatter Thanks for the link, but I think your issues is rather different than mine.
berliner avatar
bd flag
@4k4 Thanks for the suggestion. I'm looking for a solution that doesn't require custom code if possible. I'll keep your idea in mind but will wait if someone comes up with an easier way of achieving this. Still hoping there is solution that I just didn't see yet.
4uk4 avatar
cn flag
The custom code involves only string manipulation of the path. Not a big deal.
berliner avatar
bd flag
@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.
4uk4 avatar
cn flag
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.
berliner avatar
bd flag
@4k4 Ok, I tried it now and it works correctly. Are there any performance tradeoffs between this solution and actually creating path aliases?
4uk4 avatar
cn flag
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.
berliner avatar
bd flag
@4k4 Thank you for the valuable feedback. I have posted my solution based on your suggestions as an answer.
Score:2
bd flag

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;
  }
}
4uk4 avatar
cn flag
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: https://api.drupal.org/api/drupal/core%21modules%21path_alias%21src%21PathProcessor%21AliasPathProcessor.php/function/AliasPathProcessor%3A%3AprocessOutbound/9.2.x
berliner avatar
bd flag
@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.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.