2

I want to disable path based breadcrumbs if the current route is not a registered admin route. The front end of the site is driven entirely by Menu Breadcrumb, but in some cases the URL path is generated dynamically with Pathauto based on a combination of field values. This results in sometimes the parent paths generated cause path based breadcrumb in core to return a breadcrumb when it should not (if the current page is not in the menu, Menu Breadcrumb is skipped). At the same time, I do not want breadcrumbs in the admin interfered with.

Is the way to support both cases to decorate the core PathBasedBreadcrumb service? Here is what I came up with:

My module service definition:

services:
  mymodule.breadcrumb:
    class: Drupal\mymodule\PathBasedBreadcrumbBuilderOverride
    decorates: system.breadcrumb.default
    decoration_priority: 10
    public: false
    arguments: [ '@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory',  '@title_resolver', '@current_user', '@path.current', '@path.matcher', '@router.admin_context']

The class:

/**
 * Defines a class to build path-based breadcrumbs.
 */
class PathBasedBreadcrumbBuilderOverride extends PathBasedBreadcrumbBuilder {

  /**
   * The admin context service.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  /**
   * {@inheritdoc}
   */
  public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, PathMatcherInterface $path_matcher = NULL, AdminContext $admin_context = NULL) {
    parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path, $path_matcher);
    $this->adminContext = $admin_context;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    return $this->adminContext->isAdminRoute();
  }

}

applies in that case only works from the admin. I am getting the result I would expect to get (no breadcrumb in the frontend of the site unless the current page is in the menu, letting Menu Breadcrumb be the only manager).

Are there other ways of doing this, or is this the simplest way without interfering with core functionality?

1
  • There's at least one other way, but it's not as elegant as your current solution IMO
    – Clive
    Jul 31, 2023 at 19:33

1 Answer 1

3

Normally it's unnecessary to decorate a tagged service. You can register the extended core service class again with a different priority (in your case between the path based and the menu-based breadcrumb builder) and so avoid conflicts with different modules extending the same service because you don't need to swap the service in the container.

Example for "Disable core path based breadcrumb only for the frontend theme"

If you only want to disable the path-based breadcrumb under certain conditions then put the condition in applies() and return an empty breadcrumb:

/src/MyModuleBreadcrumbBuilder.php

<?php

namespace Drupal\mymodule;

use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\AdminContext;
use Drupal\Core\Routing\RouteMatchInterface;

class MyModuleBreadcrumbBuilder implements BreadcrumbBuilderInterface {

  /**
   * The admin context service.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  public function __construct(AdminContext $admin_context) {
    $this->adminContext = $admin_context;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    return !$this->adminContext->isAdminRoute($route_match->getRouteObject());
  }

  /**
   * {@inheritdoc}
   */
  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new Breadcrumb();
    return $breadcrumb;
  }

}

And tag the service with a priority higher than zero but lower than any other breadcrumb builder you don't want to override:

mymodule.services.yml

services:
  mymodule.breadcrumb:
    class: Drupal\mymodule\MyModuleBreadcrumbBuilder
    arguments: ['@router.admin_context']
    tags:
      - { name: breadcrumb_builder, priority: 5 }
4
  • Might be wrong, but wouldn't you need to disable the core PathBasedBreadcrumbBuilder if not replacing/decorating it in this case? Breadcrumb builders cascade and short circuit on priority, so if the extended service returns FALSE in applies (not an admin route), it's going to fall back to the core service, which isn't desired
    – Clive
    Jul 31, 2023 at 19:29
  • You can disable it with a breadcrumb_builder tagged service which applies with a higher priority and return an empty breadcrumb if that's what you want.
    – 4uk4
    Jul 31, 2023 at 19:50
  • Sure; OP would need to change the applies method to return TRUE; in that case, and set a priority lower than the core taxonomy breadcrumb builder too if that still needs to run
    – Clive
    Jul 31, 2023 at 19:59
  • I've added a code example.
    – 4uk4
    Jul 31, 2023 at 21:17

Your Answer

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

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