Completed
Pull Request — 3.1 (#347)
by Łukasz
16:49 queued 11:36
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 ManagerInterface
25
     */
26
    private $manager;
27
28
    public function __construct(ManagerInterface $manager, string $configFilePath)
29
    {
30
        $this->configFilePath = $configFilePath;
31
        $this->manager = $manager;
32
    }
33
34
    public function createMainMenu(MenuEvent $event): Item
35
    {
36
        $config = Yaml::parse(
37
            file_get_contents($this->configFilePath),
38
            Yaml::PARSE_OBJECT | Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE
39
        );
40
41
        if (!isset($config['menu'])) {
42
            throw new InvalidYamlStructureException(
43
                sprintf('File "%s" should contain top level "menu:" key', $this->configFilePath)
44
            );
45
        }
46
47
        $menu = $event->getMenu();
48
        $menu->setOptions([
49
            'attr' => [
50
                'id' => 'top-menu',
51
                'class' => 'nav navbar-nav',
52
            ]
53
        ]);
54
55
        $this->populateMenu($menu, $config['menu']);
56
57
        return $menu;
58
    }
59
60
    private function populateMenu(Item $menu, array $configs): void
61
    {
62
        foreach ($configs as $itemConfig) {
63
            $item = $this->buildSingleItem($itemConfig);
64
65
            if (null !== $item) {
66
                $options = ['attr' => ['class' => 'admin-element']];
67
                if ($item instanceof ElementItem) {
68
                    $options['elements'] = $this->buildItemElements($itemConfig);
69
                }
70
                $item->setOptions($options);
71
            }
72
73
            if (null === $item) {
74
                if ($this->isSingleItem($itemConfig)) {
75
                    continue;
76
                }
77
                $item = new Item(key($itemConfig));
78
                $group = array_values($itemConfig);
79
                $this->populateMenu($item, $group[0]);
80
            }
81
82
            $menu->addChild($item);
83
        }
84
    }
85
86
    /**
87
     * @param array|string $itemConfig
88
     * @return Item|null
89
     */
90
    private function buildSingleItem($itemConfig): ?Item
91
    {
92
        if (is_string($itemConfig)) {
93
            if ($this->manager->hasElement($itemConfig)) {
94
                return new ElementItem($itemConfig, $this->manager->getElement($itemConfig));
95
            }
96
97
            return new Item($itemConfig);
98
        }
99
100
        if (!$this->isSingleItem($itemConfig)) {
101
            return null;
102
        }
103
104
        if ($this->hasEntry($itemConfig, 'id') && $this->manager->hasElement($itemConfig['id'])) {
105
            return new ElementItem(
106
                $this->hasEntry($itemConfig, 'name') ? $itemConfig['name'] : $itemConfig['id'],
107
                $this->manager->getElement($itemConfig['id'])
108
            );
109
        }
110
111
        if ($this->hasEntry($itemConfig, 'route')) {
112
            return new RoutableItem(
113
                $itemConfig['name'] ?? $itemConfig['route'],
114
                $itemConfig['route'],
115
                $itemConfig['route_parameters'] ?? []
116
            );
117
        }
118
119
        return null;
120
    }
121
122
    /**
123
     * @param array|string $itemConfig
124
     * @return bool
125
     */
126
    private function isSingleItem($itemConfig): bool
127
    {
128
        return $this->hasEntry($itemConfig, 'id') || $this->hasEntry($itemConfig, 'route');
129
    }
130
131
    /**
132
     * @param array|string $itemConfig
133
     * @param string $keyName
134
     * @return bool
135
     */
136
    private function hasEntry($itemConfig, string $keyName): bool
137
    {
138
        return is_array($itemConfig) && array_key_exists($keyName, $itemConfig);
139
    }
140
141
    /**
142
     * @param array|string $itemConfig
143
     * @return Element[]
144
     */
145
    private function buildItemElements($itemConfig): array
146
    {
147
        $elements = [];
148
149
        if ($this->hasEntry($itemConfig, 'elements')) {
150
            $elementIds = (array)$itemConfig['elements'];
151
            foreach ($elementIds as $elementId) {
152
                $elements[] = $this->manager->getElement($elementId);
153
            }
154
        }
155
156
        return $elements;
157
    }
158
}
159