vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php line 71

Open in your IDE?
  1. <?php
  2. namespace Knp\Menu\Twig;
  3. use Knp\Menu\ItemInterface;
  4. use Knp\Menu\Matcher\MatcherInterface;
  5. use Knp\Menu\Provider\MenuProviderInterface;
  6. use Knp\Menu\Renderer\RendererProviderInterface;
  7. use Knp\Menu\Util\MenuManipulator;
  8. /**
  9.  * Helper class containing logic to retrieve and render menus from templating engines
  10.  */
  11. class Helper
  12. {
  13.     public function __construct(
  14.         private RendererProviderInterface $rendererProvider,
  15.         private ?MenuProviderInterface $menuProvider null,
  16.         private ?MenuManipulator $menuManipulator null,
  17.         private ?MatcherInterface $matcher null
  18.     ) {
  19.     }
  20.     /**
  21.      * Retrieves item in the menu, eventually using the menu provider.
  22.      *
  23.      * @param ItemInterface|string $menu
  24.      * @param array<int, string>   $path
  25.      * @param array<string, mixed> $options
  26.      *
  27.      * @throws \BadMethodCallException   when there is no menu provider and the menu is given by name
  28.      * @throws \LogicException
  29.      * @throws \InvalidArgumentException when the path is invalid
  30.      */
  31.     public function get($menu, array $path = [], array $options = []): ItemInterface
  32.     {
  33.         if (!$menu instanceof ItemInterface) {
  34.             if (null === $this->menuProvider) {
  35.                 throw new \BadMethodCallException('A menu provider must be set to retrieve a menu');
  36.             }
  37.             $menuName $menu;
  38.             $menu $this->menuProvider->get($menuName$options);
  39.         }
  40.         foreach ($path as $child) {
  41.             $menu $menu->getChild($child);
  42.             if (null === $menu) {
  43.                 throw new \InvalidArgumentException(\sprintf('The menu has no child named "%s"'$child));
  44.             }
  45.         }
  46.         return $menu;
  47.     }
  48.     /**
  49.      * Renders a menu with the specified renderer.
  50.      *
  51.      * If the argument is an array, it will follow the path in the tree to
  52.      * get the needed item. The first element of the array is the whole menu.
  53.      * If the menu is a string instead of an ItemInterface, the provider
  54.      * will be used.
  55.      *
  56.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  57.      * @param array<string, mixed>                             $options
  58.      *
  59.      * @throws \InvalidArgumentException
  60.      */
  61.     public function render($menu, array $options = [], ?string $renderer null): string
  62.     {
  63.         $menu $this->castMenu($menu);
  64.         return $this->rendererProvider->get($renderer)->render($menu$options);
  65.     }
  66.     /**
  67.      * Renders an array ready to be used for breadcrumbs.
  68.      *
  69.      * Each element in the array will be an array with 3 keys:
  70.      * - `label` containing the label of the item
  71.      * - `uri` containing the url of the item (may be `null`)
  72.      * - `item` containing the original item (may be `null` for the extra items)
  73.      *
  74.      * The subItem can be one of the following forms
  75.      *   * 'subItem'
  76.      *   * ItemInterface object
  77.      *   * ['subItem' => '@homepage']
  78.      *   * ['subItem1', 'subItem2']
  79.      *   * [['label' => 'subItem1', 'uri' => '@homepage'], ['label' => 'subItem2']]
  80.      *
  81.      * @param mixed $menu
  82.      * @param mixed $subItem A string or array to append onto the end of the array
  83.      *
  84.      * @phpstan-param string|ItemInterface|array<int|string, string|int|float|null|array{label: string, uri: string|null, item: ItemInterface|null}|ItemInterface>|\Traversable<string|int|float|null|array{label: string, uri: string|null, item: ItemInterface|null}|ItemInterface> $subItem
  85.      *
  86.      * @return array<int, array<string, mixed>>
  87.      *
  88.      * @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
  89.      */
  90.     public function getBreadcrumbsArray($menu$subItem null): array
  91.     {
  92.         if (null === $this->menuManipulator) {
  93.             throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
  94.         }
  95.         $menu $this->castMenu($menu);
  96.         return $this->menuManipulator->getBreadcrumbsArray($menu$subItem);
  97.     }
  98.     /**
  99.      * Returns the current item of a menu.
  100.      *
  101.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  102.      */
  103.     public function getCurrentItem($menu): ?ItemInterface
  104.     {
  105.         $menu $this->castMenu($menu);
  106.         return $this->retrieveCurrentItem($menu);
  107.     }
  108.     /**
  109.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  110.      */
  111.     private function castMenu($menu): ItemInterface
  112.     {
  113.         if (!$menu instanceof ItemInterface) {
  114.             $path = [];
  115.             if (\is_array($menu)) {
  116.                 if (empty($menu)) {
  117.                     throw new \InvalidArgumentException('The array cannot be empty');
  118.                 }
  119.                 $path $menu;
  120.                 $menu \array_shift($path);
  121.             }
  122.             return $this->get($menu$path);
  123.         }
  124.         return $menu;
  125.     }
  126.     private function retrieveCurrentItem(ItemInterface $item): ?ItemInterface
  127.     {
  128.         if (null === $this->matcher) {
  129.             throw new \BadMethodCallException('The matcher must be set to get the current item of a menu');
  130.         }
  131.         if ($this->matcher->isCurrent($item)) {
  132.             return $item;
  133.         }
  134.         if ($this->matcher->isAncestor($item)) {
  135.             foreach ($item->getChildren() as $child) {
  136.                 $currentItem $this->retrieveCurrentItem($child);
  137.                 if (null !== $currentItem) {
  138.                     return $currentItem;
  139.                 }
  140.             }
  141.         }
  142.         return null;
  143.     }
  144. }