Completed
Pull Request — 3.1 (#348)
by Piotr
07:35
created

MainMenuListener::populateMenu()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8977
c 0
b 0
f 0
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
Documentation introduced by
true is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to Yaml::parse() has too many arguments starting with 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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

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