Braunstetter /
menu-bundle
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Braunstetter\MenuBundle\Services\Resolver; |
||
| 6 | |||
| 7 | use Braunstetter\MenuBundle\Contracts\MenuInterface; |
||
| 8 | use Braunstetter\MenuBundle\Contracts\MenuItemInterface; |
||
| 9 | use Braunstetter\MenuBundle\Contracts\MenuResolverInterface; |
||
| 10 | use Symfony\Component\HttpFoundation\RequestStack; |
||
| 11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
||
| 12 | use Symfony\Component\String\UnicodeString; |
||
| 13 | |||
| 14 | abstract class AbstractMenuResolver implements MenuResolverInterface |
||
| 15 | { |
||
| 16 | /** |
||
| 17 | * @var iterable<MenuInterface> |
||
| 18 | */ |
||
| 19 | protected iterable $entries; |
||
| 20 | |||
| 21 | /** |
||
| 22 | * @var array<array-key, string> |
||
|
0 ignored issues
–
show
Documentation
Bug
introduced
by
Loading history...
|
|||
| 23 | */ |
||
| 24 | protected array $selectedSubnavItem = []; |
||
| 25 | |||
| 26 | private RequestStack $requestStack; |
||
| 27 | |||
| 28 | private UrlGeneratorInterface $generator; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * @param iterable<MenuInterface> $entries |
||
| 32 | */ |
||
| 33 | public function __construct(iterable $entries, RequestStack $requestStack, UrlGeneratorInterface $generator) |
||
| 34 | { |
||
| 35 | $this->entries = $entries; |
||
| 36 | $this->requestStack = $requestStack; |
||
| 37 | $this->generator = $generator; |
||
| 38 | } |
||
| 39 | |||
| 40 | protected function matches(MenuItemInterface $item): bool |
||
| 41 | { |
||
| 42 | if ($this->selectedSubnavItemMatches($item)) { |
||
| 43 | $item->setCurrent(true); |
||
| 44 | return true; |
||
| 45 | } |
||
| 46 | |||
| 47 | $routeName = $item->getRouteName(); |
||
| 48 | if ($routeName === null) { |
||
| 49 | return false; |
||
| 50 | } |
||
| 51 | |||
| 52 | $uri = $this->generator->generate($routeName, $item->getRouteParameters() ?: []); |
||
| 53 | |||
| 54 | if ($uri === $this->requestStack->getCurrentRequest()?->getPathInfo()) { |
||
| 55 | $item->setCurrent(true); |
||
| 56 | return true; |
||
| 57 | } |
||
| 58 | |||
| 59 | return false; |
||
| 60 | } |
||
| 61 | |||
| 62 | protected function oneOfTheChildrenMatches(MenuItemInterface $item): bool |
||
| 63 | { |
||
| 64 | if (! empty($item->getChildren())) { |
||
| 65 | foreach ($item->getChildren() as $subItem) { |
||
| 66 | if ($this->matches($subItem)) { |
||
| 67 | $item->setInActiveTrail(true); |
||
| 68 | return true; |
||
| 69 | } |
||
| 70 | |||
| 71 | if ($this->oneOfTheChildrenMatches($subItem)) { |
||
| 72 | $item->setInActiveTrail(true); |
||
| 73 | return true; |
||
| 74 | } |
||
| 75 | } |
||
| 76 | } |
||
| 77 | |||
| 78 | return false; |
||
| 79 | } |
||
| 80 | |||
| 81 | /** |
||
| 82 | * @param array<array-key, mixed> $context |
||
|
0 ignored issues
–
show
|
|||
| 83 | */ |
||
| 84 | protected function setSubnavItem(array $context): void |
||
| 85 | { |
||
| 86 | if (! array_key_exists('selectedSubnavItem', $context)) { |
||
| 87 | return; |
||
| 88 | } |
||
| 89 | |||
| 90 | /** @var array<array-key, string>|string $selectedSubnavItem */ |
||
| 91 | $selectedSubnavItem = $context['selectedSubnavItem']; |
||
| 92 | |||
| 93 | if (is_string($selectedSubnavItem)) { |
||
| 94 | $selectedSubnavItem = [$selectedSubnavItem]; |
||
| 95 | } |
||
| 96 | |||
| 97 | $this->selectedSubnavItem = $selectedSubnavItem; |
||
| 98 | } |
||
| 99 | |||
| 100 | protected function getHandle(MenuInterface|MenuItemInterface $class): string |
||
| 101 | { |
||
| 102 | if (method_exists($class, 'getHandle')) { |
||
| 103 | /** @var string $handle */ |
||
| 104 | $handle = $class->getHandle(); |
||
| 105 | return $handle; |
||
| 106 | } |
||
| 107 | |||
| 108 | if ($class instanceof MenuItemInterface) { |
||
| 109 | $label = $class->getLabel(); |
||
| 110 | if ($label !== null) { |
||
| 111 | return $this->toSnakeCase($label); |
||
| 112 | } |
||
| 113 | } |
||
| 114 | |||
| 115 | $className = (new \ReflectionClass($class))->getShortName(); |
||
| 116 | return $this->toSnakeCase($className); |
||
| 117 | } |
||
| 118 | |||
| 119 | /** |
||
| 120 | * @return MenuItemInterface[] |
||
| 121 | */ |
||
| 122 | protected function resolveMenu(MenuInterface $menu): array |
||
| 123 | { |
||
| 124 | return iterator_to_array($menu->define(), false); |
||
| 125 | } |
||
| 126 | |||
| 127 | private function selectedSubnavItemMatches(MenuItemInterface $item): bool |
||
| 128 | { |
||
| 129 | return in_array($this->getHandle($item), $this->selectedSubnavItem, true); |
||
| 130 | } |
||
| 131 | |||
| 132 | private function toSnakeCase(string $string): string |
||
| 133 | { |
||
| 134 | return (new UnicodeString($string))->snake() |
||
| 135 | ->toString(); |
||
| 136 | } |
||
| 137 | } |
||
| 138 |