MainMenuListener::populateMenu()   A
last analyzed

Complexity

Conditions 6
Paths 10

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 14
c 1
b 0
f 1
dl 0
loc 23
rs 9.2222
cc 6
nc 10
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FSi\Bundle\AdminBundle\EventListener;
6
7
use FSi\Bundle\AdminBundle\Admin\Element;
8
use FSi\Bundle\AdminBundle\Admin\ManagerInterface;
9
use FSi\Bundle\AdminBundle\Event\MenuEvent;
10
use FSi\Bundle\AdminBundle\Menu\Builder\Exception\InvalidYamlStructureException;
11
use FSi\Bundle\AdminBundle\Menu\Item\ElementItem;
12
use FSi\Bundle\AdminBundle\Menu\Item\Item;
13
use FSi\Bundle\AdminBundle\Menu\Item\RoutableItem;
14
use Symfony\Component\Yaml\Yaml;
15
16
class MainMenuListener
17
{
18
    /**
19
     * @var string
20
     */
21
    private $configFilePath;
22
23
    /**
24
     * @var Yaml
25
     */
26
    private $yaml;
27
28
    /**
29
     * @var ManagerInterface
30
     */
31
    private $manager;
32
33
    public function __construct(ManagerInterface $manager, string $configFilePath)
34
    {
35
        $this->configFilePath = $configFilePath;
36
        $this->yaml = new Yaml();
37
        $this->manager = $manager;
38
    }
39
40
    public function createMainMenu(MenuEvent $event): Item
41
    {
42
        if (defined('Symfony\Component\Yaml\Yaml::PARSE_OBJECT')) {
43
            $config = $this->yaml->parse(
44
                file_get_contents($this->configFilePath),
45
                Yaml::PARSE_OBJECT | Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE
46
            );
47
        } else {
48
            $config = $this->yaml->parse(file_get_contents($this->configFilePath), true, true);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Component\Yaml\Yaml::parse() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

48
            /** @scrutinizer ignore-call */ 
49
            $config = $this->yaml->parse(file_get_contents($this->configFilePath), true, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $flags of Symfony\Component\Yaml\Yaml::parse(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

48
            $config = $this->yaml->parse(file_get_contents($this->configFilePath), /** @scrutinizer ignore-type */ true, true);
Loading history...
49
        }
50
51
        if (!isset($config['menu'])) {
52
            throw new InvalidYamlStructureException(
53
                sprintf('File "%s" should contain top level "menu:" key', $this->configFilePath)
54
            );
55
        }
56
57
        $menu = $event->getMenu();
58
        $menu->setOptions([
59
            'attr' => [
60
                'id' => 'top-menu',
61
                'class' => 'nav navbar-nav',
62
            ]
63
        ]);
64
65
        $this->populateMenu($menu, $config['menu']);
66
67
        return $menu;
68
    }
69
70
    private function populateMenu(Item $menu, array $configs): void
71
    {
72
        foreach ($configs as $itemConfig) {
73
            $item = $this->buildSingleItem($itemConfig);
74
75
            if (null !== $item) {
76
                $options = ['attr' => ['class' => 'admin-element']];
77
                if ($item instanceof ElementItem) {
78
                    $options['elements'] = $this->buildItemElements($itemConfig);
79
                }
80
                $item->setOptions($options);
81
            }
82
83
            if (null === $item) {
84
                if ($this->isSingleItem($itemConfig)) {
85
                    continue;
86
                }
87
                $item = new Item(key($itemConfig));
88
                $group = array_values($itemConfig);
89
                $this->populateMenu($item, $group[0]);
90
            }
91
92
            $menu->addChild($item);
93
        }
94
    }
95
96
    /**
97
     * @param array|string $itemConfig
98
     * @return Item|null
99
     */
100
    private function buildSingleItem($itemConfig): ?Item
101
    {
102
        if (is_string($itemConfig)) {
103
            if ($this->manager->hasElement($itemConfig)) {
104
                return new ElementItem($itemConfig, $this->manager->getElement($itemConfig));
105
            }
106
107
            return new Item($itemConfig);
108
        }
109
110
        if (!$this->isSingleItem($itemConfig)) {
111
            return null;
112
        }
113
114
        if ($this->hasEntry($itemConfig, 'id') && $this->manager->hasElement($itemConfig['id'])) {
115
            return new ElementItem(
116
                $this->hasEntry($itemConfig, 'name') ? $itemConfig['name'] : $itemConfig['id'],
117
                $this->manager->getElement($itemConfig['id'])
118
            );
119
        }
120
121
        if ($this->hasEntry($itemConfig, 'route')) {
122
            return new RoutableItem(
123
                $itemConfig['name'] ?? $itemConfig['route'],
124
                $itemConfig['route'],
125
                $itemConfig['route_parameters'] ?? []
126
            );
127
        }
128
129
        return null;
130
    }
131
132
    /**
133
     * @param array|string $itemConfig
134
     * @return bool
135
     */
136
    private function isSingleItem($itemConfig): bool
137
    {
138
        return $this->hasEntry($itemConfig, 'id') || $this->hasEntry($itemConfig, 'route');
139
    }
140
141
    /**
142
     * @param array|string $itemConfig
143
     * @param string $keyName
144
     * @return bool
145
     */
146
    private function hasEntry($itemConfig, string $keyName): bool
147
    {
148
        return is_array($itemConfig) && array_key_exists($keyName, $itemConfig);
149
    }
150
151
    /**
152
     * @param array|string $itemConfig
153
     * @return Element[]
154
     */
155
    private function buildItemElements($itemConfig): array
156
    {
157
        $elements = [];
158
159
        if ($this->hasEntry($itemConfig, 'elements')) {
160
            $elementIds = (array)$itemConfig['elements'];
161
            foreach ($elementIds as $elementId) {
162
                $elements[] = $this->manager->getElement($elementId);
163
            }
164
        }
165
166
        return $elements;
167
    }
168
}
169