diff --git a/core/core.services.yml b/core/core.services.yml
index 0fe0a7f..9b3f72a 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -39,6 +39,11 @@ services:
arguments: ['@request_stack']
tags:
- { name: cache.context }
+ cache_context.url.path:
+ class: Drupal\Core\Cache\PathCacheContext
+ arguments: ['@request_stack']
+ tags:
+ - { name: cache.context }
cache_context.url.query_args:
class: Drupal\Core\Cache\Context\QueryArgsCacheContext
arguments: ['@request_stack']
diff --git a/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
new file mode 100644
index 0000000..1a38f8b
--- /dev/null
+++ b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
@@ -0,0 +1,62 @@
+links;
+ }
+
+ /**
+ * Sets the breadcrumb links.
+ *
+ * @param \Drupal\Core\Link[] $links
+ * The breadcrumb links.
+ *
+ * @return $this
+ */
+ public function setLinks(array $links) {
+ $this->links = $links;
+
+ return $this;
+ }
+
+ /**
+ * Appends a link to the end of the ordered list of breadcrumb links.
+ *
+ * @param \Drupal\Core\Link $link
+ * The link appended to the breadcrumb.
+ *
+ * @return $this
+ */
+ public function addLink(Link $link) {
+ $this->links[] = $link;
+
+ return $this;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
index ebdfa55..e566f54 100644
--- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
@@ -32,9 +32,8 @@ public function applies(RouteMatchInterface $route_match);
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*
- * @return \Drupal\Core\Link[]
- * An array of links for the breadcrumb. Returning an empty array will
- * suppress all breadcrumbs.
+ * @return \Drupal\Core\Breadcrumb\Breadcrumb
+ * A breadcrumb.
*/
public function build(RouteMatchInterface $route_match);
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
index 0099897..c233691 100644
--- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
@@ -76,21 +76,28 @@ public function applies(RouteMatchInterface $route_match) {
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {
- $breadcrumb = array();
+ $build = new Breadcrumb();
+
+ // Without builders return early.
+ $sorted_builders = $this->getSortedBuilders();
+ if (empty($sorted_builders)) {
+ return $build;
+ }
+
$context = array('builder' => NULL);
// Call the build method of registered breadcrumb builders,
// until one of them returns an array.
- foreach ($this->getSortedBuilders() as $builder) {
+ foreach ($sorted_builders as $builder) {
if (!$builder->applies($route_match)) {
// The builder does not apply, so we continue with the other builders.
continue;
}
- $build = $builder->build($route_match);
+ $breadcrumb = $builder->build($route_match);
- if (is_array($build)) {
- // The builder returned an array of breadcrumb links.
- $breadcrumb = $build;
+ if ($breadcrumb instanceof Breadcrumb) {
+ $build->setCacheContexts($breadcrumb->getCacheContexts());
+ $build->setLinks($breadcrumb->getLinks());
$context['builder'] = $builder;
break;
}
@@ -99,9 +106,9 @@ public function build(RouteMatchInterface $route_match) {
}
}
// Allow modules to alter the breadcrumb.
- $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context);
- // Fall back to an empty breadcrumb.
- return $breadcrumb;
+ $this->moduleHandler->alter('system_breadcrumb', $build, $route_match, $context);
+
+ return $build;
}
/**
diff --git a/core/lib/Drupal/Core/Cache/PathCacheContext.php b/core/lib/Drupal/Core/Cache/PathCacheContext.php
new file mode 100644
index 0000000..3fd3bc6
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/PathCacheContext.php
@@ -0,0 +1,40 @@
+t('Path');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContext() {
+ $request = $this->requestStack->getCurrentRequest();
+ return $request->getBasePath() . $request->getPathInfo();
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/menu.api.php b/core/lib/Drupal/Core/Menu/menu.api.php
index 1d8298f..67aa7c9 100644
--- a/core/lib/Drupal/Core/Menu/menu.api.php
+++ b/core/lib/Drupal/Core/Menu/menu.api.php
@@ -533,12 +533,8 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
/**
* Perform alterations to the breadcrumb built by the BreadcrumbManager.
*
- * @param array $breadcrumb
- * An array of breadcrumb link a tags, returned by the breadcrumb manager
- * build method, for example
- * @code
- * array('Home');
- * @endcode
+ * @param \Drupal\Core\Breadcrumb\Breadcrumb $breadcrumb
+ * A breadcrumb object returned by BreadcrumbBuilderInterface::build().
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param array $context
@@ -549,9 +545,9 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
*
* @ingroup menu
*/
-function hook_system_breadcrumb_alter(array &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
+function hook_system_breadcrumb_alter(Drupal\Core\Breadcrumb\Breadcrumb &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
// Add an item to the end of the breadcrumb.
- $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name');
+ $breadcrumb->addLink(Drupal::l(t('Text'), 'example_route_name'));
}
/**
diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml
index 0a022a7..06affbb 100644
--- a/core/modules/book/book.services.yml
+++ b/core/modules/book/book.services.yml
@@ -26,6 +26,8 @@ services:
cache_context.route.book_navigation:
class: Drupal\book\Cache\BookNavigationCacheContext
arguments: ['@request_stack']
+ calls:
+ - [setContainer, ['@service_container']]
tags:
- { name: cache.context}
diff --git a/core/modules/book/src/BookBreadcrumbBuilder.php b/core/modules/book/src/BookBreadcrumbBuilder.php
index be0e63a..b1ece44 100644
--- a/core/modules/book/src/BookBreadcrumbBuilder.php
+++ b/core/modules/book/src/BookBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
namespace Drupal\book;
use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Link;
@@ -72,6 +73,8 @@ public function applies(RouteMatchInterface $route_match) {
*/
public function build(RouteMatchInterface $route_match) {
$book_nids = array();
+ $breadcrumb = new Breadcrumb();
+
$links = array(Link::createFromRoute($this->t('Home'), ''));
$book = $route_match->getParameter('node')->book;
$depth = 1;
@@ -92,7 +95,9 @@ public function build(RouteMatchInterface $route_match) {
$depth++;
}
}
- return $links;
+ $breadcrumb->setLinks($links);
+ $breadcrumb->setCacheContexts(['route.book_navigation']);
+ return $breadcrumb;
}
}
diff --git a/core/modules/comment/src/CommentBreadcrumbBuilder.php b/core/modules/comment/src/CommentBreadcrumbBuilder.php
index 8bc2f25..8c6866f 100644
--- a/core/modules/comment/src/CommentBreadcrumbBuilder.php
+++ b/core/modules/comment/src/CommentBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
namespace Drupal\comment;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
@@ -47,19 +48,21 @@ public function applies(RouteMatchInterface $route_match) {
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {
- $breadcrumb = [Link::createFromRoute($this->t('Home'), '')];
+ $breadcrumb = new Breadcrumb();
+ $breadcrumb->setCacheContexts(['route']);
+ $links = [Link::createFromRoute($this->t('Home'), '')];
$entity = $route_match->getParameter('entity');
- $breadcrumb[] = new Link($entity->label(), $entity->urlInfo());
+ $links[] = new Link($entity->label(), $entity->urlInfo());
if (($pid = $route_match->getParameter('pid')) && ($comment = $this->storage->load($pid))) {
/** @var \Drupal\comment\CommentInterface $comment */
// Display link to parent comment.
// @todo Clean-up permalink in https://www.drupal.org/node/2198041
- $breadcrumb[] = new Link($comment->getSubject(), $comment->urlInfo());
+ $links[] = new Link($comment->getSubject(), $comment->urlInfo());
}
- return $breadcrumb;
+ return $breadcrumb->setLinks($links);
}
}
diff --git a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
index f595ee8..d15478f 100644
--- a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
+++ b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
@@ -8,6 +8,7 @@
namespace Drupal\forum\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Link;
@@ -65,14 +66,17 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {
- $breadcrumb[] = Link::createFromRoute($this->t('Home'), '');
+ $breadcrumb = new Breadcrumb();
+ $breadcrumb->setCacheContexts(['route']);
+
+ $links[] = Link::createFromRoute($this->t('Home'), '');
$vocabulary = $this->entityManager
->getStorage('taxonomy_vocabulary')
->load($this->config->get('vocabulary'));
- $breadcrumb[] = Link::createFromRoute($vocabulary->label(), 'forum.index');
+ $links[] = Link::createFromRoute($vocabulary->label(), 'forum.index');
- return $breadcrumb;
+ return $breadcrumb->setLinks($links);
}
}
diff --git a/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php b/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php
index 9d63772..b446f3e 100644
--- a/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php
+++ b/core/modules/forum/src/Breadcrumb/ForumListingBreadcrumbBuilder.php
@@ -7,6 +7,7 @@
namespace Drupal\forum\Breadcrumb;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
@@ -27,6 +28,7 @@ public function applies(RouteMatchInterface $route_match) {
*/
public function build(RouteMatchInterface $route_match) {
$breadcrumb = parent::build($route_match);
+ $breadcrumb->addCacheContexts(['route']);
// Add all parent forums to breadcrumbs.
$term_id = $route_match->getParameter('taxonomy_term')->id();
@@ -34,9 +36,9 @@ public function build(RouteMatchInterface $route_match) {
if ($parents) {
foreach (array_reverse($parents) as $parent) {
if ($parent->id() != $term_id) {
- $breadcrumb[] = Link::createFromRoute($parent->label(), 'forum.page', array(
+ $breadcrumb->addLink(Link::createFromRoute($parent->label(), 'forum.page', array(
'taxonomy_term' => $parent->id(),
- ));
+ )));
}
}
}
diff --git a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
index 090f0ea..09923f3 100644
--- a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
+++ b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
@@ -29,18 +29,20 @@ public function applies(RouteMatchInterface $route_match) {
*/
public function build(RouteMatchInterface $route_match) {
$breadcrumb = parent::build($route_match);
+ $breadcrumb->addCacheContexts(['route']);
$parents = $this->forumManager->getParents($route_match->getParameter('node')->forum_tid);
if ($parents) {
$parents = array_reverse($parents);
foreach ($parents as $parent) {
- $breadcrumb[] = Link::createFromRoute($parent->label(), 'forum.page',
+ $breadcrumb->addLink(Link::createFromRoute($parent->label(), 'forum.page',
array(
'taxonomy_term' => $parent->id(),
)
- );
+ ));
}
}
+
return $breadcrumb;
}
diff --git a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php
index da7ff11..528aceb 100644
--- a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php
+++ b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumBreadcrumbBuilderBaseTest.php
@@ -128,7 +128,7 @@ public function testBuild() {
);
// And finally, the test.
- $this->assertEquals($expected, $breadcrumb_builder->build($route_match));
+ $this->assertEquals($expected, $breadcrumb_builder->build($route_match)->getLinks());
}
}
diff --git a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumListingBreadcrumbBuilderTest.php b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumListingBreadcrumbBuilderTest.php
index 95c670f..b607b48 100644
--- a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumListingBreadcrumbBuilderTest.php
+++ b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumListingBreadcrumbBuilderTest.php
@@ -10,6 +10,7 @@
use Drupal\Core\Link;
use Drupal\Tests\UnitTestCase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\forum\Breadcrumb\ForumListingBreadcrumbBuilder
@@ -171,6 +172,13 @@ public function testBuild() {
)
);
+ $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $container = new Container();
+ $container->set('cache_contexts_manager', $cache_contexts_manager);
+ \Drupal::setContainer($container);
+
// Add a translation manager for t().
$translation_manager = $this->getStringTranslationStub();
$breadcrumb_builder->setStringTranslation($translation_manager);
@@ -197,7 +205,7 @@ public function testBuild() {
Link::createFromRoute('Fora_is_the_plural_of_forum', 'forum.index'),
Link::createFromRoute('Something', 'forum.page', array('taxonomy_term' => 1)),
);
- $this->assertEquals($expected1, $breadcrumb_builder->build($route_match));
+ $this->assertEquals($expected1, $breadcrumb_builder->build($route_match)->getLinks());
// Second test.
$expected2 = array(
@@ -206,7 +214,7 @@ public function testBuild() {
Link::createFromRoute('Something else', 'forum.page', array('taxonomy_term' => 2)),
Link::createFromRoute('Something', 'forum.page', array('taxonomy_term' => 1)),
);
- $this->assertEquals($expected2, $breadcrumb_builder->build($route_match));
+ $this->assertEquals($expected2, $breadcrumb_builder->build($route_match)->getLinks());
}
}
diff --git a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php
index ec5dec0..2b94b93 100644
--- a/core/modules/forum/tests/src/Unit/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php
+++ b/core/modules/forum/tests/src/Unit/Breadcrumb/ForumNodeBreadcrumbBuilderTest.php
@@ -9,7 +9,7 @@
use Drupal\Core\Link;
use Drupal\Tests\UnitTestCase;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\Container;
/**
* @coversDefaultClass \Drupal\forum\Breadcrumb\ForumNodeBreadcrumbBuilder
@@ -179,6 +179,13 @@ public function testBuild() {
)
);
+ $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $container = new Container();
+ $container->set('cache_contexts_manager', $cache_contexts_manager);
+ \Drupal::setContainer($container);
+
// Add a translation manager for t().
$translation_manager = $this->getStringTranslationStub();
$property = new \ReflectionProperty('Drupal\forum\Breadcrumb\ForumNodeBreadcrumbBuilder', 'stringTranslation');
@@ -203,7 +210,7 @@ public function testBuild() {
Link::createFromRoute('Forums', 'forum.index'),
Link::createFromRoute('Something', 'forum.page', array('taxonomy_term' => 1)),
);
- $this->assertEquals($expected1, $breadcrumb_builder->build($route_match));
+ $this->assertEquals($expected1, $breadcrumb_builder->build($route_match)->getLinks());
// Second test.
$expected2 = array(
@@ -212,7 +219,7 @@ public function testBuild() {
Link::createFromRoute('Something else', 'forum.page', array('taxonomy_term' => 2)),
Link::createFromRoute('Something', 'forum.page', array('taxonomy_term' => 1)),
);
- $this->assertEquals($expected2, $breadcrumb_builder->build($route_match));
+ $this->assertEquals($expected2, $breadcrumb_builder->build($route_match)->getLinks());
}
}
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 0c097ba..3872aa1 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -486,14 +486,14 @@ function menu_ui_preprocess_block(&$variables) {
/**
* Implements hook_system_breadcrumb_alter().
*/
-function menu_ui_system_breadcrumb_alter(array &$breadcrumb, RouteMatchInterface $route_match, array $context) {
+function menu_ui_system_breadcrumb_alter(&$breadcrumb, RouteMatchInterface $route_match, array $context) {
// Custom breadcrumb behavior for editing menu links, we append a link to
// the menu in which the link is found.
if (($route_match->getRouteName() == 'menu_ui.link_edit') && $menu_link = $route_match->getParameter('menu_link_plugin')) {
if (($menu_link instanceof MenuLinkInterface)) {
// Add a link to the menu admin screen.
$menu = Menu::load($menu_link->getMenuName());
- $breadcrumb[] = Link::createFromRoute($menu->label(), 'entity.menu.edit_form', array('menu' => $menu->id()));
+ $breadcrumb->addLink(Link::createFromRoute($menu->label(), 'entity.menu.edit_form', array('menu' => $menu->id())));
}
}
}
diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
index d8dec6a..bcc142d 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
@@ -77,6 +77,7 @@ function testPageCacheTags() {
'route.menu_active_trails:tools',
'theme',
'timezone',
+ 'url.path',
'user.permissions',
// The cache contexts associated with the (in)accessible menu links are
// bubbled.
diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
index c6e51af..2ee39b4 100644
--- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php
+++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
@@ -9,7 +9,9 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Link;
@@ -125,6 +127,7 @@ public function applies(RouteMatchInterface $route_match) {
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match) {
+ $breadcrumb = new Breadcrumb();
$links = array();
// General path-based breadcrumbs. Use the actual request path, prior to
@@ -139,17 +142,21 @@ public function build(RouteMatchInterface $route_match) {
// /user is just a redirect, so skip it.
// @todo Find a better way to deal with /user.
$exclude['/user'] = TRUE;
+ // Because this breadcrumb builder is entirely path-based, vary by the 'url.path'
+ // cache context.
+ $breadcrumb->setCacheContexts(['url.path']);
while (count($path_elements) > 1) {
array_pop($path_elements);
// Copy the path elements for up-casting.
$route_request = $this->getRequestForPath('/' . implode('/', $path_elements), $exclude);
if ($route_request) {
$route_match = RouteMatch::createFromRequest($route_request);
- $access = $this->accessManager->check($route_match, $this->currentUser);
- if ($access) {
+ $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
+ // The set of breadcrumb links depends on the access result, so merge
+ // the access result's cacheability metadata.
+ $breadcrumb = $breadcrumb->merge(CacheableMetadata::createFromObject($access));
+ if ($access->isAllowed()) {
$title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());
- }
- if ($access) {
if (!isset($title)) {
// Fallback to using the raw path component as the title if the
// route is missing a _title or _title_callback attribute.
@@ -165,7 +172,8 @@ public function build(RouteMatchInterface $route_match) {
// Add the Home link, except for the front page.
$links[] = Link::createFromRoute($this->t('Home'), '');
}
- return array_reverse($links);
+
+ return $breadcrumb->setLinks(array_reverse($links));
}
/**
diff --git a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
index c7629f0..40da616 100644
--- a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
@@ -77,20 +77,13 @@ public function build() {
$breadcrumb = $this->breadcrumbManager->build($this->routeMatch);
if (!empty($breadcrumb)) {
// $breadcrumb is expected to be an array of rendered breadcrumb links.
- return array(
+ $build = [
'#theme' => 'breadcrumb',
- '#links' => $breadcrumb,
- );
+ '#links' => $breadcrumb->getLinks(),
+ ];
+ $breadcrumb->applyTo($build);
+ return $build;
}
}
- /**
- * {@inheritdoc}
- *
- * @todo Make cacheable in https://www.drupal.org/node/2483183
- */
- public function getCacheMaxAge() {
- return 0;
- }
-
}
diff --git a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
index 0a00029..052f9f6 100644
--- a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
+++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
@@ -8,11 +8,12 @@
namespace Drupal\Tests\system\Unit\Breadcrumbs;
use Drupal\Core\Link;
-use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultAllowed;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Url;
use Drupal\Core\Utility\LinkGeneratorInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\system\PathBasedBreadcrumbBuilder;
use Drupal\Tests\UnitTestCase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -130,7 +131,7 @@ public function testBuildOnFrontpage() {
->will($this->returnValue('/'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(), $links);
+ $this->assertEquals([], $links->getLinks());
}
/**
@@ -144,7 +145,7 @@ public function testBuildWithOnePathElement() {
->will($this->returnValue('/example'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(0 => new Link('Home', new Url(''))), $links);
+ $this->assertEquals([0 => new Link('Home', new Url(''))], $links->getLinks());
}
/**
@@ -176,7 +177,7 @@ public function testBuildWithTwoPathElements() {
$this->setupAccessManagerToAllow();
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(0 => new Link('Home', new Url('')), 1 => new Link('Example', new Url('example'))), $links);
+ $this->assertEquals([0 => new Link('Home', new Url('')), 1 => new Link('Example', new Url('example'))], $links->getLinks());
}
/**
@@ -216,11 +217,11 @@ public function testBuildWithThreePathElements() {
$this->setupAccessManagerToAllow();
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(
+ $this->assertEquals([
new Link('Home', new Url('')),
new Link('Example', new Url('example')),
new Link('Bar', new Url('example_bar')),
- ), $links);
+ ], $links->getLinks());
}
/**
@@ -244,7 +245,7 @@ public function testBuildWithException($exception_class, $exception_argument) {
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
// No path matched, though at least the frontpage is displayed.
- $this->assertEquals(array(0 => new Link('Home', new Url(''))), $links);
+ $this->assertEquals([0 => new Link('Home', new Url(''))], $links->getLinks());
}
/**
@@ -285,7 +286,7 @@ public function testBuildWithNonProcessedPath() {
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
// No path matched, though at least the frontpage is displayed.
- $this->assertEquals(array(0 => new Link('Home', new Url(''))), $links);
+ $this->assertEquals([0 => new Link('Home', new Url(''))], $links->getLinks());
}
/**
@@ -330,7 +331,7 @@ public function testBuildWithUserPath() {
->will($this->returnValue('Admin'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(0 => new Link('Home', new Url('')), 1 => new Link('Admin', new Url('user_page'))), $links);
+ $this->assertEquals([0 => new Link('Home', new Url('')), 1 => new Link('Admin', new Url('user_page'))], $links->getLinks());
}
/**
@@ -339,7 +340,7 @@ public function testBuildWithUserPath() {
public function setupAccessManagerToAllow() {
$this->accessManager->expects($this->any())
->method('check')
- ->willReturn(TRUE);
+ ->willReturn(new AccessResultAllowed());
}
protected function setupStubPathProcessor() {
diff --git a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
index c2e47a1..8f4fc26 100644
--- a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
+++ b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
namespace Drupal\taxonomy;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
@@ -67,9 +68,14 @@ public function build(RouteMatchInterface $route_match) {
$breadcrumb[] = Link::createFromRoute($term->getName(), 'entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
}
$breadcrumb[] = Link::createFromRoute($this->t('Home'), '');
- $breadcrumb = array_reverse($breadcrumb);
+ $links = array_reverse($breadcrumb);
- return $breadcrumb;
+ $breadcrumb = new Breadcrumb();
+ // This breadcrumb builder is based on a route parameter, and hence it
+ // depends on the 'route' cache context.
+ $breadcrumb->setCacheContexts(['route']);
+
+ return $breadcrumb->setLinks($links);
}
}
diff --git a/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php b/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php
index a2cbbf0..1e21ad9 100644
--- a/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Breadcrumb/BreadcrumbManagerTest.php
@@ -7,8 +7,10 @@
namespace Drupal\Tests\Core\Breadcrumb;
+use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbManager;
use Drupal\Tests\UnitTestCase;
+use PHPUnit_Framework_Assert as Assert;
/**
* @coversDefaultClass \Drupal\Core\Breadcrumb\BreadcrumbManager
@@ -17,6 +19,13 @@
class BreadcrumbManagerTest extends UnitTestCase {
/**
+ * The breadcrumb object.
+ *
+ * @var \Drupal\Core\Breadcrumb\Breadcrumb
+ */
+ protected $breadcrumb;
+
+ /**
* The tested breadcrumb manager.
*
* @var \Drupal\Core\Breadcrumb\BreadcrumbManager
@@ -36,6 +45,7 @@ class BreadcrumbManagerTest extends UnitTestCase {
protected function setUp() {
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->breadcrumbManager = new BreadcrumbManager($this->moduleHandler);
+ $this->breadcrumb = new Breadcrumb();
}
/**
@@ -43,7 +53,7 @@ protected function setUp() {
*/
public function testBuildWithoutBuilder() {
$result = $this->breadcrumbManager->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
- $this->assertEquals(array(), $result);
+ $this->assertEquals(array(), $result->getLinks());
}
/**
@@ -51,7 +61,8 @@ public function testBuildWithoutBuilder() {
*/
public function testBuildWithSingleBuilder() {
$builder = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
- $breadcrumb = array('Test');
+ $links = array('Test');
+ $this->breadcrumb->setLinks($links);
$builder->expects($this->once())
->method('applies')
@@ -59,17 +70,24 @@ public function testBuildWithSingleBuilder() {
$builder->expects($this->once())
->method('build')
- ->will($this->returnValue($breadcrumb));
+ ->willReturn($this->breadcrumb);
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
- ->with('system_breadcrumb', $breadcrumb, $route_match, array('builder' => $builder));
+ ->willReturnCallback(function($type, $data, $context1, $context2) use ($route_match, $links, $builder) {
+ Assert::assertEquals($type, 'system_breadcrumb', 'SingleTest: Module handler expected alter call to system_breadcrumb');
+ $extracted_links = $data->getLinks();
+ Assert::assertEquals($extracted_links, $links, 'SingleTest: Module handler was passed the correct link');
+ Assert::assertEquals($context1, $route_match, 'SingleTest: Module handler was passed route_match.');
+ Assert::assertEquals($context2, array('builder' => $builder), 'SingleTest: Module handler received the builder context');
+ }
+ );
$this->breadcrumbManager->addBuilder($builder, 0);
$result = $this->breadcrumbManager->build($route_match);
- $this->assertEquals($breadcrumb, $result);
+ $this->assertEquals($links, $result->getLinks());
}
/**
@@ -83,25 +101,33 @@ public function testBuildWithMultipleApplyingBuilders() {
->method('build');
$builder2 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
- $breadcrumb2 = array('Test2');
+ $links2 = array('Test2');
+ $this->breadcrumb->setLinks($links2);
$builder2->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder2->expects($this->once())
->method('build')
- ->will($this->returnValue($breadcrumb2));
+ ->willReturn($this->breadcrumb);
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
- ->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2));
+ ->willReturnCallback(function($type, $data, $context1, $context2) use ($links2, $route_match, $builder2) {
+ Assert::assertEquals($type, 'system_breadcrumb', 'MultipleApplying: Module handler expected alter call to system_breadcrumb');
+ $extracted_links = $data->getLinks();
+ Assert::assertEquals($extracted_links, $links2, 'MultipleApplying: Module handler was passed the correct link');
+ Assert::assertEquals($context1, $route_match, 'MultipleApplying: Module handler was passed route_match.');
+ Assert::assertEquals($context2, array('builder' => $builder2), 'MultipleApplying: Module handler received the builder context');
+ }
+ );
$this->breadcrumbManager->addBuilder($builder1, 0);
$this->breadcrumbManager->addBuilder($builder2, 10);
$result = $this->breadcrumbManager->build($route_match);
- $this->assertEquals($breadcrumb2, $result);
+ $this->assertEquals($links2, $result->getLinks());
}
/**
@@ -116,25 +142,33 @@ public function testBuildWithOneNotApplyingBuilders() {
->method('build');
$builder2 = $this->getMock('Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface');
- $breadcrumb2 = array('Test2');
+ $links2 = array('Test2');
+ $this->breadcrumb->setLinks($links2);
$builder2->expects($this->once())
->method('applies')
->will($this->returnValue(TRUE));
$builder2->expects($this->once())
->method('build')
- ->will($this->returnValue($breadcrumb2));
+ ->willReturn($this->breadcrumb);
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->moduleHandler->expects($this->once())
->method('alter')
- ->with('system_breadcrumb', $breadcrumb2, $route_match, array('builder' => $builder2));
+ ->willReturnCallback(function($type, $data, $context1, $context2) use ($links2, $route_match, $builder2) {
+ Assert::assertEquals($type, 'system_breadcrumb', 'NotApplying: Module handler expected alter call to system_breadcrumb');
+ $extracted_links = $data->getLinks();
+ Assert::assertEquals($extracted_links, $links2, 'NotApplying: Module handler was passed the correct link');
+ Assert::assertEquals($context1, $route_match, 'NotApplying: Module handler was passed route_match.');
+ Assert::assertEquals($context2, array('builder' => $builder2), 'NotApplying: Module handler received the builder context');
+ }
+ );
$this->breadcrumbManager->addBuilder($builder1, 10);
$this->breadcrumbManager->addBuilder($builder2, 0);
$result = $this->breadcrumbManager->build($route_match);
- $this->assertEquals($breadcrumb2, $result);
+ $this->assertEquals($links2, $result->getLinks());
}
/**