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

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. }