1: <?php
2:
3: namespace Charcoal\Ui\MenuItem;
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:
12: /**
13: * A Basic Menu Item
14: *
15: * Abstract implementation of {@see \Charcoal\Ui\MenuItem\MenuItemInterface}.
16: */
17: abstract class AbstractMenuItem extends AbstractUiItem implements MenuItemInterface
18: {
19: /**
20: * Parent menu item
21: * @var MenuInterface $menu
22: */
23: private $menu;
24:
25: /**
26: * @var string $ident
27: */
28: protected $ident;
29:
30: /**
31: * @var \Charcoal\Translator\Translation|null $label
32: */
33: protected $label;
34:
35: /**
36: * @var string $url
37: */
38: protected $url;
39:
40: /**
41: * @var MenuItemInterface[] $children
42: */
43: protected $children;
44:
45: /**
46: * @var callable $childCallback
47: */
48: private $childCallback;
49:
50: /**
51: * Return a new menu item.
52: *
53: * @param array|\ArrayAccess $data Class dependencies.
54: */
55: public function __construct($data)
56: {
57: $this->setMenu($data['menu']);
58: $this->setMenuItemBuilder($data['menu_item_builder']);
59:
60: parent::__construct($data);
61: }
62:
63: /**
64: * Set the parent (menu) object.
65: *
66: * @param MenuInterface $menu The parent menu object.
67: * @return MenuItemInterface Chainable
68: */
69: protected function setMenu(MenuInterface $menu)
70: {
71: $this->menu = $menu;
72: return $this;
73: }
74:
75: /**
76: * @param MenuItemBuilder $menuItemBuilder The Menu Item Builder that will be used to create new items.
77: * @return MenuItemInterface Chainable
78: */
79: public function setMenuItemBuilder(MenuItemBuilder $menuItemBuilder)
80: {
81: $this->menuItemBuilder = $menuItemBuilder;
82: return $this;
83: }
84:
85: /**
86: * @param callable $cb The item callback.
87: * @return MenuItemInterface Chainable
88: */
89: public function setItemCallback(callable $cb)
90: {
91: $this->childCallback = $cb;
92: return $this;
93: }
94:
95: /**
96: * @param string $ident The menu item identifier.
97: * @throws InvalidArgumentException If the identifier argument is not a string.
98: * @return MenuItem Chainable
99: */
100: public function setIdent($ident)
101: {
102: if (!is_string($ident)) {
103: throw new InvalidArgumentException(
104: 'Menu item identifier must a string'
105: );
106: }
107: $this->ident = $ident;
108: return $this;
109: }
110:
111: /**
112: * @return string
113: */
114: public function ident()
115: {
116: return $this->ident;
117: }
118:
119: /**
120: * @param mixed $label The menu item label.
121: * @return MenuItem Chainable
122: */
123: public function setLabel($label)
124: {
125: $this->label = $this->translator()->translation($label);
126: return $this;
127: }
128:
129: /**
130: * @return string
131: */
132: public function label()
133: {
134: return $this->label;
135: }
136:
137: /**
138: * @param string $url The menu item URL.
139: * @return MenuItem Chainable
140: */
141: public function setUrl($url)
142: {
143: $this->url = $url;
144: return $this;
145: }
146:
147: /**
148: * @return string
149: */
150: public function url()
151: {
152: return $this->url;
153: }
154:
155: /**
156: * @return boolean
157: */
158: public function hasUrl()
159: {
160: return !!($this->url());
161: }
162:
163: /**
164: * @param array $children The menu item children items structure.
165: * @return MenuItem Chainable
166: */
167: public function setChildren(array $children)
168: {
169: $this->children = [];
170: foreach ($children as $c) {
171: $this->addChild($c);
172: }
173: return $this;
174: }
175:
176: /**
177: * @param array|MenuItem $child The child menu structure or object.
178: * @throws InvalidArgumentException If the child is not a menu object or structure.
179: * @return MenuItem Chainable
180: */
181: public function addChild($child)
182: {
183: if (is_array($child)) {
184: $child['menu'] = $this->menu;
185: $c = $this->menuItemBuilder->build($child);
186: $this->children[] = $c;
187: } elseif ($child instanceof MenuItemInterface) {
188: $this->children[] = $child;
189: } else {
190: throw new InvalidArgumentException(
191: 'Child must be an array or a MenuItem object'
192: );
193: }
194: return $this;
195: }
196:
197: /**
198: * Children (menu item) generator
199: *
200: * @param callable $childCallback Optional callback.
201: * @return MenuItemInterface[]
202: */
203: public function children(callable $childCallback = null)
204: {
205: $children = $this->children;
206: uasort($children, ['self', 'sortChildrenByPrioriy']);
207:
208: $childCallback = isset($childCallback) ? $childCallback : $this->childCallback;
209: foreach ($children as $child) {
210: if ($childCallback) {
211: $childCallback($child);
212: }
213: $GLOBALS['widget_template'] = $item->template();
214: yield $child->ident() => $child;
215: $GLOBALS['widget_template'] = '';
216: }
217: }
218:
219: /**
220: * @return boolean
221: */
222: public function hasChildren()
223: {
224: return (count($this->children) > 0);
225: }
226:
227: /**
228: * @return integer
229: */
230: public function numChildren()
231: {
232: return count($this->children);
233: }
234: }
235: