├── README.md
├── composer.json
└── src
├── Builder.php
├── Collection.php
├── Facades
└── Menu.php
├── Item.php
├── Link.php
├── Menu.php
└── MenusServiceProvider.php
/README.md:
--------------------------------------------------------------------------------
1 | Caffeinated Menus
2 | =================
3 | [](https://github.com/caffeinated/menus)
4 | [](https://tldrlegal.com/license/mit-license)
5 |
6 | ---
7 |
8 | Easily create dynamic menus from within your Laravel 5 application. Originally developed for [FusionCMS](https://github.com/fusioncms/fusioncms), an open source content management system.
9 |
10 | Caffeinated Menus was based off of Lavary's [Laravel Menu](https://github.com/lavary/laravel-menu) package with support for the Caffeinated Shinobi package.
11 |
12 | The package follows the FIG standards PSR-1, PSR-2, and PSR-4 to ensure a high level of interoperability between shared PHP code. At the moment the package is not unit tested, but is planned to be covered later down the road.
13 |
14 | Documentation
15 | -------------
16 | You will find user friendly and updated documentation in the wiki here: [Caffeinated Menus Wiki](https://github.com/caffeinated/menus/wiki)
17 |
18 | Quick Installation
19 | ------------------
20 | Begin by installing the package through Composer.
21 |
22 | ```
23 | composer require caffeinated/menus
24 | ```
25 |
26 | Once this operation is complete, simply add the service provider class and facade alias to your project's `config/app.php` file:
27 |
28 | #### Service Provider
29 | ```php
30 | Caffeinated\Menus\MenusServiceProvider::class,
31 | ```
32 |
33 | #### Facade
34 | ```php
35 | 'Menu' => Caffeinated\Menus\Facades\Menu::class,
36 | ```
37 |
38 | And that's it! With your coffee in reach, start building out some awesome menus!
39 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "caffeinated/menus",
3 | "description": "Laravel Menus",
4 | "keywords": ["menu", "navigation", "laravel", "caffeinated"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Shea Lewis",
9 | "email": "shea.lewis89@gmail.com"
10 | }
11 | ],
12 | "require": {
13 | "php": "^7.2|^8.0",
14 | "illuminate/support": "^6.0|^7.0|^8.0"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "Caffeinated\\Menus\\": "src/"
19 | }
20 | },
21 | "extra": {
22 | "laravel": {
23 | "providers": [
24 | "Caffeinated\\Menus\\MenusServiceProvider"
25 | ],
26 | "aliases": {
27 | "Menu": "Caffeinated\\Menus\\Facades\\Menu"
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Builder.php:
--------------------------------------------------------------------------------
1 | name = $name;
49 | $this->config = $config;
50 | $this->items = new Collection;
51 | }
52 |
53 | /**
54 | * Add an item to the defined menu.
55 | *
56 | * @param string $title
57 | * @param array|string $options
58 | *
59 | * @return Item
60 | */
61 | public function add($title, $options = '')
62 | {
63 | $item = new Item($this, $this->id(), $title, $options);
64 |
65 | $this->items->push($item);
66 |
67 | $this->lastId = $item->id;
68 |
69 | return $item;
70 | }
71 |
72 | /**
73 | * Generate a unique ID for every item added to the menu.
74 | *
75 | * @return int
76 | */
77 | protected function id()
78 | {
79 | return $this->lastId + 1;
80 | }
81 |
82 | /**
83 | * Extract the valid attributes from the passed options.
84 | *
85 | * @param array $options
86 | *
87 | * @return array
88 | */
89 | public function extractAttributes($options = array())
90 | {
91 | if (is_array($options)) {
92 | if (count($this->groupStack) > 0) {
93 | $options = $this->mergeWithLastGroup($options);
94 | }
95 |
96 | return Arr::except($options, $this->reserved);
97 | }
98 |
99 | return array();
100 | }
101 |
102 | /**
103 | * Insert a divider after the item.
104 | *
105 | * @param array $attributes
106 | *
107 | * @return void
108 | */
109 | public function divide($attributes = array())
110 | {
111 | $attributes['class'] = self::formatGroupClass(['class' => 'divider'], $attributes);
112 |
113 | $this->items->last()->divider = $attributes;
114 | }
115 |
116 | /**
117 | * Return the configuration value by key.
118 | *
119 | * @param string $key
120 | *
121 | * @return string
122 | */
123 | public function config($key)
124 | {
125 | return $this->config[$key];
126 | }
127 |
128 | /**
129 | * Get the prefix from the last group of the stack.
130 | *
131 | * @return mixed
132 | */
133 | public function getLastGroupPrefix()
134 | {
135 | if (count($this->groupStack) > 0) {
136 | return Arr::get(last($this->groupStack), 'prefix', '');
137 | }
138 |
139 | return null;
140 | }
141 |
142 | /**
143 | * Format the groups class.
144 | *
145 | * @return mixed
146 | */
147 | public static function formatGroupClass($new, $old)
148 | {
149 | if (isset($new['class'])) {
150 | $classes = trim(trim(Arr::get($old, 'class')).' '.trim(Arr::get($new, 'class')));
151 |
152 | return implode(' ', array_unique(explode(' ', $classes)));
153 | }
154 |
155 | return Arr::get($old, 'class');
156 | }
157 |
158 | /*
159 | |--------------------------------------------------------------------------
160 | | Fetching Methods
161 | |--------------------------------------------------------------------------
162 | |
163 | */
164 |
165 | /**
166 | * Fetches and returns all menu items.
167 | *
168 | * @return Collection
169 | */
170 | public function all()
171 | {
172 | return $this->items;
173 | }
174 |
175 | /**
176 | * Returns all items with no parents.
177 | *
178 | * @return Collection
179 | */
180 | public function roots()
181 | {
182 | return $this->whereParent();
183 | }
184 |
185 | /**
186 | * Fetches and returns a menu item by it's slug.
187 | *
188 | * @param string $slug
189 | *
190 | * @return Item
191 | */
192 | public function get($slug)
193 | {
194 | return $this->whereSlug($slug)->first();
195 | }
196 |
197 | /**
198 | * Facade method for the get() method.
199 | *
200 | * @param string $slug
201 | *
202 | * @return Item
203 | */
204 | public function item($slug)
205 | {
206 | return $this->get($slug);
207 | }
208 |
209 | /**
210 | * Fetches and returns a menu item by it's ID.
211 | *
212 | * @param integer $id
213 | *
214 | * @return Item
215 | */
216 | public function find($id)
217 | {
218 | return $this->whereId($id)->first();
219 | }
220 |
221 | /**
222 | * Fetches and returns the first menu item.
223 | *
224 | * @return Item
225 | */
226 | public function first()
227 | {
228 | return $this->items->first();
229 | }
230 |
231 | /**
232 | * Fetches and returns the last menu item.
233 | *
234 | * @return Item
235 | */
236 | public function last()
237 | {
238 | return $this->items->last();
239 | }
240 |
241 | /**
242 | * Fetches and returns all active state menu items.
243 | *
244 | * @return Collection
245 | */
246 | public function active()
247 | {
248 | $activeItems = array();
249 |
250 | foreach ($this->items as $item) {
251 | if ($item->data('active')) {
252 | $activeItems[] = $item;
253 | }
254 | }
255 |
256 | return $activeItems;
257 | }
258 |
259 | /*
260 | |--------------------------------------------------------------------------
261 | | Dispatch Methods
262 | |--------------------------------------------------------------------------
263 | |
264 | */
265 |
266 | /**
267 | * Get the action type from the options.
268 | *
269 | * @param array $options
270 | *
271 | * @return string
272 | */
273 | public function dispatch($options)
274 | {
275 | if (isset($options['url'])) {
276 | return $this->getUrl($options);
277 | } elseif (isset($options['route'])) {
278 | return $this->getRoute($options['route']);
279 | } elseif (isset($options['action'])) {
280 | return $this->getAction($options['action']);
281 | }
282 |
283 | return null;
284 | }
285 |
286 | /**
287 | * Get the action for a "url" option.
288 | *
289 | * @param array|string $options
290 | *
291 | * @return string
292 | */
293 | protected function getUrl($options)
294 | {
295 | foreach ($options as $key => $value) {
296 | $$key = $value;
297 | }
298 |
299 | $secure = (isset($options['secure']) and $options['secure'] === true) ? true : false;
300 |
301 | if ($prefix) {
302 | $prefix = $prefix.'/';
303 | }
304 |
305 | if (is_array($url)) {
306 | if (self::isAbsolute($url[0])) {
307 | return $url[0];
308 | }
309 |
310 | return url()->to($prefix.$url[0], array_slice($url, 1), $secure);
311 | }
312 |
313 | if (self::isAbsolute($url)) {
314 | return $url;
315 | }
316 |
317 | return url()->to($prefix.$url, array(), $secure);
318 | }
319 |
320 | /**
321 | * Get the route action for a "route" option.
322 | *
323 | * @param array|string $route
324 | *
325 | * @return string
326 | */
327 | protected function getRoute($route)
328 | {
329 | if (is_array($route)) {
330 | return url()->route($route[0], array_slice($route, 1));
331 | }
332 |
333 | return url()->route($route);
334 | }
335 |
336 | /**
337 | * Get the controller action for a "action" option.
338 | *
339 | * @param array|string $action
340 | *
341 | * @return string
342 | */
343 | protected function getAction($action)
344 | {
345 | if (is_array($action)) {
346 | return url()->action($action[0], array_slice($action, 1));
347 | }
348 |
349 | return url()->action($action);
350 | }
351 |
352 | /**
353 | * Determines if the given URL is absolute.
354 | *
355 | * @param string $url
356 | *
357 | * @return bool
358 | */
359 | public static function isAbsolute($url)
360 | {
361 | return parse_url($url, PHP_URL_SCHEME) or false;
362 | }
363 |
364 | /*
365 | |--------------------------------------------------------------------------
366 | | Filter Methods
367 | |--------------------------------------------------------------------------
368 | |
369 | */
370 |
371 | /**
372 | * Filter menu items through a callback.
373 | *
374 | * Since menu items are stored as a collection, this will
375 | * simply forward the callback to the Laravel Collection
376 | * filter() method and return the results.
377 | *
378 | * @param callable $callback
379 | *
380 | * @return Builder
381 | */
382 | public function filter($callback)
383 | {
384 | if (is_callable($callback)) {
385 | $this->items = $this->items->filter($callback);
386 | }
387 |
388 | return $this;
389 | }
390 |
391 | /**
392 | * Filter menu items recursively.
393 | *
394 | * @param string $attribute
395 | * @param mixed $value
396 | *
397 | * @return Collection
398 | */
399 | public function filterRecursively($attribute, $value)
400 | {
401 | $collection = new Collection;
402 |
403 | $this->items->each(function ($item) use ($attribute, $value, &$collection) {
404 | if (! property_exists($item, $attribute)) {
405 | return false;
406 | }
407 |
408 | if ($item->$attribute == $value) {
409 | $collection->push($item);
410 |
411 | if ($item->hasChildren()) {
412 | $collection = $collection->merge($this->filterRecursively($attribute, $item->id));
413 | }
414 | }
415 | });
416 |
417 | return $collection;
418 | }
419 |
420 | /**
421 | * Sorts the menu based on key given in ascending order.
422 | *
423 | * @param string $key
424 | *
425 | * @return Builder
426 | */
427 | public function sortBy($key)
428 | {
429 | $this->items = $this->items->sortBy(function ($item) use ($key) {
430 | return $item->$key;
431 | });
432 |
433 | return $this;
434 | }
435 |
436 | /**
437 | * Sorts the menu based on key given in descending order.
438 | *
439 | * @param string $key
440 | *
441 | * @return Builder
442 | */
443 | public function sortByDesc($key)
444 | {
445 | $this->items = $this->items->sortByDesc(function ($item) use ($key) {
446 | return $item->$key;
447 | });
448 |
449 | return $this;
450 | }
451 |
452 | /**
453 | * Filter menu items based on Shinobi permissions.
454 | *
455 | * @return Builder
456 | */
457 | public function guard()
458 | {
459 | if (class_exists('Caffeinated\Shinobi\Shinobi')) {
460 | $this->filter(function ($item) {
461 | if (! $item->data('can') and ! $item->data('canatleast')) {
462 | return true;
463 | } elseif ($item->data('canatleast')) {
464 | return \Shinobi::canAtLeast($item->data('canatleast'));
465 | } else {
466 | return \Shinobi::can($item->data('can'));
467 | }
468 | });
469 | }
470 |
471 | return $this;
472 | }
473 |
474 | /**
475 | * Dynamic search method against a menu attribute.
476 | *
477 | * @param string $method
478 | * @param array $args
479 | *
480 | * @return Item|bool
481 | */
482 | public function __call($method, $args)
483 | {
484 | preg_match('/^[W|w]here([a-zA-Z0-9_]+)$/', $method, $matches);
485 |
486 | if ($matches) {
487 | $attribute = Str::lower($matches[1]);
488 | } else {
489 | throw new BadMethodCallException('Call to undefined method '.$method);
490 | }
491 |
492 | $value = $args ? $args[0] : null;
493 | $recursive = isset($args[1]) ? $args[1] : false;
494 |
495 | if ($recursive) {
496 | return $this->filterRecursively($attribute, $value);
497 | }
498 |
499 | return $this->items->filter(function ($item) use ($attribute, $value) {
500 | if (isset($item->data[$attribute]) && $item->data[$attribute] == $value) {
501 | return true;
502 | }
503 |
504 | if (! property_exists($item, $attribute)) {
505 | return false;
506 | }
507 |
508 | if ($item->$attribute == $value) {
509 | return true;
510 | }
511 |
512 | return false;
513 | })->values();
514 | }
515 |
516 | /**
517 | * Returns menu item by name.
518 | *
519 | * @param string $property
520 | *
521 | * @return Item
522 | */
523 | public function __get($property)
524 | {
525 | if (property_exists($this, $property)) {
526 | return $this->$property;
527 | }
528 |
529 | return $this->whereSlug($property)->first();
530 | }
531 | }
532 |
--------------------------------------------------------------------------------
/src/Collection.php:
--------------------------------------------------------------------------------
1 | each(function($item) use ($args) {
19 | if (count($args) >= 2) {
20 | $item->attr($args[0], $args[1]);
21 | } else {
22 | $item->attr($args[0]);
23 | }
24 | });
25 |
26 | return $this;
27 | }
28 |
29 | /**
30 | * Add metadata to the collection of items.
31 | *
32 | * @param mixed
33 | * @return \Caffeinated\Menus\Collection
34 | */
35 | public function data()
36 | {
37 | $args = func_get_args();
38 |
39 | $this->each(function($item) use ($args) {
40 | if (count($args) >= 2) {
41 | $item->data($args[0], $args[1]);
42 | } else {
43 | $item->data($args[0]);
44 | }
45 | });
46 |
47 | return $this;
48 | }
49 |
50 | /**
51 | * Appends text or HTML to the collection of items.
52 | *
53 | * @param string $html
54 | * @return \Caffeinated\Menus\Collection
55 | */
56 | public function append($html)
57 | {
58 | $this->each(function($item) use ($html) {
59 | $item->title .= $html;
60 | });
61 |
62 | return $this;
63 | }
64 |
65 | /**
66 | * Prepends text or HTML to the collection of items.
67 | *
68 | * @param string $html
69 | * @param mixed $key
70 | * @return \Caffeinated\Menus\Collection
71 | */
72 | public function prepend($html, $key = null)
73 | {
74 | $this->each(function($item) use ($html) {
75 | $item->title = $html.$item->title;
76 | });
77 |
78 | return $this;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Facades/Menu.php:
--------------------------------------------------------------------------------
1 | builder = $builder;
61 | $this->id = $id;
62 | $this->title = $title;
63 | $this->slug = Str::camel(Str::slug($title, ' '));
64 | $this->attributes = $this->builder->extractAttributes($options);
65 | $this->parent = (is_array($options) and isset($options['parent'])) ? $options['parent'] : null;
66 |
67 | $this->configureLink($options);
68 | }
69 |
70 | public function builder()
71 | {
72 | return $this->builder;
73 | }
74 |
75 | /**
76 | * Configures the link for the menu item.
77 | *
78 | * @param array|string $options
79 | * @return null
80 | */
81 | public function configureLink($options)
82 | {
83 | if (! is_array($options)) {
84 | $path = ['url' => $options];
85 | } elseif (isset($options['raw']) and $options['raw'] == true) {
86 | $path = null;
87 | } else {
88 | $path = Arr::only($options, ['url', 'route', 'action', 'secure']);
89 | }
90 |
91 | if (! is_null($path)) {
92 | $path['prefix'] = $this->builder->getLastGroupPrefix();
93 | }
94 |
95 | $this->link = isset($path) ? new Link($path) : null;
96 |
97 | $this->checkActiveStatus();
98 | }
99 |
100 | /**
101 | * Adds a sub item to the menu.
102 | *
103 | * @param string $title
104 | * @param array|string $options
105 | * @return \Caffeinated\Menus\Item
106 | */
107 | public function add($title, $options = '')
108 | {
109 | if (! is_array($options)) {
110 | $url = $options;
111 | $options = array();
112 | $options['url'] = $url;
113 | }
114 |
115 | $options['parent'] = $this->id;
116 |
117 | return $this->builder->add($title, $options);
118 | }
119 |
120 | /**
121 | * Get all attributes.
122 | *
123 | * @return array
124 | */
125 | public function getAttributes()
126 | {
127 | return $this->attributes;
128 | }
129 |
130 | /**
131 | * Assign or fetch the desired attribute.
132 | *
133 | * @param array|string $attribute
134 | * @param string $value
135 | * @return mixed
136 | */
137 | public function attribute($attribute, $value = null)
138 | {
139 | if (isset($attribute) and is_array($attribute)) {
140 | if (array_key_exists('class', $attribute)) {
141 | $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $attribute['class']], $this->attributes);
142 | unset($attribute['class']);
143 | }
144 |
145 | $this->attributes = array_merge($this->attributes, $attribute);
146 |
147 | return $this;
148 | } elseif (isset($attribute) and isset($value)) {
149 | if ($attribute == 'class') {
150 | $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $value], $this->attributes);
151 | } else {
152 | $this->attributes[$attribute] = $value;
153 | }
154 |
155 | return $this;
156 | }
157 |
158 | return isset($this->attributes[$attribute]) ? $this->attributes[$attribute] : null;
159 | }
160 |
161 | /**
162 | * Generates a valid URL for the menu item.
163 | *
164 | * @return string
165 | */
166 | public function url()
167 | {
168 | if (! is_null($this->link)) {
169 | if ($this->link->href) {
170 | return $this->link->href;
171 | }
172 |
173 | return $this->builder->dispatch($this->link->path);
174 | }
175 | }
176 |
177 | /**
178 | * Prepends HTML to the item.
179 | *
180 | * @param string $html
181 | * @return \Caffeinated\Menus\Item
182 | */
183 | public function prepend($html)
184 | {
185 | $this->title = $html.' '.$this->title;
186 |
187 | return $this;
188 | }
189 |
190 | /**
191 | * Appends HTML to the item.
192 | *
193 | * @param string $html
194 | * @return \Caffeinated\Menus\Item
195 | */
196 | public function append($html)
197 | {
198 | $this->title = $this->title.' '.$html;
199 |
200 | return $this;
201 | }
202 |
203 | /**
204 | * Appends the specified icon to the item.
205 | *
206 | * @param string $icon
207 | * @param string $type Can be either "fontawesome" or "glyphicon"
208 | * @return \Caffeinated\Menus\Item
209 | */
210 | public function icon($icon, $type = 'fontawesome')
211 | {
212 | switch ($type) {
213 | case 'fontawesome':
214 | $html = '';
215 | break;
216 |
217 | case 'glyphicon':
218 | $html = '';
219 | break;
220 |
221 | case 'entypo':
222 | $html = '';
223 | break;
224 |
225 | default:
226 | $html = '';
227 | break;
228 | }
229 |
230 | return $this->data('icon', $html);
231 | }
232 |
233 | /**
234 | * Return the title with the icon prepended automatically.
235 | *
236 | * @return string
237 | */
238 | public function prependIcon()
239 | {
240 | return $this->prepend($this->data('icon'));
241 | }
242 |
243 | /**
244 | * Return the title with the icon appended automatically.
245 | *
246 | * @return string
247 | */
248 | public function appendIcon()
249 | {
250 | return $this->append($this->data('icon'));
251 | }
252 |
253 | /**
254 | * Insert a divider after the item.
255 | *
256 | * @param array $attributes
257 | * @return void
258 | */
259 | public function divide($attributes = array())
260 | {
261 | $attributes['class'] = $this->builder->formatGroupClass($attributes, ['class' => 'divider']);
262 |
263 | $this->divider = $attributes;
264 |
265 | return $this;
266 | }
267 |
268 | /**
269 | * Determines if the menu item has children.
270 | *
271 | * @return bool
272 | */
273 | public function hasChildren()
274 | {
275 | return count($this->builder->whereParent($this->id)) or false;
276 | }
277 |
278 | /**
279 | * Returns all children underneath the menu item.
280 | *
281 | * @return \Caffeinated\Menus\Collection
282 | */
283 | public function children()
284 | {
285 | return $this->builder->whereParent($this->id);
286 | }
287 |
288 | /**
289 | * Set or get an item's metadata.
290 | *
291 | * @param mixed
292 | * @return string|\Caffeinated\Menus\Item
293 | */
294 | public function data()
295 | {
296 | $args = func_get_args();
297 |
298 | if (isset($args[0]) and is_array($args[0])) {
299 | $this->data = array_merge($this->data, array_change_key_case($args[0]));
300 |
301 | return $this;
302 | } elseif (isset($args[0]) and isset($args[1])) {
303 | $this->data[strtolower($args[0])] = $args[1];
304 |
305 | return $this;
306 | } elseif (isset($args[0])) {
307 | return isset($this->data[$args[0]]) ? $this->data[$args[0]] : null;
308 | }
309 |
310 | return $this->data;
311 | }
312 |
313 | /**
314 | * Decide if the item should be active.
315 | *
316 | * @return null
317 | */
318 | public function checkActiveStatus()
319 | {
320 | $path = ltrim(parse_url($this->url(), PHP_URL_PATH), '/');
321 | $requestPath = Request::path();
322 |
323 | if (isset($this->builder->config) && $this->builder->config['rest_base']) {
324 | $base = (is_array($this->builder->config['rest_base'])) ? implode('|', $this->builder->config['rest_base']) : $this->builder->conf['rest_base'];
325 |
326 | list($path, $requestPath) = preg_replace('@^('.$base.')/@', '', [$path, $requestPath], 1);
327 | }
328 |
329 | if ($this->url() == Request::url() || $this->url() == \URL::secure(Request::path())) {
330 | $this->activate();
331 | }
332 | }
333 |
334 | public function activate(Item $item = null)
335 | {
336 | $item = (is_null($item)) ? $this : $item;
337 |
338 | $item->active();
339 |
340 | $item->data('active', true);
341 |
342 | if ($item->parent) {
343 | $parent = $this->builder->whereId($item->parent)->first();
344 | $parent->attributes['class'] = $parent->builder->formatGroupClass(['class' => 'opened'], $parent->attributes);
345 | $this->activate($parent);
346 | }
347 | }
348 |
349 | public function active($pattern = null)
350 | {
351 | if (! is_null($pattern)) {
352 | $pattern = ltrim(preg_replace('/\/\*/', '(/.*)?', $pattern), '/');
353 |
354 | if (preg_match("@{$pattern}\z@", Request::path())) {
355 | $this->activate();
356 | }
357 |
358 | return $this;
359 | }
360 |
361 | $this->attributes['class'] = $this->builder->formatGroupClass(['class' => 'active'], $this->attributes);
362 |
363 | return $this;
364 | }
365 |
366 | /**
367 | * Returns bool value if item is active or not.
368 | *
369 | * @return bool
370 | */
371 | public function isActive()
372 | {
373 | return $this->data('active');
374 | }
375 |
376 | public function can($permissions)
377 | {
378 | return $this->data('can', $permissions);
379 | }
380 |
381 | public function canAtLeast($permissions)
382 | {
383 | return $this->data('canatleast', $permissions);
384 | }
385 |
386 | /**
387 | * Return either a property or attribute item value.
388 | *
389 | * @param string $property
390 | * @return string
391 | */
392 | public function __get($property)
393 | {
394 | if (property_exists($this, $property)) {
395 | return $this->$property;
396 | }
397 |
398 | return $this->data($property);
399 | }
400 | }
401 |
--------------------------------------------------------------------------------
/src/Link.php:
--------------------------------------------------------------------------------
1 | path = $path;
29 | }
30 |
31 | /**
32 | * Set the link's href property.
33 | *
34 | * @param string $href
35 | * @return \Caffeinated\Menus\Link
36 | */
37 | public function href($href)
38 | {
39 | $this->href = $href;
40 |
41 | return $this;
42 | }
43 |
44 | /**
45 | * Make the link active.
46 | *
47 | * @return \Caffeinated\Menus\Link
48 | */
49 | public function active()
50 | {
51 | $this->attributes['class'] = Builder::formatGroupClass(['class' => 'active'], $this->attributes);
52 |
53 | return $this;
54 | }
55 |
56 | /**
57 | * Add attributes to the link.
58 | *
59 | * @param mixed
60 | * @return \Caffeinated\Menus\Link|string
61 | */
62 | public function attr()
63 | {
64 | $args = func_get_args();
65 |
66 | if (isset($args[0]) and is_array($args[0])) {
67 | $this->attributes = array_merge($this->attributes, $args[0]);
68 |
69 | return $this;
70 | } elseif (isset($args[0]) and isset($args[1])) {
71 | $this->attributes[$args[0]] = $args[1];
72 |
73 | return $this;
74 | } elseif (isset($args[0])) {
75 | return isset($this->attributes[$args[0]]) ? $this->attributes[$args[0]] : null;
76 | }
77 |
78 | return $this->attributes;
79 | }
80 |
81 | /**
82 | * Dynamically retrieve property value.
83 | *
84 | * @param string $property
85 | * @return mixed
86 | */
87 | public function __get($property)
88 | {
89 | if (property_exists($this, $property)) {
90 | return $this->$property;
91 | }
92 |
93 | return $this->attr($property);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/Menu.php:
--------------------------------------------------------------------------------
1 | config = $config;
26 | $this->collection = new Collection;
27 | }
28 |
29 | /**
30 | * Create a new menu instance.
31 | *
32 | * @param string $name
33 | * @param callable $callback
34 | * @return \Caffeinated\Menus\Builder
35 | */
36 | public function make($name, $callback)
37 | {
38 | if (is_callable($callback)) {
39 | $menu = new Builder($name, $this->loadConfig($name));
40 |
41 | call_user_func($callback, $menu);
42 |
43 | $this->collection->put($name, $menu);
44 |
45 | view()->share('menu_'.$name, $menu);
46 |
47 | return $menu;
48 | }
49 | }
50 |
51 | /**
52 | * Loads and merges configuration data.
53 | *
54 | * @param string $name
55 | * @return array
56 | */
57 | public function loadConfig($name)
58 | {
59 | $options = $this->config->get('menu.settings');
60 | $name = strtolower($name);
61 |
62 | if (isset($options[$name]) and is_array($options[$name])) {
63 | return array_merge($options['default'], $options[$name]);
64 | }
65 |
66 | return $options['default'] ?? null;
67 | }
68 |
69 | /**
70 | * Find and return the given menu collection.
71 | *
72 | * @param string $key
73 | * @return \Caffeinated\Menus\Collection
74 | */
75 | public function get($key)
76 | {
77 | return $this->collection->get($key);
78 | }
79 |
80 | /**
81 | * Returns all menu instances.
82 | *
83 | * @return \Caffeinated\Menus\Collection
84 | */
85 | public function all()
86 | {
87 | return $this->collection;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/MenusServiceProvider.php:
--------------------------------------------------------------------------------
1 | registerServices();
23 | }
24 |
25 | /**
26 | * Get the services provided by the provider.
27 | *
28 | * @return array
29 | */
30 | public function provides()
31 | {
32 | return ['menu'];
33 | }
34 |
35 | /**
36 | * Register the package services.
37 | *
38 | * @return void
39 | */
40 | protected function registerServices()
41 | {
42 | // Bind our Menu class to the IoC container
43 | $this->app->singleton('menu', function($app) {
44 | return new Menu($app['config']);
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------