Score:2

Disable core path based breadcrumb only for the frontend theme

in flag

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?

cn flag
There's at least one other way, but it's not as elegant as your current solution IMO
Score:3
cn flag

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 }
cn flag
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
4uk4 avatar
cn flag
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.
cn flag
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
4uk4 avatar
cn flag
I've added a code example.
I sit in a Tesla and translated this thread with Ai:

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.