Passed
Push — main ( 7a068b...7d69bc )
by Michael
02:22
created

AbstractMenuResolver::resolveMenu()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
use Webmozart\Assert\Assert;
14
15
abstract class AbstractMenuResolver implements MenuResolverInterface
16
{
17
    /**
18
     * @var iterable<MenuInterface>
19
     */
20
    protected iterable $entries;
21
22
    protected ?string $selectedSubnavItem = null;
23
24
    private RequestStack $requestStack;
25
26
    private UrlGeneratorInterface $generator;
27
28
    /**
29
     * @param iterable<MenuInterface> $entries
30
     */
31
    public function __construct(iterable $entries, RequestStack $requestStack, UrlGeneratorInterface $generator)
32
    {
33
        $this->entries = $entries;
34
        $this->requestStack = $requestStack;
35
        $this->generator = $generator;
36
    }
37
38
    protected function matches(MenuItemInterface $item): bool
39
    {
40
        if ($this->selectedSubnavItemMatches($item)) {
41
            $item->setCurrent(true);
42
            return true;
43
        }
44
45
        $routeName = $item->getRouteName();
46
        if ($routeName === null) {
47
            return false;
48
        }
49
50
        $uri = $this->generator->generate($routeName, $item->getRouteParameters() ?: []);
51
52
        if ($uri === $this->requestStack->getCurrentRequest()?->getPathInfo()) {
53
            $item->setCurrent(true);
54
            return true;
55
        }
56
57
        return false;
58
    }
59
60
    protected function oneOfTheChildrenMatches(MenuItemInterface $item): bool
61
    {
62
        if (! empty($item->getChildren())) {
63
            foreach ($item->getChildren() as $subItem) {
64
                if ($this->matches($subItem)) {
65
                    $item->setInActiveTrail(true);
66
                    return true;
67
                }
68
69
                if ($this->oneOfTheChildrenMatches($subItem)) {
70
                    $item->setInActiveTrail(true);
71
                    return true;
72
                }
73
            }
74
        }
75
76
        return false;
77
    }
78
79
    /**
80
     * @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...
81
     */
82
    protected function setSubnavItem(array $context): void
83
    {
84
        if (array_key_exists('selectedSubnavItem', $context)) {
85
            $selectedSubnavItem = $context['selectedSubnavItem'];
86
            Assert::string($selectedSubnavItem);
87
            $this->selectedSubnavItem = $selectedSubnavItem;
88
        }
89
    }
90
91
    protected function getHandle(MenuInterface|MenuItemInterface $class): string
92
    {
93
        if (method_exists($class, 'getHandle')) {
94
            /** @var string $handle */
95
            $handle = $class->getHandle();
96
            return $handle;
97
        }
98
99
        if ($class instanceof MenuItemInterface) {
100
            $label = $class->getLabel();
101
            if ($label !== null) {
102
                return $this->toSnakeCase($label);
103
            }
104
        }
105
106
        $className = (new \ReflectionClass($class))->getShortName();
107
        return $this->toSnakeCase($className);
108
    }
109
110
    /**
111
     * @return  MenuItemInterface[]
112
     */
113
    protected function resolveMenu(MenuInterface $menu): array
114
    {
115
        return iterator_to_array($menu->define(), false);
116
    }
117
118
    private function selectedSubnavItemMatches(MenuItemInterface $item): bool
119
    {
120
        if (! isset($this->selectedSubnavItem)) {
121
            return false;
122
        }
123
124
        return $this->getHandle($item) === $this->selectedSubnavItem;
125
    }
126
127
    private function toSnakeCase(string $string): string
128
    {
129
        return (new UnicodeString($string))->snake()
130
            ->toString();
131
    }
132
}
133