Completed
Push — master ( 878d6c...baaae8 )
by Florian
03:45
created

Builder::addItem()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 16
cts 16
cp 1
rs 9.1288
c 0
b 0
f 0
cc 5
nc 6
nop 3
crap 5
1
<?php
2
3
namespace JeroenNoten\LaravelAdminLte\Menu;
4
5
use Illuminate\Support\Arr;
6
7
class Builder
8
{
9
    protected const ADD_AFTER = 0;
10
    protected const ADD_BEFORE = 1;
11
    protected const ADD_INSIDE = 2;
12
13
    /**
14
     * The set of menu items.
15
     *
16
     * @var array
17
     */
18
    public $menu = [];
19
20
    /**
21
     * The set of filters applied to menu items.
22
     *
23
     * @var array
24
     */
25
    private $filters;
26
27
    /**
28
     * Constructor.
29
     *
30
     * @param array $filters
31
     */
32 49
    public function __construct(array $filters = [])
33
    {
34 49
        $this->filters = $filters;
35 49
    }
36
37
    /**
38
     * Add new items at the end of the menu.
39
     *
40
     * @param mixed $newItems Items to be added
41
     */
42 49
    public function add(...$newItems)
43
    {
44 49
        $items = $this->transformItems($newItems);
45 49
        array_push($this->menu, ...$items);
46 49
    }
47
48
    /**
49
     * Add new items after a specific menu item.
50
     *
51
     * @param mixed $itemKey The key that represents the specific menu item
52
     * @param mixed $newItems Items to be added
53
     */
54 5
    public function addAfter($itemKey, ...$newItems)
55
    {
56 5
        $this->addItem($itemKey, self::ADD_AFTER, ...$newItems);
57 5
    }
58
59
    /**
60
     * Add new items before a specific menu item.
61
     *
62
     * @param mixed $itemKey The key that represents the specific menu item
63
     * @param mixed $newItems Items to be added
64
     */
65 5
    public function addBefore($itemKey, ...$newItems)
66
    {
67 5
        $this->addItem($itemKey, self::ADD_BEFORE, ...$newItems);
68 5
    }
69
70
    /**
71
     * Add new submenu items inside a specific menu item.
72
     *
73
     * @param mixed $itemKey The key that represents the specific menu item
74
     * @param mixed $newItems Items to be added
75
     */
76 4
    public function addIn($itemKey, ...$newItems)
77
    {
78 4
        $this->addItem($itemKey, self::ADD_INSIDE, ...$newItems);
79 4
    }
80
81
    /**
82
     * Remove a specific menu item.
83
     *
84
     * @param mixed $itemKey The key of the menu item to remove
85
     */
86 5
    public function remove($itemKey)
87
    {
88
        // Find the specific menu item. Return if not found.
89
90 5
        if (! ($itemPath = $this->findItem($itemKey, $this->menu))) {
91 1
            return;
92
        }
93
94
        // Remove the item.
95
96 4
        Arr::forget($this->menu, implode('.', $itemPath));
97
98
        // Normalize the menu (remove holes in the numeric indexes).
99
100 4
        $holedArrPath = implode('.', array_slice($itemPath, 0, -1)) ?: null;
101 4
        $holedArr = Arr::get($this->menu, $holedArrPath, $this->menu);
102 4
        Arr::set($this->menu, $holedArrPath, array_values($holedArr));
103 4
    }
104
105
    /**
106
     * Check if exists a menu item with the specified key.
107
     *
108
     * @param mixed $itemKey The key of the menu item to check for
109
     * @return bool
110
     */
111 3
    public function itemKeyExists($itemKey)
112
    {
113 3
        return (bool) $this->findItem($itemKey, $this->menu);
114
    }
115
116
    /**
117
     * Transform the items by applying the filters.
118
     *
119
     * @param array $items An array with items to be transformed
120
     * @return array Array with the new transformed items
121
     */
122 49
    public function transformItems($items)
123
    {
124 49
        return array_filter(array_map([$this, 'applyFilters'], $items));
125
    }
126
127
    /**
128
     * Find a menu item by the item key and return the path to it.
129
     *
130
     * @param mixed $itemKey The key of the item to find
131
     * @param array $items The array to look up for the item
132
     * @return mixed Array with the path sequence, or empty array if not found
133
     */
134 22
    protected function findItem($itemKey, $items)
135
    {
136
        // Look up on all the items.
137
138 22
        foreach ($items as $key => $item) {
139 22
            if (isset($item['key']) && $item['key'] === $itemKey) {
140 18
                return [$key];
141 11
            } elseif (isset($item['submenu']) && is_array($item['submenu'])) {
142
143
                // Do the recursive call to search on submenu. If we found the
144
                // item, merge the path with the current one.
145
146 6
                if ($newPath = $this->findItem($itemKey, $item['submenu'])) {
147 6
                    return array_merge([$key, 'submenu'], $newPath);
148
                }
149
            }
150
        }
151
152
        // Return empty array when the item is not found.
153
154 6
        return [];
155
    }
156
157
    /**
158
     * Apply all the available filters to a menu item.
159
     *
160
     * @param mixed $item A menu item
161
     * @return mixed A new item with all the filters applied
162
     */
163 49
    protected function applyFilters($item)
164
    {
165 49
        if (is_string($item)) {
166
            return $item;
167
        }
168
169 49
        foreach ($this->filters as $filter) {
170 44
            $item = $filter->transform($item, $this);
171
        }
172
173 49
        return $item;
174
    }
175
176
    /**
177
     * Add new items to the menu in a particular place, relative to a
178
     * specific menu item.
179
     *
180
     * @param mixed $itemKey The key that represents the specific menu item
181
     * @param int $where Where to add the new items
182
     * @param mixed $newItems Items to be added
183
     */
184 14
    protected function addItem($itemKey, $where, ...$newItems)
185
    {
186
        // Find the specific menu item. Return if not found.
187
188 14
        if (! ($itemPath = $this->findItem($itemKey, $this->menu))) {
189 3
            return;
190
        }
191
192
        // Apply the filters to the new items.
193
194 11
        $items = $this->transformItems($newItems);
195
196
        // Get the target array and add the new items there.
197
198 11
        $itemKeyIdx = end($itemPath);
199 11
        reset($itemPath);
200
201 11
        if ($where === self::ADD_INSIDE) {
202 3
            $targetPath = implode('.', array_merge($itemPath, ['submenu']));
203 3
            $targetArr = Arr::get($this->menu, $targetPath, []);
204 3
            array_push($targetArr, ...$items);
205
        } else {
206 8
            $targetPath = implode('.', array_slice($itemPath, 0, -1)) ?: null;
207 8
            $targetArr = Arr::get($this->menu, $targetPath, $this->menu);
208 8
            $offset = ($where === self::ADD_AFTER) ? 1 : 0;
209 8
            array_splice($targetArr, $itemKeyIdx + $offset, 0, $items);
210
        }
211
212 11
        Arr::set($this->menu, $targetPath, $targetArr);
213 11
    }
214
}
215