Issue #2224861 by Wim Leers, dawehner, pwolanin: Cache SystemMenuBlock and BookNavigationBlock per active trail (currently cached per URL, this breaks on very long URLs).
parent
3c89fb90c3
commit
8866760fca
|
@ -488,16 +488,9 @@ class BookManager implements BookManagerInterface {
|
|||
'max_depth' => $max_depth,
|
||||
);
|
||||
if ($nid) {
|
||||
// The tree is for a single item, so we need to match the values in its
|
||||
// p columns and 0 (the top level) with the plid values of other links.
|
||||
$parents = array(0);
|
||||
for ($i = 1; $i < static::BOOK_MAX_DEPTH; $i++) {
|
||||
if (!empty($link["p$i"])) {
|
||||
$parents[] = $link["p$i"];
|
||||
}
|
||||
}
|
||||
$tree_parameters['expanded'] = $parents;
|
||||
$tree_parameters['active_trail'] = $parents;
|
||||
$active_trail = $this->getActiveTrailIds($bid, $link);
|
||||
$tree_parameters['expanded'] = $active_trail;
|
||||
$tree_parameters['active_trail'] = $active_trail;
|
||||
$tree_parameters['active_trail'][] = $nid;
|
||||
}
|
||||
|
||||
|
@ -508,6 +501,22 @@ class BookManager implements BookManagerInterface {
|
|||
return $tree[$cid];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getActiveTrailIds($bid, $link) {
|
||||
$nid = isset($link['nid']) ? $link['nid'] : 0;
|
||||
// The tree is for a single item, so we need to match the values in its
|
||||
// p columns and 0 (the top level) with the plid values of other links.
|
||||
$active_trail = array(0);
|
||||
for ($i = 1; $i < static::BOOK_MAX_DEPTH; $i++) {
|
||||
if (!empty($link["p$i"])) {
|
||||
$active_trail[] = $link["p$i"];
|
||||
}
|
||||
}
|
||||
return $active_trail;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -40,6 +40,19 @@ interface BookManagerInterface {
|
|||
*/
|
||||
public function bookTreeAllData($bid, $link = NULL, $max_depth = NULL);
|
||||
|
||||
/**
|
||||
* Gets the active trail IDs for the specified book at the provided path.
|
||||
*
|
||||
* @param string $bid
|
||||
* The Book ID to find links for.
|
||||
* @param array $link
|
||||
* A fully loaded menu link.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the active trail: a list of mlids.
|
||||
*/
|
||||
public function getActiveTrailIds($bid, $link);
|
||||
|
||||
/**
|
||||
* Loads a single book entry.
|
||||
*
|
||||
|
|
|
@ -164,14 +164,30 @@ class BookNavigationBlock extends BlockBase implements ContainerFactoryPluginInt
|
|||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKeys() {
|
||||
// Add a key for the active book trail.
|
||||
$current_bid = 0;
|
||||
if ($node = $this->request->get('node')) {
|
||||
$current_bid = empty($node->book['bid']) ? 0 : $node->book['bid'];
|
||||
}
|
||||
if ($current_bid === 0) {
|
||||
return parent::getCacheKeys();
|
||||
}
|
||||
$active_trail = $this->bookManager->getActiveTrailIds($node->book['bid'], $node->book);
|
||||
$active_trail_key = 'trail.' . implode('|', $active_trail);
|
||||
return array_merge(parent::getCacheKeys(), array($active_trail_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getRequiredCacheContexts() {
|
||||
// The "Book navigation" block must be cached per URL and per role: the
|
||||
// "active" menu link may differ per URL and different roles may have access
|
||||
// to different menu links.
|
||||
return array('cache_context.url', 'cache_context.user.roles');
|
||||
// The "Book navigation" block must be cached per role: different roles may
|
||||
// have access to different menu links.
|
||||
return array('cache_context.user.roles');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -204,8 +204,6 @@ class MenuTree implements MenuTreeInterface {
|
|||
public function buildPageData($menu_name, $max_depth = NULL, $only_active_trail = FALSE) {
|
||||
$language_interface = $this->languageManager->getCurrentLanguage();
|
||||
|
||||
// Check if the active trail has been overridden for this menu tree.
|
||||
$active_path = $this->getPath($menu_name);
|
||||
// Load the request corresponding to the current page.
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
$system_path = NULL;
|
||||
|
@ -247,34 +245,17 @@ class MenuTree implements MenuTreeInterface {
|
|||
'min_depth' => 1,
|
||||
'max_depth' => $max_depth,
|
||||
);
|
||||
// Parent mlids; used both as key and value to ensure uniqueness.
|
||||
// We always want all the top-level links with plid == 0.
|
||||
$active_trail = array(0 => 0);
|
||||
$active_trail = $this->getActiveTrailIds($menu_name);
|
||||
|
||||
// If this page is accessible to the current user, build the tree
|
||||
// parameters accordingly.
|
||||
if ($page_not_403) {
|
||||
// Find a menu link corresponding to the current path. If
|
||||
// $active_path is NULL, let menu_link_get_preferred() determine
|
||||
// the path.
|
||||
if ($active_link = $this->menuLinkGetPreferred($menu_name, $active_path)) {
|
||||
// The active link may only be taken into account to build the
|
||||
// active trail, if it resides in the requested menu.
|
||||
// Otherwise, we'd needlessly re-run _menu_build_tree() queries
|
||||
// for every menu on every page.
|
||||
if ($active_link['menu_name'] == $menu_name) {
|
||||
// Use all the coordinates, except the last one because
|
||||
// there can be no child beyond the last column.
|
||||
for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
|
||||
if ($active_link['p' . $i]) {
|
||||
$active_trail[$active_link['p' . $i]] = $active_link['p' . $i];
|
||||
}
|
||||
}
|
||||
// If we are asked to build links for the active trail only,skip
|
||||
// the entire 'expanded' handling.
|
||||
if ($only_active_trail) {
|
||||
$tree_parameters['only_active_trail'] = TRUE;
|
||||
}
|
||||
// The active trail contains more than only array(0 => 0).
|
||||
if (count($active_trail) > 1) {
|
||||
// If we are asked to build links for the active trail only,skip
|
||||
// the entire 'expanded' handling.
|
||||
if ($only_active_trail) {
|
||||
$tree_parameters['only_active_trail'] = TRUE;
|
||||
}
|
||||
}
|
||||
$parents = $active_trail;
|
||||
|
@ -317,6 +298,39 @@ class MenuTree implements MenuTreeInterface {
|
|||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getActiveTrailIds($menu_name) {
|
||||
// Parent mlids; used both as key and value to ensure uniqueness.
|
||||
// We always want all the top-level links with plid == 0.
|
||||
$active_trail = array(0 => 0);
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
|
||||
// @todo https://drupal.org/node/2068471 is adding support so we can tell
|
||||
// if this is called on a 404/403 page.
|
||||
// Check if the active trail has been overridden for this menu tree.
|
||||
$active_path = $this->getPath($menu_name);
|
||||
// Find a menu link corresponding to the current path. If
|
||||
// $active_path is NULL, let menu_link_get_preferred() determine
|
||||
// the path.
|
||||
if ($active_link = $this->menuLinkGetPreferred($menu_name, $active_path)) {
|
||||
if ($active_link['menu_name'] == $menu_name) {
|
||||
// Use all the coordinates, except the last one because
|
||||
// there can be no child beyond the last column.
|
||||
for ($i = 1; $i < MENU_MAX_DEPTH; $i++) {
|
||||
if ($active_link['p' . $i]) {
|
||||
$active_trail[$active_link['p' . $i]] = $active_link['p' . $i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $active_trail;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -59,6 +59,17 @@ interface MenuTreeInterface {
|
|||
*/
|
||||
public function getPath($menu_name);
|
||||
|
||||
/**
|
||||
* Gets the active trail IDs of the specified menu tree.
|
||||
*
|
||||
* @param string $menu_name
|
||||
* The menu name of the requested tree.
|
||||
*
|
||||
* @return array
|
||||
* An array containing the active trail: a list of mlids.
|
||||
*/
|
||||
public function getActiveTrailIds($menu_name);
|
||||
|
||||
/**
|
||||
* Sorts and returns the built data representing a menu tree.
|
||||
*
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
* Contains \Drupal\menu_link\Tests\MenuTreeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\menu_link\Tests;
|
||||
namespace Drupal\menu_link\Tests {
|
||||
|
||||
use Drupal\menu_link\MenuTree;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
|
@ -272,6 +274,52 @@ class MenuTreeTest extends UnitTestCase {
|
|||
$this->assertEquals($items[4]['mlid'], $tree['50000 2']['below']['50000 3']['below']['50000 4']['link']['mlid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getActiveTrailIds().
|
||||
*
|
||||
* @covers ::getActiveTrailIds()
|
||||
*/
|
||||
public function testGetActiveTrailIds() {
|
||||
$menu_link = array(
|
||||
'mlid' => 10,
|
||||
'route_name' => 'example1',
|
||||
'p1' => 3,
|
||||
'p2' => 2,
|
||||
'p3' => 1,
|
||||
'p4' => 4,
|
||||
'p5' => 9,
|
||||
'p6' => 5,
|
||||
'p7' => 6,
|
||||
'p8' => 7,
|
||||
'p9' => 8,
|
||||
'menu_name' => 'test_menu'
|
||||
);
|
||||
$this->menuTree->setPreferredMenuLink('test_menu', 'test/path', $menu_link);
|
||||
$request = (new Request());
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route');
|
||||
$this->requestStack->push($request);
|
||||
$this->menuTree->setPath('test_menu', 'test/path');
|
||||
|
||||
$trail = $this->menuTree->getActiveTrailIds('test_menu');
|
||||
$this->assertEquals(array(0 => 0, 3 => 3, 2 => 2, 1 => 1, 4 => 4, 9 => 9, 5 => 5, 6 => 6, 7 => 7), $trail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getActiveTrailIds() without preferred link.
|
||||
*
|
||||
* @covers ::getActiveTrailIds()
|
||||
*/
|
||||
public function testGetActiveTrailIdsWithoutPreferredLink() {
|
||||
$request = (new Request());
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route');
|
||||
$this->requestStack->push($request);
|
||||
$this->menuTree->setPath('test_menu', 'test/path');
|
||||
|
||||
$trail = $this->menuTree->getActiveTrailIds('test_menu');
|
||||
$this->assertEquals(array(0 => 0), $trail);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests the output with a single level.
|
||||
*
|
||||
|
@ -369,6 +417,13 @@ class TestMenuTree extends MenuTree {
|
|||
*/
|
||||
public $menuLinkTranslateCallable;
|
||||
|
||||
/**
|
||||
* Stores the preferred menu link per menu and path.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $preferredMenuLink;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -382,6 +437,29 @@ class TestMenuTree extends MenuTree {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function menuLinkGetPreferred($menu_name, $active_path) {
|
||||
return isset($this->preferredMenuLink[$menu_name][$active_path]) ? $this->preferredMenuLink[$menu_name][$active_path] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred menu link.
|
||||
*
|
||||
* @param string $menu_name
|
||||
* The menu name.
|
||||
* @param string $active_path
|
||||
* The active path.
|
||||
* @param array $menu_link
|
||||
* The preferred menu link.
|
||||
*/
|
||||
public function setPreferredMenuLink($menu_name, $active_path, $menu_link) {
|
||||
$this->preferredMenuLink[$menu_name][$active_path] = $menu_link;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
if (!defined('MENU_MAX_DEPTH')) {
|
||||
define('MENU_MAX_DEPTH', 9);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,17 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa
|
|||
return array('cache' => array('max_age' => \Drupal\Core\Cache\Cache::PERMANENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheKeys() {
|
||||
// Add a key for the active menu trail.
|
||||
$menu = $this->getDerivativeId();
|
||||
$active_trail = $this->menuTree->getActiveTrailIds($menu);
|
||||
$active_trail_key = 'trail.' . implode('|', $active_trail);
|
||||
return array_merge(parent::getCacheKeys(), array($active_trail_key));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -101,10 +112,9 @@ class SystemMenuBlock extends BlockBase implements ContainerFactoryPluginInterfa
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getRequiredCacheContexts() {
|
||||
// Menu blocks must be cached per URL and per role: the "active" menu link
|
||||
// may differ per URL and different roles may have access to different menu
|
||||
// links.
|
||||
return array('cache_context.url', 'cache_context.user.roles');
|
||||
// Menu blocks must be cached per role: different roles may have access to
|
||||
// different menu links.
|
||||
return array('cache_context.user.roles');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue