Add parameter to the URL based on a field value Is it possible to append a campaign tracking code to the requested URL basing on a specific field value on the linked node? I'm trying to use a path_processor_outbound service, but it doesn't work quite as expected and it works rather randomly. 

This is the code I am using.

function processOutbound($path, &amp;$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {if(isset($options['entity']) &amp;&amp; $options['entity'] instanceof \Drupal\node\NodeInterface ) {
      $node = $options['entity'];

      if($node-&gt;bundle() == 'article') {
        $channel = $node-&gt;get('field_channel')-&gt;getValue();
        $promotions = array(10);
        $channel_value = reset($channel);
        $channel_id = $channel_value['target_id'];

        if(in_array($channel_id, $promotions)) {
          $options['query']['foo'] = 'bar';
        }
      }
    }
    return $path;
  }


I have a feeling this is correct approach for links to nodes generated on site (teasers on front page), but it doesn't append parameters on page request.

I also considered adding parameters to the path alias, but that doesn't seem to be right and JavaScript code would be added too late.
Don't know if it fits for your use case, but I wanted all outgoing links from a specific node type to have the ID appended, e.g. on every product page all outgoing "Call to action" links should appent something like ?from_product_id=1234. My module name was 'wt_ctalinks`

wt_ctalinks.services.yml

services:
  cache_context.wt_ctalinks:
    class: Drupal\wt_ctalinks\CtaLinksCacheContext
    arguments: ['@request_stack']
    tags:
      - { name: cache.context }
  wt_ctalinks.ctalinks_pathmodifier_service:
    class: Drupal\wt_ctalinks\CtaLinksPathModifierService
    arguments: ['@request_stack']
    tags:
      - { name: path_processor_outbound, priority: 3000 }


src/CtaLinksCacheContext.php

&lt;?php

namespace Drupal\wt_ctalinks;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;

class CtaLinksCacheContext implements CacheContextInterface {


  /**
   * @var Symfony\Component\HttpFoundation\RequestStack;
   */
  protected $requestStack;

  public function __construct(RequestStack $request_stack) {
    $this-&gt;requestStack = $request_stack;
  }

  /**
  * {@inheritdoc}
  */
  public static function getLabel() {
    drupal_set_message('Modify CTA links based on current node type');
  }

  /**
  * {@inheritdoc}
  */
  public function getContext() {
    $nid = 0;
    $request = $this-&gt;requestStack-&gt;getCurrentRequest();
    $node = $request-&gt;attributes-&gt;get('node');
    if (is_numeric($node)) {
      $node = Node::load($node);
    }
    if ($node instanceof NodeInterface &amp;&amp; $node-&gt;bundle() === 'article') {
      $nid = $node-&gt;id();
    }
    return $nid;
  }

  /**
  * {@inheritdoc}
  */
  public function getCacheableMetadata() {
    return new CacheableMetadata();
  }
}



src/CtaLinksPathModifierService.php

&lt;?php

namespace Drupal\wt_ctalinks;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\config_pages\Entity\ConfigPages;
use Drupal\node\Entity\Node;


/**
 * Class DefaultService.
 */
class CtaLinksPathModifierService implements OutboundPathProcessorInterface {

  /**
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  protected $bookingPath;
  protected $enquirePath;

  /**
   * Constructs a new DefaultService object.
   */
  public function __construct(RequestStack $request_stack) {
    $this-&gt;requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &amp;$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    if (empty($request)) {
      $request = $this-&gt;requestStack-&gt;getCurrentRequest();
    }

    $node = $request-&gt;attributes-&gt;get('node');

    if (is_numeric($node)) {
      // missing upcasting in revision view, have to load manually there
      $node = Node::load($node);
    }

    if ($node instanceof NodeInterface &amp;&amp; $node-&gt;getType() === 'article') {
      // your logic here
    }

    if ($bubbleable_metadata) {
      $bubbleable_metadata
        -&gt;addCacheContexts([
          'wt_ctalinks',
        ]);
    }
    return $path;
  }
}