1

Visiting a node Foobar a breadcrumb gets created which adds the parent menu structure like so:

$currentPluginId = $menu_link->getPluginId();
$trailIds = $this->menuActiveTrail->getActiveTrailIds('main');

foreach (array_reverse($trailIds) as $key => $value) {
  if ($value && $value !== $currentPluginId) {
    // @todo add cache per item
    $instance = $menu_link_manager->createInstance($value);
    $breadcrumb->addLink(
      new Link(
        $instance->getTitle(),
        $instance->getUrlObject()
      )
    );
  }
}

$breadcrumb->addCacheContexts(['route.menu_active_trails:main']);

So the breadcrumb looks like:

Home > John Doe > Foobar

But if the node/ menu item of John Doe changes, the breadcrumb for Foobar stays the same.

dpm($instance->getUrlObject()->getRouteName());
dpm($instance->getUrlObject()->getRouteParameters());

gives me:

entity.node.canonical

and

array( [node] => 25 )

How can I use that for adding each breadcrumb part as cache context/ tag?

2 Answers 2

0

Look at \Drupal\book\BookBreadcrumbBuilder::build() for an example. You'll want to add the cacheability metadata (or at minimum the cache tags) of the node/menu item John Doe.

So:

$breadcrumb->addCacheableDependency($entity);

You could have easily figured this out yourself: you're building a \Drupal\Core\Breadcrumb\Breadcrumb object… which implements RefinableCacheableDependencyInterface, and that interface lists all things related to cacheability.

I highly recommend you read https://www.drupal.org/developing/api/8/cache and all documentation it points to.

2
  • I've read the API and looked through the breadcrumb functions and inherited class methods. I dont have the $node or $entity object, only the nid. So I added $breadcrumb->addCacheTags(['node:' . $instance->getUrlObject()->getRouteParameters()['node']]);
    – Alex
    Jun 8, 2016 at 14:35
  • You can load the node object. The breadcrumb will be cached on subsequent requests anyway, so you only incur the cost of loading the node once :)
    – Wim Leers
    Jun 9, 2016 at 7:51
0

So I came up with the following:

foreach (array_reverse($trailIds) as $key => $value) {
  if ($value && $value !== $currentPluginId) {
    // @todo add cache per item
    $instance = $menu_link_manager->createInstance($value);
    $breadcrumb->addLink(
      new Link(
        $instance->getTitle(),
        $instance->getUrlObject()
      )
    );
    $routeParams = $instance->getUrlObject()->getRouteParameters();
    if (is_array($routeParams) && count($routeParams) && isset($routeParams['node'])) {
      $breadcrumb->addCacheTags(['node:' . $routeParams['node']]);
    }
  }
}

As it turns out, changing a menu item without really changing the node though does not invalidate the cache so the breadcrumb would still have old menu titles. This line somehow has no impact, either

$breadcrumb->addCacheContexts(['route.menu_active_trails:main']);

So I added this to the breadcrumb:

$breadcrumb->addCacheTags(['config:system.menu.main']);

And voila. As soon as the menu title of John Doe changes, the breadcrumb on page Foobar also updates.

Maybe it's even a better idea to not add the node cache tags but only the menu cache tag. Guess you have to play with how the site behaves over time.

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.