1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace JumpGate\Menu; |
4
|
|
|
|
5
|
|
|
use Illuminate\Support\Collection; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Class Container |
9
|
|
|
* |
10
|
|
|
* @package JumpGate\Menu |
11
|
|
|
*/ |
12
|
|
|
class Container extends Collection |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* The slug to set to active during render |
16
|
|
|
* |
17
|
|
|
* @var array |
18
|
|
|
*/ |
19
|
|
|
private $active = []; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Get a menu you have created. |
23
|
|
|
* |
24
|
|
|
* @param $menuName |
25
|
|
|
* |
26
|
|
|
* @return mixed |
27
|
|
|
*/ |
28
|
|
|
public function getMenu($menuName) |
29
|
|
|
{ |
30
|
|
|
$menu = $this->getMenuObject($menuName); |
31
|
|
|
if ($menu) { |
32
|
|
|
return $menu; |
33
|
|
|
} else { |
34
|
|
|
return $this->add($menuName); |
35
|
|
|
} |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Add a new menu. |
40
|
|
|
* |
41
|
|
|
* @param $menuName |
42
|
|
|
* |
43
|
|
|
* @return Menu |
44
|
|
|
*/ |
45
|
|
|
public function add($menuName) |
46
|
|
|
{ |
47
|
|
|
return $this->items[] = new Menu($this->snakeName($menuName)); |
|
|
|
|
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Check if a menu exists. |
52
|
|
|
* |
53
|
|
|
* @param $menuName |
54
|
|
|
* |
55
|
|
|
* @return bool |
56
|
|
|
*/ |
57
|
|
|
public function exists($menuName) |
58
|
|
|
{ |
59
|
|
|
return (bool)$this->getMenuObject($menuName); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Check if a menu is empty |
64
|
|
|
* |
65
|
|
|
* @param $menuName |
66
|
|
|
* |
67
|
|
|
* @return bool |
68
|
|
|
*/ |
69
|
|
|
public function hasLinks($menuName) |
70
|
|
|
{ |
71
|
|
|
return (count($this->getMenuObject($menuName)->links) > 0); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Update the active and order values and then return the object. |
76
|
|
|
* |
77
|
|
|
* @param $menuName |
78
|
|
|
* |
79
|
|
|
* @return Menu |
80
|
|
|
* @throws \Exception |
81
|
|
|
*/ |
82
|
|
|
public function render($menuName) |
83
|
|
|
{ |
84
|
|
|
// We set active at the last possible moment. |
85
|
|
|
$this->updateActive(); |
86
|
|
|
|
87
|
|
|
$menu = $this->getMenuObject($menuName); |
88
|
|
|
|
89
|
|
|
if (! $menu) { |
90
|
|
|
throw new \Exception("Menu {$menuName} not found."); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
return $menu; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Sanitize the menus names for safe use in array keys. |
98
|
|
|
* |
99
|
|
|
* @param $name |
100
|
|
|
* |
101
|
|
|
* @return string |
102
|
|
|
*/ |
103
|
|
|
public function snakeName($name) |
104
|
|
|
{ |
105
|
|
|
return e(snake_case($name)); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Get the menu object |
110
|
|
|
* |
111
|
|
|
* @param $menuName |
112
|
|
|
* |
113
|
|
|
* @return mixed|null |
114
|
|
|
*/ |
115
|
|
|
private function getMenuObject($menuName) |
116
|
|
|
{ |
117
|
|
|
return $this->where('name', $this->snakeName($menuName))->first(); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Get the active link by slug. |
122
|
|
|
* |
123
|
|
|
* @return array |
124
|
|
|
*/ |
125
|
|
|
public function getActive() |
126
|
|
|
{ |
127
|
|
|
return $this->active; |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Set the active link by slug. |
132
|
|
|
* |
133
|
|
|
* @param $slug |
134
|
|
|
*/ |
135
|
|
|
public function setActive($slug) |
136
|
|
|
{ |
137
|
|
|
$this->active = explode('::', $slug); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Use the active param and set the link to active |
142
|
|
|
*/ |
143
|
|
|
private function updateActive() |
144
|
|
|
{ |
145
|
|
|
$this->each(function ($item) { |
146
|
|
|
if (isset($item->links)) { |
147
|
|
|
$this->makeActive($item); |
148
|
|
|
} |
149
|
|
|
}); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Loop through the links and check for active. |
154
|
|
|
* Sets the item and it's parents as active when told to. |
155
|
|
|
*/ |
156
|
|
|
private function makeActive($item, $parent = null) |
157
|
|
|
{ |
158
|
|
|
$item->links->each(function ($link) use ($item, $parent) { |
159
|
|
|
if (in_array($link->slug, $this->active)) { |
160
|
|
|
$this->makeParentActive($parent); |
161
|
|
|
$this->makeParentActive($item); |
162
|
|
|
|
163
|
|
|
$link->setActive(true); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
if (isset($link->links)) { |
167
|
|
|
$this->makeActive($link, $item); |
168
|
|
|
} |
169
|
|
|
}); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Set the parent item as active if able to do so. |
174
|
|
|
*/ |
175
|
|
|
private function makeParentActive($parent) |
176
|
|
|
{ |
177
|
|
|
if (! is_null($parent) |
178
|
|
|
&& method_exists($parent, 'activeParentage') |
179
|
|
|
&& $parent->activeParentage() |
180
|
|
|
) { |
181
|
|
|
$parent->setActive(true); |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.