Commit 11765277 authored by webchick's avatar webchick

Issue #2292025 by tim.plunkett: Convert BreadcrumbBuilderInterface applies()...

Issue #2292025 by tim.plunkett: Convert BreadcrumbBuilderInterface applies() and build() to use RouteMatch.
parent b6a4ff8a
......@@ -2150,7 +2150,7 @@ function template_preprocess_page(&$variables) {
if (!defined('MAINTENANCE_MODE')) {
$variables['breadcrumb'] = array(
'#theme' => 'breadcrumb',
'#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::request()->attributes->all()),
'#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::routeMatch()),
);
}
}
......
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Breadcrumb;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Defines an interface for classes that build breadcrumbs.
*/
......@@ -15,25 +17,25 @@ interface BreadcrumbBuilderInterface {
/**
* Whether this breadcrumb builder should be used to build the breadcrumb.
*
* @param array $attributes
* Attributes representing the current page.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*
* @return bool
* TRUE if this builder should be used or FALSE to let other builders
* decide.
*/
public function applies(array $attributes);
public function applies(RouteMatchInterface $route_match);
/**
* Builds the breadcrumb.
*
* @param array $attributes
* Attributes representing the current page.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*
* @return array
* An array of HTML links for the breadcrumb. Returning an empty array will
* suppress all breadcrumbs.
*/
public function build(array $attributes);
public function build(RouteMatchInterface $route_match);
}
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\String;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Provides a breadcrumb manager.
......@@ -41,7 +42,7 @@ class BreadcrumbManager implements ChainBreadcrumbBuilderInterface {
*
* Set to NULL if the array needs to be re-calculated.
*
* @var array|null
* @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface[]|null
*/
protected $sortedBuilders;
......@@ -67,25 +68,25 @@ public function addBuilder(BreadcrumbBuilderInterface $builder, $priority) {
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
public function applies(RouteMatchInterface $route_match) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
public function build(RouteMatchInterface $route_match) {
$breadcrumb = array();
$context = array('builder' => NULL);
// Call the build method of registered breadcrumb builders,
// until one of them returns an array.
foreach ($this->getSortedBuilders() as $builder) {
if (!$builder->applies($attributes)) {
if (!$builder->applies($route_match)) {
// The builder does not apply, so we continue with the other builders.
continue;
}
$build = $builder->build($attributes);
$build = $builder->build($route_match);
if (is_array($build)) {
// The builder returned an array of breadcrumb links.
......@@ -98,7 +99,7 @@ public function build(array $attributes) {
}
}
// Allow modules to alter the breadcrumb.
$this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $attributes, $context);
$this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context);
// Fall back to an empty breadcrumb.
return $breadcrumb;
}
......@@ -106,7 +107,7 @@ public function build(array $attributes) {
/**
* Returns the sorted array of breadcrumb builders.
*
* @return array
* @return \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface[]
* An array of breadcrumb builder objects.
*/
protected function getSortedBuilders() {
......
......@@ -10,6 +10,7 @@
use Drupal\Core\Access\AccessManager;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
......@@ -58,19 +59,18 @@ public function __construct(EntityManagerInterface $entity_manager, AccessManage
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
return !empty($attributes['node'])
&& ($attributes['node'] instanceof NodeInterface)
&& !empty($attributes['node']->book);
public function applies(RouteMatchInterface $route_match) {
$node = $route_match->getParameter('node');
return $node instanceof NodeInterface && !empty($node->book);
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
public function build(RouteMatchInterface $route_match) {
$book_nids = array();
$links = array($this->l($this->t('Home'), '<front>'));
$book = $attributes['node']->book;
$book = $route_match->getParameter('node')->book;
$depth = 1;
// We skip the current node.
while (!empty($book['p' . ($depth + 1)])) {
......
......@@ -9,7 +9,7 @@
use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase;
use Drupal\Core\Entity\EntityManagerInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Class to define the comment breadcrumb builder.
......@@ -36,23 +36,23 @@ public function __construct(EntityManagerInterface $entity_manager) {
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
return isset($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'comment.reply'
&& isset($attributes['entity_type'])
&& isset($attributes['entity_id'])
&& isset($attributes['field_name']);
public function applies(RouteMatchInterface $route_match) {
return $route_match->getRouteName() == 'comment.reply'
&& $route_match->getParameter('entity_type')
&& $route_match->getParameter('entity_id')
&& $route_match->getParameter('field_name');
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
public function build(RouteMatchInterface $route_match) {
$breadcrumb = array();
$breadcrumb[] = $this->l($this->t('Home'), '<front>');
$entity = $this->entityManager
->getStorage($attributes['entity_type'])
->load($attributes['entity_id']);
->getStorage($route_match->getParameter('entity_type'))
->load($route_match->getParameter('entity_id'));
$breadcrumb[] = \Drupal::linkGenerator()->generateFromUrl($entity->label(), $entity->urlInfo());
return $breadcrumb;
}
......
......@@ -10,6 +10,7 @@
use Drupal\Core\Breadcrumb\BreadcrumbBuilderBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\forum\ForumManagerInterface;
/**
......@@ -60,7 +61,7 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
public function build(RouteMatchInterface $route_match) {
$breadcrumb[] = $this->l($this->t('Home'), '<front>');
$vocabulary = $this->entityManager
......
......@@ -7,7 +7,7 @@
namespace Drupal\forum\Breadcrumb;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Provides a breadcrumb builder base class for forum listing pages.
......@@ -17,20 +17,18 @@ class ForumListingBreadcrumbBuilder extends ForumBreadcrumbBuilderBase {
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
return !empty($attributes[RouteObjectInterface::ROUTE_NAME])
&& $attributes[RouteObjectInterface::ROUTE_NAME] == 'forum.page'
&& isset($attributes['taxonomy_term']);
public function applies(RouteMatchInterface $route_match) {
return $route_match->getRouteName() == 'forum.page' && $route_match->getParameter('taxonomy_term');
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
$breadcrumb = parent::build($attributes);
public function build(RouteMatchInterface $route_match) {
$breadcrumb = parent::build($route_match);
// Add all parent forums to breadcrumbs.
$term_id = $attributes['taxonomy_term']->id();
$term_id = $route_match->getParameter('taxonomy_term')->id();
$parents = $this->forumManager->getParents($term_id);
if ($parents) {
foreach (array_reverse($parents) as $parent) {
......
......@@ -7,7 +7,7 @@
namespace Drupal\forum\Breadcrumb;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Breadcrumb builder for forum nodes.
......@@ -17,20 +17,19 @@ class ForumNodeBreadcrumbBuilder extends ForumBreadcrumbBuilderBase {
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
return !empty($attributes[RouteObjectInterface::ROUTE_NAME])
&& $attributes[RouteObjectInterface::ROUTE_NAME] == 'node.view'
&& isset($attributes['node'])
&& $this->forumManager->checkNodeType($attributes['node']);
public function applies(RouteMatchInterface $route_match) {
return $route_match->getRouteName() == 'node.view'
&& $route_match->getParameter('node')
&& $this->forumManager->checkNodeType($route_match->getParameter('node'));
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
$breadcrumb = parent::build($attributes);
public function build(RouteMatchInterface $route_match) {
$breadcrumb = parent::build($route_match);
$parents = $this->forumManager->getParents($attributes['node']->forum_tid);
$parents = $this->forumManager->getParents($route_match->getParameter('node')->forum_tid);
if ($parents) {
$parents = array_reverse($parents);
foreach ($parents as $parent) {
......
......@@ -147,7 +147,7 @@ public function testBuild() {
$property->setValue($breadcrumb_builder, $link_generator);
// Our empty data set.
$attributes = array();
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
// Expected result set.
$expected = array(
......@@ -156,7 +156,7 @@ public function testBuild() {
);
// And finally, the test.
$this->assertSame($expected, $breadcrumb_builder->build($attributes));
$this->assertSame($expected, $breadcrumb_builder->build($route_match));
}
}
......@@ -37,13 +37,15 @@ public static function getInfo() {
*
* @param bool $expected
* ForumListingBreadcrumbBuilder::applies() expected result.
* @param array $attributes
* ForumListingBreadcrumbBuilder::applies() $attributes parameter.
* @param string|null $route_name
* (optional) A route name.
* @param array $parameter_map
* (optional) An array of parameter names and values.
*
* @dataProvider providerTestApplies
* @covers ::applies
*/
public function testApplies($expected, $attributes) {
public function testApplies($expected, $route_name = NULL, $parameter_map = array()) {
// Make some test doubles.
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$config_factory = $this->getConfigFactoryStub(array());
......@@ -59,7 +61,15 @@ public function testApplies($expected, $attributes) {
->setMethods(NULL)
->getMock();
$this->assertEquals($expected, $builder->applies($attributes));
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->once())
->method('getRouteName')
->will($this->returnValue($route_name));
$route_match->expects($this->any())
->method('getParameter')
->will($this->returnValueMap($parameter_map));
$this->assertEquals($expected, $builder->applies($route_match));
}
/**
......@@ -79,33 +89,24 @@ public function providerTestApplies() {
return array(
array(
FALSE,
array(),
),
array(
FALSE,
array(
RouteObjectInterface::ROUTE_NAME => 'NOT.forum.page',
),
'NOT.forum.page',
),
array(
FALSE,
array(
RouteObjectInterface::ROUTE_NAME => 'forum.page',
),
'forum.page',
),
array(
TRUE,
array(
RouteObjectInterface::ROUTE_NAME => 'forum.page',
'taxonomy_term' => 'anything',
),
'forum.page',
array(array('taxonomy_term', 'anything')),
),
array(
TRUE,
array(
RouteObjectInterface::ROUTE_NAME => 'forum.page',
'taxonomy_term' => $mock_term,
),
'forum.page',
array(array('taxonomy_term', $mock_term)),
),
);
}
......@@ -212,9 +213,11 @@ public function testBuild() {
->will($this->returnValue('You_should_not_see_this'));
// Our data set.
$attributes = array(
'taxonomy_term' => $forum_listing,
);
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->exactly(2))
->method('getParameter')
->with('taxonomy_term')
->will($this->returnValue($forum_listing));
// First test.
$expected1 = array(
......@@ -222,7 +225,7 @@ public function testBuild() {
'Fora_is_the_plural_of_forum',
'Something',
);
$this->assertSame($expected1, $breadcrumb_builder->build($attributes));
$this->assertSame($expected1, $breadcrumb_builder->build($route_match));
// Second test.
$expected2 = array(
......@@ -231,7 +234,7 @@ public function testBuild() {
'Something else',
'Something',
);
$this->assertSame($expected2, $breadcrumb_builder->build($attributes));
$this->assertSame($expected2, $breadcrumb_builder->build($route_match));
}
}
......@@ -37,13 +37,15 @@ public static function getInfo() {
*
* @param bool $expected
* ForumNodeBreadcrumbBuilder::applies() expected result.
* @param array $attributes
* ForumNodeBreadcrumbBuilder::applies() $attributes parameter.
* @param string|null $route_name
* (optional) A route name.
* @param array $parameter_map
* (optional) An array of parameter names and values.
*
* @dataProvider providerTestApplies
* @covers ::applies
*/
public function testApplies($expected, $attributes) {
public function testApplies($expected, $route_name = NULL, $parameter_map = array()) {
// Make some test doubles.
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$config_factory = $this->getConfigFactoryStub(array());
......@@ -65,7 +67,15 @@ public function testApplies($expected, $attributes) {
->setMethods(NULL)
->getMock();
$this->assertEquals($expected, $builder->applies($attributes));
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->once())
->method('getRouteName')
->will($this->returnValue($route_name));
$route_match->expects($this->any())
->method('getParameter')
->will($this->returnValueMap($parameter_map));
$this->assertEquals($expected, $builder->applies($route_match));
}
/**
......@@ -87,33 +97,24 @@ public function providerTestApplies() {
return array(
array(
FALSE,
array(),
),
array(
FALSE,
array(
RouteObjectInterface::ROUTE_NAME => 'NOT.node.view',
),
'NOT.node.view',
),
array(
FALSE,
array(
RouteObjectInterface::ROUTE_NAME => 'node.view',
),
'node.view',
),
array(
FALSE,
array(
RouteObjectInterface::ROUTE_NAME => 'node.view',
'node' => NULL,
),
'node.view',
array(array('node', NULL)),
),
array(
TRUE,
array(
RouteObjectInterface::ROUTE_NAME => 'node.view',
'node' => $mock_node,
),
'node.view',
array(array('node', $mock_node)),
),
);
}
......@@ -216,9 +217,11 @@ public function testBuild() {
->getMock();
// Our data set.
$attributes = array(
'node' => $forum_node,
);
$route_match = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$route_match->expects($this->exactly(2))
->method('getParameter')
->with('node')
->will($this->returnValue($forum_node));
// First test.
$expected1 = array(
......@@ -226,7 +229,7 @@ public function testBuild() {
'Forums',
'Something',
);
$this->assertSame($expected1, $breadcrumb_builder->build($attributes));
$this->assertSame($expected1, $breadcrumb_builder->build($route_match));
// Second test.
$expected2 = array(
......@@ -235,7 +238,7 @@ public function testBuild() {
'Something else',
'Something',
);
$this->assertSame($expected2, $breadcrumb_builder->build($attributes));
$this->assertSame($expected2, $breadcrumb_builder->build($route_match));
}
}
......@@ -5,10 +5,10 @@
* Enables users to create menu links.
*/
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\menu_link\Entity\MenuLink;
use Drupal\menu_link\MenuLinkInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
function menu_link_help($route_name, Request $request) {
......@@ -213,11 +213,10 @@ function menu_link_maintain($module, $op, $link_path, $link_title = NULL) {
/**
* Implements hook_system_breadcrumb_alter().
*/
function menu_link_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) {
function menu_link_system_breadcrumb_alter(array &$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 (!empty($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'menu_ui.link_edit' && !empty($attributes['menu_link'])) {
$menu_link = $attributes['menu_link'];
if (($route_match->getRouteName() == 'menu_ui.link_edit') && $menu_link = $route_match->getParameter('menu_link')) {
if (($menu_link instanceof MenuLinkInterface) && !$menu_link->isNew()) {
// Add a link to the menu admin screen.
$menu = entity_load('menu', $menu_link->menu_name);
......
......@@ -13,6 +13,7 @@
use Drupal\Core\Access\AccessManager;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Utility\Unicode;
use Symfony\Component\HttpFoundation\Request;
......@@ -107,14 +108,14 @@ public function __construct(RequestContext $context, AccessManager $access_manag
/**
* {@inheritdoc}
*/
public function applies(array $attributes) {
public function applies(RouteMatchInterface $route_match) {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function build(array $attributes) {
public function build(RouteMatchInterface $route_match) {
$links = array();
// General path-based breadcrumbs. Use the actual request path, prior to
......
......@@ -8,6 +8,10 @@
namespace Drupal\system\Plugin\Block;
use Drupal\block\BlockBase;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a block to display the breadcrumbs.
......@@ -17,15 +21,60 @@
* admin_label = @Translation("Breadcrumbs")
* )
*/
class SystemBreadcrumbBlock extends BlockBase {
class SystemBreadcrumbBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The breadcrumb manager.
*
* @var \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
*/
protected $breadcrumbManager;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new SystemBreadcrumbBlock object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface $breadcrumb_manager
* The breadcrumb manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, BreadcrumbBuilderInterface $breadcrumb_manager, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->breadcrumbManager = $breadcrumb_manager;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('breadcrumb'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
public function build() {
$breadcrumb_manager = \Drupal::service('breadcrumb');
$request = \Drupal::service('request');
$breadcrumb = $breadcrumb_manager->build($request->attributes->all());
$breadcrumb = $this->breadcrumbManager->build($this->routeMatch);
if (!empty($breadcrumb)) {
// $breadcrumb is expected to be an array of rendered breadcrumb links.
return array(
......
......@@ -895,16 +895,15 @@ function hook_module_implements_alter(&$implementations, $hook) {
* @code
* array('<a href="/">Home</a>');
* @endcode
* @param array $attributes
* Attributes representing the current page, coming from
* \Drupal::request()->attributes.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param array $context
* May include the following key:
* - builder: the instance of
* \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface that constructed this
* breadcrumb, or NULL if no builder acted based on the current attributes.
*/
function hook_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) {
function hook_system_breadcrumb_alter(array &$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');
}
......
......@@ -140,7 +140,7 @@ public function testBuildOnFrontpage() {
->method('getPathInfo')
->will($this->returnValue('/'));
$links = $this->builder->build(array());
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(), $links);
}
......@@ -156,7 +156,7 @@ public function testBuildWithOnePathElement() {
$this->setupLinkGeneratorWithFrontpage();
$links = $this->builder->build(array());
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => '<a href="/">Home</a>'), $links);
}
......@@ -198,7 +198,7 @@ public function testBuildWithTwoPathElements() {
->will($this->returnValue($link_front));
$this->setupAccessManagerWithTrue();
$links = $this->builder->build(array());
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => '<a href="/">Home</a>', 1 => $link_example), $links);
}
......@@ -254,7 +254,7 @@ public function testBuildWithThreePathElements() {
->will($this->returnValue($link_front));
$this->setupAccessManagerWithTrue();
$links = $this->builder->build(array());
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => '<a href="/">Home</a>', 1 => $link_example, 2 => $link_example_bar), $links);
}
......@@ -277,7 +277,7 @@ public function testBuildWithException($exception_class, $exception_argument) {
->will($this->throwException(new $exception_class($exception_argument)));
$this->setupLinkGeneratorWithFrontpage();
$links = $this->builder->build(array());
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
// No path matched, though at least the frontpage is displayed.
$this->assertEquals(array(0 => '<a href="/">Home</a>'), $links);
......@@ -319,7 +319,7 @@ public function testBuildWithNonProcessedPath() {