Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

The breadcrumb system has been completely redesigned for Drupal 8.

In Drupal 7 and earlier, any code could arbitrarily call drupal_set_breadcrumb() at any point. That led to modules often bumping into each other, themes having a difficult time controlling breadcrumbs, etc. Breadcrumbs were also special cased within the theme system, which also made them harder to control.

In Drupal 8, the breadcrumb system has been converted into a modular service (breadcrumb, implemented by default by Drupal\Core\Breadcrumb\BreadcrumbManager ), and the display of breadcrumbs is now a block (Drupal\system\Plugin\Block\SystemBreadcrumbBlock). Since it is a block that means breadcrumbs may be relocated on the page or even removed entirely the same as any other block.

For declaring what the breadcrumb on a particular page is, modules may register one or more breadcrumb builder services. A breadcrumb builder implements the BreadcrumbBuilderInterface, and is registered with the Service Container with a breadcrumb_builder tag and a priority.

When the block is rendered, each breadcrumb builder will be called in turn with information on the current request to determine if it wishes to control the breadcrumbs for that page. The first builder to declare its ability to do so will then be asked to return a valid breadcrumb definition array. That allows a module to control the breadcrumb on only certain paths or only under certain conditions. Alternatively, a site-specific module could add a builder that takes over all breadcrumbs on all pages and does whatever it wants to.

As an example, the following class sets the breadcrumb of all article nodes but ignores everything else:

// mymodule/src/MyBreadcrumbBuilder.php

namespace Drupal\mymodule;

use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\LinkGeneratorTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;

class MyBreadcrumbBuilder implements BreadcrumbBuilderInterface {
  use LinkGeneratorTrait;
  use StringTranslationTrait;

  /**
   * {@inheritdoc}
   */
  public function applies(array $attributes) {
    if ($attributes['_route'] == 'node_page') {
      return $attributes['node']->bundle() == 'article';
    }
  }

  /**
   * {@inheritdoc}
   */
  public function build(array $attributes) {
    $breadcrumb = [Link::createFromRoute($this->t('Home'), '<front>')];
    // Presumably this link has been defined elsewhere.
    $breadcrumb[] = Link::createFromRoute($this->t('Articles'), 'articles_route');
    // Breadcrumbs should not include the current page. The theme system
    // will take care of that.
    return $breadcrumb;
  }
}
# mymodule.services.yml
services:
  mymodule.breadcrumb:
    class: Drupal\mymodule\MyBreadcrumbBuilder
    tags:
      - { name: breadcrumb_builder, priority: 100 }

In order to fetch the breadcrumb build for the page you can use the following piece of code:

    $breadcrumb_manager = \Drupal::service('breadcrumb');
    $request = \Drupal::service('request');
    $breadcrumb = $breadcrumb_manager->build($request->attributes->all());

though you should never need that as you can't alter it directly.

Impacts: 
Site builders, administrators, editors
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

Oleksiy’s picture

The following piece of code not works to get the breadcrumbs build for the page:

<?php
    $breadcrumb_manager = \Drupal::service('breadcrumb');
    $request = \Drupal::service('request');
    $breadcrumb = $breadcrumb_manager->build($request->attributes->all());
?>

Have updated it a bit to make it works:

<?php
    $breadcrumb_manager = \Drupal::service('breadcrumb');
    $current_route_match = \Drupal::service('current_route_match');
    $breadcrumb = $breadcrumb_manager->build($current_route_match);
?>
cambraca’s picture

The build method works a bit different now, it should return a Breadcrumb object, like so:

  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new \Drupal\Core\Breadcrumb\Breadcrumb();
    $breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
    // Presumably this link has been defined elsewhere.
    $breadcrumb->addLink(Link::createFromRoute($this->t('Articles'), 'articles_route'));
    // Breadcrumbs should not include the current page. The theme system
    // will take care of that.
    return $breadcrumb;
  }
rhuffstedtler’s picture

It's not quite clear from any of the Drupal documentation, but the Symfony DI docs say that for priority, higher priorities take precedence over lower. That, of course, is the opposite of weights in Drupal. Potential pitfall for those who might have multiple breadcrumb services.

Oops - intended that as a new comment, not a reply.

FlutterStack’s picture

I am using drupal 8.2.1 version .

To create bread crumb In service yml service name I was using breadcrumb_builder. Then I end up with
exception 'Symfony\Component\DependencyInjection\Exception\LogicException' with message 'Service [error] 'xxxxx.breadcrumb_xxxx_create' for consumer 'breadcrumb' does not implement Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface.'

# mymodule.services.yml
services:
mymodule.breadcrumb:
class: Drupal\mymodule\MyBreadcrumbBuilder
tags:
- { name: breadcrumb_builder, priority: 100 }

Then I have used

services:
mymodule.breadcrumb:
class: Drupal\mymodule\MyBreadcrumbBuilder
tags:
- { name: breadcrumb, priority: 100 }

worked fine for me .

In // mymodule/src/MyBreadcrumbBuilder.php
namespace Drupal\mymodule;

use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\Url;

/**
* {@inheritdoc}
*/
class MyBreadcrumbBuilder implements BreadcrumbBuilderInterface {

/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match) {
$route_name = $route_match->getRouteName();
// $params = $route_match->getParameters()->all();
if (in_array($route_name, ['- something-'])) {
return TRUE;
}
return FALSE;
}

/**
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {

$breadcrumb = new Breadcrumb();
$links = array();
$links[] = Link::createFromRoute(t('Home'), '');

return $breadcrumb->setLinks($links)->addCacheableDependency(0);
}

}

Worked fine for me.