Unrelated to this question, which is about how to properly inject breadcrumbs as a renderable item into a node template.. I have a custom breadcrumb builder for certain node types with the same sort of cache clearing issue.
After I deploy code to production, and clear the Drupal cache, about 50% of the time anonymous users stop seeing breadcrumbs on these 3 node types. I find that I have to clear cache again, then clear the Varnish cache on the server, and then they appear again for anonymous users. Am I missing something in the build method?
<?php
declare(strict_types = 1);
namespace Drupal\harlib_breadcrumbs;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Link;
use Drupal\Core\Routing\AdminContext;
use Drupal\node\NodeInterface;
use Drupal\Core\Path\AliasManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Url;
/**
* Class ContentTypeBasedBreadcrumbBuilder.
*
* @package Drupal\harlib_breadcrumbs
*/
class ContentTypeBasedBreadcrumbBuilder implements BreadcrumbBuilderInterface {
use StringTranslationTrait;
/**
* The AdminContext service.
*
* @var \Drupal\Core\Routing\AdminContext
*/
protected $adminContext;
/**
* The AliasManager service.
*
* @var \Drupal\Core\Path\AliasManagerInterface
*/
protected $aliasManager;
/**
* The EntityTypeManager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* {@inheritdoc}
*/
public function __construct(
AdminContext $admin_context,
AliasManagerInterface $alias_manager,
EntityTypeManagerInterface $entity_type_manager
) {
$this->adminContext = $admin_context;
$this->aliasManager = $alias_manager;
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match) : bool {
if (!$this->adminContext->isAdminRoute()) {
$parameters = $route_match->getParameters()->all();
if (isset($parameters['node']) && $parameters['node'] instanceof NodeInterface) {
$types = ['story', 'press_release', 'news_mention'];
$type = $parameters['node']->getType();
return in_array($type, $types);
}
}
return FALSE;
}
/**
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) : Breadcrumb {
$node = $route_match->getParameter('node');
$breadcrumb = new Breadcrumb();
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
if ($url = $this->getNodeUrlByAlias('/about')) {
$breadcrumb->addLink(Link::fromTextAndUrl($this->t('About'), $url));
}
if ($url = $this->getNodeUrlByAlias('/about/news')) {
$breadcrumb->addLink(Link::fromTextAndUrl($this->t('News'), $url));
}
$breadcrumb->addCacheContexts(['url.path']);
$breadcrumb->addCacheTags(["node:{$node->nid->value}"]);
return $breadcrumb;
}
/**
* Create a Url object if the alias matches a node object path.
*
* @param string $alias
* The node alias.
*
* @return bool|\Drupal\Core\Url
* A Url object, or FALSE if no result found.
*/
protected function getNodeUrlByAlias(string $alias) {
$path = $this->aliasManager->getPathByAlias($alias);
if (preg_match('/node\/(\d+)/', $path, $matches)) {
$id = $matches[1];
return Url::fromRoute('entity.node.canonical', ['node' => $id]);
}
return FALSE;
}
}