1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\Ui\Menu; |
4
|
|
|
|
5
|
|
|
use \InvalidArgumentException; |
6
|
|
|
|
7
|
|
|
// Intra-module (`charcoal-ui`) dependencies |
8
|
|
|
use \Charcoal\Ui\AbstractUiItem; |
9
|
|
|
use \Charcoal\Ui\Menu\MenuInterface; |
10
|
|
|
use \Charcoal\Ui\MenuItem\MenuItemInterface; |
11
|
|
|
use \Charcoal\Ui\MenuItem\MenuItemBuilder; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* A Basic Menu |
15
|
|
|
* |
16
|
|
|
* Abstract implementation of {@see \Charcoal\Ui\Menu\MenuInterface}. |
17
|
|
|
*/ |
18
|
|
|
abstract class AbstractMenu extends AbstractUiItem implements |
19
|
|
|
MenuInterface |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* A collection menu items. |
23
|
|
|
* |
24
|
|
|
* @var MenuItemInterface[] |
25
|
|
|
*/ |
26
|
|
|
private $items = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* A callback applied to each menu item output by {@see self::items()}. |
30
|
|
|
* |
31
|
|
|
* @var callable |
32
|
|
|
*/ |
33
|
|
|
private $itemCallback; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Store a menu builder instance. |
37
|
|
|
* |
38
|
|
|
* @var MenuItemBuilder $menuItemBuilder |
39
|
|
|
*/ |
40
|
|
|
private $menuItemBuilder; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Return a new menu. |
44
|
|
|
* |
45
|
|
|
* @param array|\ArrayAccess $data Class dependencies. |
46
|
|
|
*/ |
47
|
|
|
public function __construct($data) |
48
|
|
|
{ |
49
|
|
|
$this->setMenuItemBuilder($data['menu_item_builder']); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @param MenuItemBuilder $menuItemBuilder The Menu Item Builder that will be used to create new items. |
54
|
|
|
* @return AsbtractMenu Chainable |
55
|
|
|
*/ |
56
|
|
|
public function setMenuItemBuilder(MenuItemBuilder $menuItemBuilder) |
57
|
|
|
{ |
58
|
|
|
$this->menuItemBuilder = $menuItemBuilder; |
59
|
|
|
return $this; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @param callable $cb The item callback. |
64
|
|
|
* @return AbstractMenu Chainable |
65
|
|
|
*/ |
66
|
|
|
public function setItemCallback(callable $cb) |
67
|
|
|
{ |
68
|
|
|
$this->itemCallback = $cb; |
69
|
|
|
return $this; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @param array $items The menu items. |
74
|
|
|
* @return AbstractMenu Chainable |
75
|
|
|
*/ |
76
|
|
|
public function setItems(array $items) |
77
|
|
|
{ |
78
|
|
|
$this->items = []; |
79
|
|
|
foreach ($items as $ident => $item) { |
80
|
|
|
$this->addItem($item, $ident); |
81
|
|
|
} |
82
|
|
|
return $this; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @param array|MenuItemInterface $item A menu item structure or object. |
87
|
|
|
* @param string $ident The menu item identifier, if any. |
88
|
|
|
* @throws InvalidArgumentException If the item argument is not a structure or object. |
89
|
|
|
* @return MenuItem Chainable |
90
|
|
|
*/ |
91
|
|
|
public function addItem($item, $ident = null) |
92
|
|
|
{ |
93
|
|
|
if (is_array($item)) { |
94
|
|
|
$item['menu'] = $this; |
95
|
|
|
if (!isset($item['ident'])) { |
96
|
|
|
$item['ident'] = $ident; |
97
|
|
|
} |
98
|
|
|
$i = $this->menuItemBuilder->build($item); |
99
|
|
|
$item = $i; |
100
|
|
|
} elseif ($item instanceof MenuItemInterface) { |
101
|
|
|
if ($item->ident() === null) { |
102
|
|
|
$item->setIdent($ident); |
103
|
|
|
} |
104
|
|
|
$item->setMenu($this); |
105
|
|
|
} else { |
106
|
|
|
throw new InvalidArgumentException( |
107
|
|
|
'Item must be an array of menu item options or a MenuItem object' |
108
|
|
|
); |
109
|
|
|
} |
110
|
|
|
if ($ident === null) { |
111
|
|
|
$this->items[] = $item; |
112
|
|
|
} else { |
113
|
|
|
$this->items[$ident] = $item; |
114
|
|
|
} |
115
|
|
|
return $this; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Menu Item generator. |
120
|
|
|
* |
121
|
|
|
* @param callable $itemCallback Optional. Item callback. |
122
|
|
|
* @return MenuItemInterface[] |
123
|
|
|
*/ |
124
|
|
View Code Duplication |
public function items(callable $itemCallback = null) |
|
|
|
|
125
|
|
|
{ |
126
|
|
|
$items = $this->items; |
127
|
|
|
uasort($items, [$this, 'sortItemsByPriority']); |
128
|
|
|
|
129
|
|
|
$itemCallback = isset($itemCallback) ? $itemCallback : $this->itemCallback; |
130
|
|
|
foreach ($items as $item) { |
131
|
|
|
if ($itemCallback) { |
132
|
|
|
$itemCallback($item); |
133
|
|
|
} |
134
|
|
|
$GLOBALS['widget_template'] = $item->template(); |
135
|
|
|
yield $item->ident() => $item; |
136
|
|
|
$GLOBALS['widget_template'] = ''; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* @return boolean |
142
|
|
|
*/ |
143
|
|
|
public function hasItems() |
144
|
|
|
{ |
145
|
|
|
return (count($this->items) > 0); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @return integer |
150
|
|
|
*/ |
151
|
|
|
public function numItems() |
152
|
|
|
{ |
153
|
|
|
return count($this->items); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.