PageRenderTime 200ms CodeModel.GetById 150ms RepoModel.GetById 1ms app.codeStats 1ms

/web/core/lib/Drupal/Core/Menu/MenuLinkTree.php

https://gitlab.com/mohamed_hussein/prodt
PHP | 307 lines | 137 code | 33 blank | 137 comment | 15 complexity | 8e1ef6599a48688520379c5acd7818e7 MD5 | raw file
  1. <?php
  2. namespace Drupal\Core\Menu;
  3. use Drupal\Component\Utility\NestedArray;
  4. use Drupal\Core\Access\AccessResultInterface;
  5. use Drupal\Core\Cache\CacheableMetadata;
  6. use Drupal\Core\Controller\ControllerResolverInterface;
  7. use Drupal\Core\Routing\PreloadableRouteProviderInterface;
  8. use Drupal\Core\Routing\RouteProviderInterface;
  9. use Drupal\Core\Template\Attribute;
  10. /**
  11. * Implements the loading, transforming and rendering of menu link trees.
  12. */
  13. class MenuLinkTree implements MenuLinkTreeInterface {
  14. /**
  15. * The menu link tree storage.
  16. *
  17. * @var \Drupal\Core\Menu\MenuTreeStorageInterface
  18. */
  19. protected $treeStorage;
  20. /**
  21. * The menu link plugin manager.
  22. *
  23. * @var \Drupal\Core\Menu\MenuLinkManagerInterface
  24. */
  25. protected $menuLinkManager;
  26. /**
  27. * The route provider to load routes by name.
  28. *
  29. * @var \Drupal\Core\Routing\RouteProviderInterface
  30. */
  31. protected $routeProvider;
  32. /**
  33. * The active menu trail service.
  34. *
  35. * @var \Drupal\Core\Menu\MenuActiveTrailInterface
  36. */
  37. protected $menuActiveTrail;
  38. /**
  39. * The controller resolver.
  40. *
  41. * @var \Drupal\Core\Controller\ControllerResolverInterface
  42. */
  43. protected $controllerResolver;
  44. /**
  45. * Constructs a \Drupal\Core\Menu\MenuLinkTree object.
  46. *
  47. * @param \Drupal\Core\Menu\MenuTreeStorageInterface $tree_storage
  48. * The menu link tree storage.
  49. * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
  50. * The menu link plugin manager.
  51. * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
  52. * The route provider to load routes by name.
  53. * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail
  54. * The active menu trail service.
  55. * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
  56. * The controller resolver.
  57. */
  58. public function __construct(MenuTreeStorageInterface $tree_storage, MenuLinkManagerInterface $menu_link_manager, RouteProviderInterface $route_provider, MenuActiveTrailInterface $menu_active_trail, ControllerResolverInterface $controller_resolver) {
  59. $this->treeStorage = $tree_storage;
  60. $this->menuLinkManager = $menu_link_manager;
  61. $this->routeProvider = $route_provider;
  62. $this->menuActiveTrail = $menu_active_trail;
  63. $this->controllerResolver = $controller_resolver;
  64. }
  65. /**
  66. * {@inheritdoc}
  67. */
  68. public function getCurrentRouteMenuTreeParameters($menu_name) {
  69. $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name);
  70. $parameters = new MenuTreeParameters();
  71. $parameters->setActiveTrail($active_trail)
  72. // We want links in the active trail to be expanded.
  73. ->addExpandedParents($active_trail)
  74. // We marked the links in the active trail to be expanded, but we also
  75. // want their descendants that have the "expanded" flag enabled to be
  76. // expanded.
  77. ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail));
  78. return $parameters;
  79. }
  80. /**
  81. * {@inheritdoc}
  82. */
  83. public function load($menu_name, MenuTreeParameters $parameters) {
  84. $data = $this->treeStorage->loadTreeData($menu_name, $parameters);
  85. // Pre-load all the route objects in the tree for access checks.
  86. if ($data['route_names'] && $this->routeProvider instanceof PreloadableRouteProviderInterface) {
  87. $this->routeProvider->getRoutesByNames($data['route_names']);
  88. }
  89. return $this->createInstances($data['tree']);
  90. }
  91. /**
  92. * Returns a tree containing of MenuLinkTreeElement based upon tree data.
  93. *
  94. * This method converts the tree representation as array coming from the tree
  95. * storage to a tree containing a list of MenuLinkTreeElement[].
  96. *
  97. * @param array $data_tree
  98. * The tree data coming from the menu tree storage.
  99. *
  100. * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
  101. * An array containing the elements of a menu tree.
  102. */
  103. protected function createInstances(array $data_tree) {
  104. $tree = [];
  105. foreach ($data_tree as $key => $element) {
  106. $subtree = $this->createInstances($element['subtree']);
  107. // Build a MenuLinkTreeElement out of the menu tree link definition:
  108. // transform the tree link definition into a link definition and store
  109. // tree metadata.
  110. $tree[$key] = new MenuLinkTreeElement(
  111. $this->menuLinkManager->createInstance($element['definition']['id']),
  112. (bool) $element['has_children'],
  113. (int) $element['depth'],
  114. (bool) $element['in_active_trail'],
  115. $subtree
  116. );
  117. }
  118. return $tree;
  119. }
  120. /**
  121. * {@inheritdoc}
  122. */
  123. public function transform(array $tree, array $manipulators) {
  124. foreach ($manipulators as $manipulator) {
  125. $callable = $manipulator['callable'];
  126. $callable = $this->controllerResolver->getControllerFromDefinition($callable);
  127. // Prepare the arguments for the menu tree manipulator callable; the first
  128. // argument is always the menu link tree.
  129. if (isset($manipulator['args'])) {
  130. array_unshift($manipulator['args'], $tree);
  131. $tree = call_user_func_array($callable, $manipulator['args']);
  132. }
  133. else {
  134. $tree = call_user_func($callable, $tree);
  135. }
  136. }
  137. return $tree;
  138. }
  139. /**
  140. * {@inheritdoc}
  141. */
  142. public function build(array $tree) {
  143. $tree_access_cacheability = new CacheableMetadata();
  144. $tree_link_cacheability = new CacheableMetadata();
  145. $items = $this->buildItems($tree, $tree_access_cacheability, $tree_link_cacheability);
  146. $build = [];
  147. // Apply the tree-wide gathered access cacheability metadata and link
  148. // cacheability metadata to the render array. This ensures that the
  149. // rendered menu is varied by the cache contexts that the access results
  150. // and (dynamic) links depended upon, and invalidated by the cache tags
  151. // that may change the values of the access results and links.
  152. $tree_cacheability = $tree_access_cacheability->merge($tree_link_cacheability);
  153. $tree_cacheability->applyTo($build);
  154. if ($items) {
  155. // Make sure Drupal\Core\Render\Element::children() does not re-order the
  156. // links.
  157. $build['#sorted'] = TRUE;
  158. // Get the menu name from the last link.
  159. $item = end($items);
  160. $link = $item['original_link'];
  161. $menu_name = $link->getMenuName();
  162. // Add the theme wrapper for outer markup.
  163. // Allow menu-specific theme overrides.
  164. $build['#theme'] = 'menu__' . strtr($menu_name, '-', '_');
  165. $build['#menu_name'] = $menu_name;
  166. $build['#items'] = $items;
  167. // Set cache tag.
  168. $build['#cache']['tags'][] = 'config:system.menu.' . $menu_name;
  169. }
  170. return $build;
  171. }
  172. /**
  173. * Builds the #items property for a menu tree's renderable array.
  174. *
  175. * Helper function for ::build().
  176. *
  177. * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
  178. * A data structure representing the tree, as returned from
  179. * MenuLinkTreeInterface::load().
  180. * @param \Drupal\Core\Cache\CacheableMetadata &$tree_access_cacheability
  181. * Internal use only. The aggregated cacheability metadata for the access
  182. * results across the entire tree. Used when rendering the root level.
  183. * @param \Drupal\Core\Cache\CacheableMetadata &$tree_link_cacheability
  184. * Internal use only. The aggregated cacheability metadata for the menu
  185. * links across the entire tree. Used when rendering the root level.
  186. *
  187. * @return array
  188. * The value to use for the #items property of a renderable menu.
  189. *
  190. * @throws \DomainException
  191. */
  192. protected function buildItems(array $tree, CacheableMetadata &$tree_access_cacheability, CacheableMetadata &$tree_link_cacheability) {
  193. $items = [];
  194. foreach ($tree as $data) {
  195. /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
  196. $link = $data->link;
  197. // Generally we only deal with visible links, but just in case.
  198. if (!$link->isEnabled()) {
  199. continue;
  200. }
  201. if ($data->access !== NULL && !$data->access instanceof AccessResultInterface) {
  202. throw new \DomainException('MenuLinkTreeElement::access must be either NULL or an AccessResultInterface object.');
  203. }
  204. // Gather the access cacheability of every item in the menu link tree,
  205. // including inaccessible items. This allows us to render cache the menu
  206. // tree, yet still automatically vary the rendered menu by the same cache
  207. // contexts that the access results vary by.
  208. // However, if $data->access is not an AccessResultInterface object, this
  209. // will still render the menu link, because this method does not want to
  210. // require access checking to be able to render a menu tree.
  211. if ($data->access instanceof AccessResultInterface) {
  212. $tree_access_cacheability = $tree_access_cacheability->merge(CacheableMetadata::createFromObject($data->access));
  213. }
  214. // Gather the cacheability of every item in the menu link tree. Some links
  215. // may be dynamic: they may have a dynamic text (e.g. a "Hi, <user>" link
  216. // text, which would vary by 'user' cache context), or a dynamic route
  217. // name or route parameters.
  218. $tree_link_cacheability = $tree_link_cacheability->merge(CacheableMetadata::createFromObject($data->link));
  219. // Only render accessible links.
  220. if ($data->access instanceof AccessResultInterface && !$data->access->isAllowed()) {
  221. continue;
  222. }
  223. $element = [];
  224. // Set a variable for the <li> tag. Only set 'expanded' to true if the
  225. // link also has visible children within the current tree.
  226. $element['is_expanded'] = FALSE;
  227. $element['is_collapsed'] = FALSE;
  228. if ($data->hasChildren && !empty($data->subtree)) {
  229. $element['is_expanded'] = TRUE;
  230. }
  231. elseif ($data->hasChildren) {
  232. $element['is_collapsed'] = TRUE;
  233. }
  234. // Set a helper variable to indicate whether the link is in the active
  235. // trail.
  236. $element['in_active_trail'] = FALSE;
  237. if ($data->inActiveTrail) {
  238. $element['in_active_trail'] = TRUE;
  239. }
  240. // Note: links are rendered in the menu.html.twig template; and they
  241. // automatically bubble their associated cacheability metadata.
  242. $element['attributes'] = new Attribute();
  243. $element['title'] = $link->getTitle();
  244. $element['url'] = $link->getUrlObject();
  245. $element['url']->setOption('set_active_class', TRUE);
  246. $element['below'] = $data->subtree ? $this->buildItems($data->subtree, $tree_access_cacheability, $tree_link_cacheability) : [];
  247. if (isset($data->options)) {
  248. $element['url']->setOptions(NestedArray::mergeDeep($element['url']->getOptions(), $data->options));
  249. }
  250. $element['original_link'] = $link;
  251. // Index using the link's unique ID.
  252. $items[$link->getPluginId()] = $element;
  253. }
  254. return $items;
  255. }
  256. /**
  257. * {@inheritdoc}
  258. */
  259. public function maxDepth() {
  260. return $this->treeStorage->maxDepth();
  261. }
  262. /**
  263. * {@inheritdoc}
  264. */
  265. public function getSubtreeHeight($id) {
  266. return $this->treeStorage->getSubtreeHeight($id);
  267. }
  268. /**
  269. * {@inheritdoc}
  270. */
  271. public function getExpanded($menu_name, array $parents) {
  272. return $this->treeStorage->getExpanded($menu_name, $parents);
  273. }
  274. }