*/ private iterable $localMenuBuilders, ) {} public function getMenuFor($menuId, array $parameters = []): ItemInterface { $routes = $this->getRoutesForInternal($menuId, $parameters); /** @var MenuItem $menu */ $menu = $this->menuFactory->createItem($menuId); // build menu from routes foreach ($routes as $order => $route) { $menu->addChild($this->translator->trans($route['label']), [ 'route' => $route['key'], 'routeParameters' => $parameters['args'], 'order' => $order, ]) ->setExtras([ // 'icon' => $route['icon'], // sf4 check: commented to avoid error: `An exception has been thrown during the rendering of a template ("Notice: Undefined index: icon").` 'order' => $order, ]); } foreach ($this->localMenuBuilders as $builder) { if (in_array($menuId, $builder::getMenuIds(), true)) { $builder->buildMenu($menuId, $menu, $parameters); } } $this->reorderMenu($menu); return $menu; } /** * Return an array of routes added to $menuId, * The array is aimed to build route with MenuTwig. * * @deprecated * * @param array $parameters see https://redmine.champs-libres.coop/issues/179 */ public function getRoutesFor(string $menuId, array $parameters = []): array { return $this->getRoutesForInternal($menuId, $parameters); } private function getRoutesForInternal(string $menuId, array $parameters = []): array { $routes = []; $routeCollection = $this->router->getRouteCollection(); foreach ($routeCollection->all() as $routeKey => $route) { if ($route->hasOption('menus')) { if (\array_key_exists($menuId, $route->getOption('menus'))) { $route = $route->getOption('menus')[$menuId]; $route['key'] = $routeKey; $order = $this->resolveOrder($routes, $route['order']); // we do not want to duplicate order information unset($route['order']); $routes[$order] = $route; } } } ksort($routes); return $routes; } /** * Return true if the menu has at least one builder. * * This function is a helper to determine if the method `getMenuFor` * should be used, or `getRouteFor`. The method `getMenuFor` should be used * if the result is true (it **does** exists at least one menu builder. * * @deprecated */ public function hasLocalMenuBuilder(string $menuId): bool { foreach ($this->localMenuBuilders as $localMenuBuilder) { if (in_array($menuId, $localMenuBuilder::getMenuIds(), true)) { return true; } } return false; } private function reorderMenu(ItemInterface $menu) { $ordered = []; $unordered = []; foreach ($menu->getChildren() as $name => $item) { $order = $item->getExtra('order'); if (null !== $order) { $ordered[$this->resolveOrder($ordered, $order)] = $name; } else { $unordered = $name; } } ksort($ordered); $menus = \array_merge(\array_values($ordered), $unordered); $menu->reorderChildren($menus); } /** * recursive function to resolve the order of a array of routes. * If the order chosen in routing.yml is already in used, find the * first next order available. * * @param array $routes the routes previously added * @param int $order * * @return int */ private function resolveOrder($routes, $order) { if (isset($routes[$order])) { return $this->resolveOrder($routes, $order + 1); } return $order; } }