Drupal - How do you implement a breadcrumb?

Here we go again. These answers are mostly right. One thing you can't forget about is "cache tags" and "cache contexts".

I was setting up a taxonomy term on a node as a breadcrumb.

I got it working with advice from this post, but then I clicked around and noticed the same breadcrumbs on every page.

Long story short, make sure to set some cache contexts and tags.

Here's my service in a gist: https://gist.github.com/jonpugh/ccaeb01e173abbc6c88f7a332d271e4a

Here's my build() method:

/**
 * {@inheritdoc}
 */
public function build(RouteMatchInterface $route_match) {
  $node = $route_match->getParameter('node');
  $breadcrumb = new Breadcrumb();

  // By setting a "cache context" to the "url", each requested URL gets it's own cache.
  // This way a single breadcrumb isn't cached for all pages on the site.
  $breadcrumb->addCacheContexts(["url"]);

  // By adding "cache tags" for this specific node, the cache is invalidated when the node is edited.
  $breadcrumb->addCacheTags(["node:{$node->nid->value}"]);

  // Add "Home" breadcrumb link.
  $breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));

  // Given we have a taxonomy term reference field named "field_section", and that field has data,
  // Add that term as a breadcrumb link.
  if (!empty($node->field_section->entity)) {
    $breadcrumb->addLink($node->field_section->entity->toLink());
  }
  return $breadcrumb;
}

Yeah breadcrumb changed and the documentation must be update.

Likewise, I don't really understand what the services.yml file is doing in this regard, there's no documentation anywhere for this.

For Drupal 8: The Crash Course | DrupalCon Amsterdam 2014, awesome presentation, about 47:02:

Drupal 8 in 2 steps:

  1. Build a tool
  2. Wire it up

The wiring may vary, the approach is the same.

How we "Wire it up" the breadcrumb:

For http://www.palantir.net/blog/d8ftw-breadcrumbs-work:

Now we need to tell the system about our class. To do that, we define a new service (remember those?) referencing our new class. We'll do that in our *.services.yml file, which exists for exactly this purpose

Similar to an "info hook" in previous Drupal versions, we're defining a service named mymodule.breadcrumb. It will be an instance of our breadcrumb class. If necessary we could pass arguments to our our class's constructor as well. Importantly, though, we also tag the service. Tagged services are a feature of the Symfony DependencyInjection component specifically and tell the system to automatically connect our builder to the breadcrumb manager. The priority specifies in what order various builders should be called, highest first. In case two applies() methods might both return true, whichever builder has the higher priority will be used and the other ignored.

You can use this code for you aim:

Structure (not matter much):

- modules/custom/foo_breadcrumb
  - foo_breadcrumb.info.yml
  - foo_breadcrumb.services.yml
  - src/
    - Breadcrumb/
      - BlogBreadcrumbBuilder.php

foo_breadcrumb.services.yml:

services:
  foo_breadcrumb.breadcrumb_blog:
    class: Drupal\foo_breadcrumb\Breadcrumb\BlogBreadcrumbBuilder
    tags:
      - { name: breadcrumb_builder, priority: 100 }

BlogBreadcrumbBuilder.php:

class BlogBreadcrumbBuilder implements BreadcrumbBuilderInterface {
  use StringTranslationTrait;
  use LinkGeneratorTrait;

  /**
   * @inheritdoc
   */
  public function applies(RouteMatchInterface $route_match) {
    // This breadcrumb apply only for all articles
    $parameters = $route_match->getParameters()->all();
    if (isset($parameters['node'])) {
      return $parameters['node']->getType() == 'article';
    }
  }

  /**
   * @inheritdoc
   */
  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = [Link::createFromRoute($this->t('Home'), '<front>')];
    $breadcrumb[] = Link::createFromRoute($this->t('Blog'), '<<<your route for blog>>>');
    return $breadcrumb;
  }
}

Remember, clear cache at the end.


Update 2016 Drupal 8

The Documentation states that you must return an instance of the breadcrumb class. If you are having trouble getting it to work. here is the solution that worked for me.

<?php

//modules/MY_MODULE/src/MyBreadcrumbBuilder.php

namespace Drupal\registration;

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

class MyBreadcrumbBuilder implements BreadcrumbBuilderInterface {

    /**
     * @inheritdoc
     */
    public function applies(RouteMatchInterface $route_match) {
        /* Allways use this. Change this is another module needs to use a new custom breadcrumb */
        return true;
        /* This code allows for only the registration page to get used by this breadcrumb
         * $parameters = explode('.', $route_match->getRouteName());
         * if ($parameters[0] === 'registration') {
         *     return true;
         * } else {
         *     return false;
         * }
         */
    }

    /**
     * @inheritdoc
     */
    public function build(RouteMatchInterface $route_match) {
        $parameters = explode('.', $route_match->getRouteName());
        $b = new Breadcrumb();
        if ($parameters[0] === 'registration') {
            /* If registration page use these links */
            $b->setLinks($this->buildRegistration($parameters[1]));
        }
        return $b;
    }

    /**
     * Creates all the links for the registration breadcrumb
     * @param type $page
     * @return type
     */
    private function buildRegistration($page) {
        return [
            Link::createFromRoute(t('Step One'), 'registration.one'),
            Link::createFromRoute(t('Step Two'), 'registration.two'),
            Link::createFromRoute(t('Step Three'), 'registration.three'),
            Link::createFromRoute(t('Step Four'), 'registration.four'),
            Link::createFromRoute(t('Step Five'), 'registration.five'),
            Link::createFromRoute(t('Step Six'), 'registration.six'),
            Link::createFromRoute(t('Step Seven'), 'registration.seven')
        ];
    }

}

Then the yml file

# modules/MY_MODULE/registration/MY_MODULE.services.yml
services:
  registration.breadcrumb:
    class: Drupal\registration\MyBreadcrumbBuilder
    tags:
      - { name: breadcrumb_builder, priority: 100 }

PS: if you are using bootstrap go to your /admin/appearance/settings settings page and look at the breadcrumbs settings. Show 'Home' breadcrumb link should be checked on. And Show current page title at end should be checked off.

After all this is done clear your cache. Everytime you change a YML file, even in debug mode, you need to clear your cache. you can go to /core/rebuild.php if you get stuck and can't rebuild.

Tags:

Breadcrumbs

8