association-1.0.0-alpha2/modules/association_menu/src/AssociationMenuBuilder.php
modules/association_menu/src/AssociationMenuBuilder.php
<?php namespace Drupal\association_menu; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Template\Attribute; /** * Builds the association menu data into renderable menus for display. */ class AssociationMenuBuilder implements AssociationMenuBuilderInterface { /** * The default route match to use when determining the menu active trail. * * @var \Drupal\Core\Routing\RouteMatchInterface */ protected $routeMatch; /** * Creates a new instance of the AssociationMenuBuilder class. * * @param \Drupal\Core\Routing\RouteMatchInterface $current_route_match * The default route match to use when determining the menu active trail. */ public function __construct(RouteMatchInterface $current_route_match) { $this->routeMatch = $current_route_match; } /** * {@inheritdoc} */ public function buildMenu(array $menu, RouteMatchInterface $route_match = NULL): array { $build = [ '#theme' => 'menu', '#menu_name' => 'association_nav:' . $menu['id'], '#items' => $this->buildMenuItems($menu['items'], $route_match), ]; // Apply caching from the menu data if the cache metadata exists. if (isset($menu['cache']) && $menu['cache'] instanceof CacheableMetadata) { $menu['cache']->applyTo($build); } return $build; } /** * {@inheritdoc} */ public function buildMenuItems(array $menu_items, RouteMatchInterface $route_match = NULL): array { $routeMatch = $route_match ?? $this->routeMatch; $tree = []; $items = []; foreach ($menu_items as $id => $item) { $parentId = $item->getParentId(); if (!$parentId) { // Root level menu items. $tree[$id] = $item; } elseif (isset($menu_items[$parentId])) { // Found parent item, append as child item. Menu storage handler // loads from the datasource weight sorted already. $menu_items[$parentId]->getChildren()[$id] = $item; } // Else parent item is disabled, or is missing. This will cause children // items to not appear. It might be possible to elevate items, but // this might have unintended consequences or lose intended structure. } // Transform the menu data objects into the renderable links array. foreach ($tree as $id => $item) { if ($processed = $this->buildItem($item, $routeMatch)) { $items[$id] = $processed; } } return $items; } /** * Transform menu objects into renderable menu link items recursively. * * Transform the loaded tree menu objects from AssociationMenuStorage to menu * items ready for use with the menu.html.twig template recursively. Menu * items that are not expanded or are not accessible, will not render their * child items. * * @param \Drupal\association_menu\MenuItemInterface $item * The loaded association menu storage item, with children attached. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The route match to use to determine the active trail. * @param int $max_depth * The maximum depth that the menu navigation should build to. * @param int $depth * The current menu depth the menu items are currently being built. * * @return array|false * Menu item sub-tree for the menu item data passed. This should be a * renderable array ready to be used with menu.html.twig or FALSE if menu * item cannot be display or access to the item link is denied. */ protected function buildItem(MenuItemInterface $item, RouteMatchInterface $route_match, $max_depth = self::MAX_MENU_DEPTH, $depth = 0) { if (!$item->hasAccess()) { return FALSE; } $build = [ 'title' => $item->getTitle(), 'url' => $item->getUrl(), 'attributes' => new Attribute(), 'in_active_trail' => FALSE, 'is_expanded' => $item->isExpanded(), 'is_collapsed' => FALSE, 'below' => [], ]; if ($children = $item->getChildren()) { if (!empty($build['is_expanded']) && $depth < $max_depth) { foreach ($children as $id => $child) { $subItem = $this->buildItem($child, $route_match, $max_depth, $depth + 1); if ($subItem) { $build['below'][$id] = $subItem; $build['in_active_trail'] |= $subItem['in_active_trail']; } } } else { // There are children, but the parent menu is not expanded. $build['is_collapsed'] = TRUE; } } // If not already selected as in the active trail by one of its descendents // check if this URL itself matches the current route. if (empty($build['in_active_trail']) && $build['url']->isRouted()) { $build['in_active_trail'] = $build['url']->getRouteName() === $route_match->getRouteName() && $build['url']->getRouteParameters() == $route_match->getParameters(); } return $build; } }