Issues (5)

src/Services/Resolver/AbstractMenuResolver.php (2 issues)

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
The doc comment array<array-key, string> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, string>.
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
Documentation Bug introduced by
The doc comment array<array-key, mixed> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>.
Loading history...
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