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

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