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

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.             if (!$menu instanceof ItemInterface) {
  40.                 throw new \LogicException(\sprintf('The menu "%s" exists, but is not a valid menu item object. Check where you created the menu to be sure it returns an ItemInterface object.'$menuName));
  41.             }
  42.         }
  43.         foreach ($path as $child) {
  44.             $menu $menu->getChild($child);
  45.             if (null === $menu) {
  46.                 throw new \InvalidArgumentException(\sprintf('The menu has no child named "%s"'$child));
  47.             }
  48.         }
  49.         return $menu;
  50.     }
  51.     /**
  52.      * Renders a menu with the specified renderer.
  53.      *
  54.      * If the argument is an array, it will follow the path in the tree to
  55.      * get the needed item. The first element of the array is the whole menu.
  56.      * If the menu is a string instead of an ItemInterface, the provider
  57.      * will be used.
  58.      *
  59.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  60.      * @param array<string, mixed>                             $options
  61.      *
  62.      * @throws \InvalidArgumentException
  63.      */
  64.     public function render($menu, array $options = [], ?string $renderer null): string
  65.     {
  66.         $menu $this->castMenu($menu);
  67.         return $this->rendererProvider->get($renderer)->render($menu$options);
  68.     }
  69.     /**
  70.      * Renders an array ready to be used for breadcrumbs.
  71.      *
  72.      * Each element in the array will be an array with 3 keys:
  73.      * - `label` containing the label of the item
  74.      * - `url` containing the url of the item (may be `null`)
  75.      * - `item` containing the original item (may be `null` for the extra items)
  76.      *
  77.      * The subItem can be one of the following forms
  78.      *   * 'subItem'
  79.      *   * ItemInterface object
  80.      *   * ['subItem' => '@homepage']
  81.      *   * ['subItem1', 'subItem2']
  82.      *   * [['label' => 'subItem1', 'url' => '@homepage'], ['label' => 'subItem2']]
  83.      *
  84.      * @param mixed $menu
  85.      * @param mixed $subItem A string or array to append onto the end of the array
  86.      *
  87.      * @phpstan-param string|ItemInterface|array<int|string, string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface>|\Traversable<string|int|float|null|array{label: string, url: string|null, item: ItemInterface|null}|ItemInterface> $subItem
  88.      *
  89.      * @return array<int, array<string, mixed>>
  90.      *
  91.      * @phpstan-return list<array{label: string, uri: string|null, item: ItemInterface|null}>
  92.      */
  93.     public function getBreadcrumbsArray($menu$subItem null): array
  94.     {
  95.         if (null === $this->menuManipulator) {
  96.             throw new \BadMethodCallException('The menu manipulator must be set to get the breadcrumbs array');
  97.         }
  98.         $menu $this->castMenu($menu);
  99.         return $this->menuManipulator->getBreadcrumbsArray($menu$subItem);
  100.     }
  101.     /**
  102.      * Returns the current item of a menu.
  103.      *
  104.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  105.      */
  106.     public function getCurrentItem($menu): ?ItemInterface
  107.     {
  108.         $menu $this->castMenu($menu);
  109.         return $this->retrieveCurrentItem($menu);
  110.     }
  111.     /**
  112.      * @param ItemInterface|string|array<ItemInterface|string> $menu
  113.      */
  114.     private function castMenu($menu): ItemInterface
  115.     {
  116.         if (!$menu instanceof ItemInterface) {
  117.             $path = [];
  118.             if (\is_array($menu)) {
  119.                 if (empty($menu)) {
  120.                     throw new \InvalidArgumentException('The array cannot be empty');
  121.                 }
  122.                 $path $menu;
  123.                 $menu \array_shift($path);
  124.             }
  125.             return $this->get($menu$path);
  126.         }
  127.         return $menu;
  128.     }
  129.     private function retrieveCurrentItem(ItemInterface $item): ?ItemInterface
  130.     {
  131.         if (null === $this->matcher) {
  132.             throw new \BadMethodCallException('The matcher must be set to get the current item of a menu');
  133.         }
  134.         if ($this->matcher->isCurrent($item)) {
  135.             return $item;
  136.         }
  137.         if ($this->matcher->isAncestor($item)) {
  138.             foreach ($item->getChildren() as $child) {
  139.                 $currentItem $this->retrieveCurrentItem($child);
  140.                 if (null !== $currentItem) {
  141.                     return $currentItem;
  142.                 }
  143.             }
  144.         }
  145.         return null;
  146.     }
  147. }