MenuItem::fill()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Spinzar\Menu;
4
5
use Closure;
6
use Collective\Html\HtmlFacade as HTML;
7
use Illuminate\Contracts\Support\Arrayable as ArrayableContract;
8
use Illuminate\Support\Facades\Request;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Str;
11
12
/**
13
 * @property string url
14
 * @property string route
15
 * @property string title
16
 * @property string name
17
 * @property string icon
18
 * @property int parent
19
 * @property array attributes
20
 * @property bool active
21
 * @property int order
22
 */
23
class MenuItem implements ArrayableContract
24
{
25
    /**
26
     * Array properties.
27
     *
28
     * @var array
29
     */
30
    protected $properties = [];
31
32
    /**
33
     * The child collections for current menu item.
34
     *
35
     * @var array
36
     */
37
    protected $childs = array();
38
39
    /**
40
     * The fillable attribute.
41
     *
42
     * @var array
43
     */
44
    protected $fillable = array(
45
        'url',
46
        'route',
47
        'title',
48
        'name',
49
        'icon',
50
        'parent',
51
        'attributes',
52
        'active',
53
        'order',
54
        'hideWhen',
55
    );
56
57
    /**
58
     * The hideWhen callback.
59
     *
60
     * @var Closure
61
     */
62
    protected $hideWhen;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param array $properties
68
     */
69
    public function __construct($properties = array())
70
    {
71
        $this->properties = $properties;
72
        $this->fill($properties);
73
    }
74
75
    /**
76
     * Set the icon property when the icon is defined in the link attributes.
77
     *
78
     * @param array $properties
79
     *
80
     * @return array
81
     */
82
    protected static function setIconAttribute(array $properties)
83
    {
84
        $icon = Arr::get($properties, 'attributes.icon');
85
        if (!is_null($icon)) {
86
            $properties['icon'] = $icon;
87
88
            Arr::forget($properties, 'attributes.icon');
89
90
            return $properties;
91
        }
92
93
        return $properties;
94
    }
95
96
    /**
97
     * Get random name.
98
     *
99
     * @param array $attributes
100
     *
101
     * @return string
102
     */
103
    protected static function getRandomName(array $attributes)
104
    {
105
        return substr(md5(Arr::get($attributes, 'title', Str::random(6))), 0, 5);
106
    }
107
108
    /**
109
     * Create new static instance.
110
     *
111
     * @param array $properties
112
     *
113
     * @return static
114
     */
115
    public static function make(array $properties)
116
    {
117
        $properties = self::setIconAttribute($properties);
118
119
        return new static($properties);
120
    }
121
122
    /**
123
     * Fill the attributes.
124
     *
125
     * @param array $attributes
126
     */
127
    public function fill($attributes)
128
    {
129
        foreach ($attributes as $key => $value) {
130
            if (in_array($key, $this->fillable)) {
131
                $this->{$key} = $value;
132
            }
133
        }
134
    }
135
136
    /**
137
     * Create new menu child item using array.
138
     *
139
     * @param $attributes
140
     *
141
     * @return $this
142
     */
143
    public function child($attributes)
144
    {
145
        $this->childs[] = static::make($attributes);
146
147
        return $this;
148
    }
149
150
    /**
151
     * Register new child menu with dropdown.
152
     *
153
     * @param $title
154
     * @param callable $callback
155
     *
156
     * @return $this
157
     */
158
    public function dropdown($title, \Closure $callback, $order = 0, array $attributes = array())
159
    {
160
        $properties = compact('title', 'order', 'attributes');
161
162
        if (func_num_args() === 3) {
163
            $arguments = func_get_args();
164
165
            $title = Arr::get($arguments, 0);
166
            $attributes = Arr::get($arguments, 2);
167
168
            $properties = compact('title', 'attributes');
169
        }
170
171
        $child = static::make($properties);
172
173
        call_user_func($callback, $child);
174
175
        $this->childs[] = $child;
176
177
        return $child;
178
    }
179
180
    /**
181
     * Create new menu item and set the action to route.
182
     *
183
     * @param $route
184
     * @param $title
185
     * @param array $parameters
186
     * @param array $attributes
187
     *
188
     * @return MenuItem
189
     */
190
    public function route($route, $title, $parameters = array(), $order = 0, $attributes = array())
191
    {
192
        if (func_num_args() === 4) {
193
            $arguments = func_get_args();
194
195
            return $this->add([
196
                'route' => [Arr::get($arguments, 0), Arr::get($arguments, 2)],
197
                'title' => Arr::get($arguments, 1),
198
                'attributes' => Arr::get($arguments, 3),
199
            ]);
200
        }
201
202
        $route = array($route, $parameters);
203
204
        return $this->add(compact('route', 'title', 'order', 'attributes'));
205
    }
206
207
    /**
208
     * Create new menu item  and set the action to url.
209
     *
210
     * @param $url
211
     * @param $title
212
     * @param array $attributes
213
     *
214
     * @return MenuItem
215
     */
216
    public function url($url, $title, $order = 0, $attributes = array())
217
    {
218
        if (func_num_args() === 3) {
219
            $arguments = func_get_args();
220
221
            return $this->add([
222
                'url' => Arr::get($arguments, 0),
223
                'title' => Arr::get($arguments, 1),
224
                'attributes' => Arr::get($arguments, 2),
225
            ]);
226
        }
227
228
        return $this->add(compact('url', 'title', 'order', 'attributes'));
229
    }
230
231
    /**
232
     * Add new child item.
233
     *
234
     * @param array $properties
235
     *
236
     * @return $this
237
     */
238
    public function add(array $properties)
239
    {
240
        $item = static::make($properties);
241
242
        $this->childs[] = $item;
243
244
        return $item;
245
    }
246
247
    /**
248
     * Add new divider.
249
     *
250
     * @param int $order
251
     *
252
     * @return self
253
     */
254
    public function addDivider($order = null)
255
    {
256
        $item = static::make(array('name' => 'divider', 'order' => $order));
257
258
        $this->childs[] = $item;
259
260
        return $item;
261
    }
262
263
    /**
264
     * Alias method instead "addDivider".
265
     *
266
     * @param int $order
267
     *
268
     * @return MenuItem
269
     */
270
    public function divider($order = null)
271
    {
272
        return $this->addDivider($order);
273
    }
274
275
    /**
276
     * Add dropdown header.
277
     *
278
     * @param $title
279
     *
280
     * @return $this
281
     */
282
    public function addHeader($title)
283
    {
284
        $item = static::make(array(
285
            'name' => 'header',
286
            'title' => $title,
287
        ));
288
289
        $this->childs[] = $item;
290
291
        return $item;
292
    }
293
294
    /**
295
     * Same with "addHeader" method.
296
     *
297
     * @param $title
298
     *
299
     * @return $this
300
     */
301
    public function header($title)
302
    {
303
        return $this->addHeader($title);
304
    }
305
306
    /**
307
     * Get childs.
308
     *
309
     * @return array
310
     */
311
    public function getChilds()
312
    {
313
        if (config('menu.ordering')) {
314
            return collect($this->childs)->sortBy('order')->all();
315
        }
316
317
        return $this->childs;
318
    }
319
320
    /**
321
     * Get url.
322
     *
323
     * @return string
324
     */
325
    public function getUrl()
326
    {
327
        if ($this->route !== null) {
328
            return route($this->route[0], $this->route[1]);
329
        }
330
331
        if (empty($this->url)) {
332
            return url("/#");
333
        }
334
335
        return url($this->url);
336
    }
337
338
    /**
339
     * Get request url.
340
     *
341
     * @return string
342
     */
343
    public function getRequest()
344
    {
345
        return ltrim(str_replace(url('/'), '', $this->getUrl()), '/');
346
    }
347
348
    /**
349
     * Get icon.
350
     *
351
     * @param null|string $default
352
     *
353
     * @return string
354
     */
355
    public function getIcon($default = null)
356
    {
357
        if ($this->icon !== null && $this->icon !== '') {
358
            return '<i class="' . $this->icon . '"></i>';
359
        }
360
        if ($default === null) {
361
            return $default;
362
        }
363
364
        return '<i class="' . $default . '"></i>';
365
    }
366
367
    /**
368
     * Get properties.
369
     *
370
     * @return array
371
     */
372
    public function getProperties()
373
    {
374
        return $this->properties;
375
    }
376
377
    /**
378
     * Get HTML attribute data.
379
     *
380
     * @return mixed
381
     */
382
    public function getAttributes()
383
    {
384
        $attributes = $this->attributes ? $this->attributes : [];
385
386
        Arr::forget($attributes, ['active', 'icon']);
387
388
        return HTML::attributes($attributes);
389
    }
390
391
    /**
392
     * Check is the current item divider.
393
     *
394
     * @return bool
395
     */
396
    public function isDivider()
397
    {
398
        return $this->is('divider');
399
    }
400
401
    /**
402
     * Check is the current item divider.
403
     *
404
     * @return bool
405
     */
406
    public function isHeader()
407
    {
408
        return $this->is('header');
409
    }
410
411
    /**
412
     * Check is the current item divider.
413
     *
414
     * @param $name
415
     *
416
     * @return bool
417
     */
418
    public function is($name)
419
    {
420
        return $this->name == $name;
421
    }
422
423
    /**
424
     * Check is the current item has sub menu .
425
     *
426
     * @return bool
427
     */
428
    public function hasSubMenu()
429
    {
430
        return !empty($this->childs);
431
    }
432
433
    /**
434
     * Same with hasSubMenu.
435
     *
436
     * @return bool
437
     */
438
    public function hasChilds()
439
    {
440
        return $this->hasSubMenu();
441
    }
442
443
    /**
444
     * Check the active state for current menu.
445
     *
446
     * @return mixed
447
     */
448
    public function hasActiveOnChild()
449
    {
450
        if ($this->inactive()) {
451
            return false;
452
        }
453
454
        return $this->hasChilds() ? $this->getActiveStateFromChilds() : false;
455
    }
456
457
    /**
458
     * Get active state from child menu items.
459
     *
460
     * @return bool
461
     */
462
    public function getActiveStateFromChilds()
463
    {
464
        foreach ($this->getChilds() as $child) {
465
            if ($child->inactive()) {
466
                continue;
467
            }
468
469
            if ($child->hasChilds()) {
470
                if ($child->getActiveStateFromChilds()) {
471
                    return true;
472
                }
473
            } elseif ($child->isActive()) {
474
                return true;
475
            } elseif ($child->hasRoute() && $child->getActiveStateFromRoute()) {
476
                return true;
477
            } elseif ($child->getActiveStateFromUrl()) {
478
                return true;
479
            }
480
        }
481
482
        return false;
483
    }
484
485
    /**
486
     * Get inactive state.
487
     *
488
     * @return bool
489
     */
490
    public function inactive()
491
    {
492
        $inactive = $this->getInactiveAttribute();
493
494
        if (is_bool($inactive)) {
0 ignored issues
show
introduced by
The condition is_bool($inactive) is always false.
Loading history...
495
            return $inactive;
496
        }
497
498
        if ($inactive instanceof \Closure) {
0 ignored issues
show
introduced by
$inactive is never a sub-type of Closure.
Loading history...
499
            return call_user_func($inactive);
500
        }
501
502
        return false;
503
    }
504
505
    /**
506
     * Get active attribute.
507
     *
508
     * @return string
509
     */
510
    public function getActiveAttribute()
511
    {
512
        return Arr::get($this->attributes, 'active');
513
    }
514
515
    /**
516
     * Get inactive attribute.
517
     *
518
     * @return string
519
     */
520
    public function getInactiveAttribute()
521
    {
522
        return Arr::get($this->attributes, 'inactive');
523
    }
524
525
    /**
526
     * Get active state for current item.
527
     *
528
     * @return mixed
529
     */
530
    public function isActive()
531
    {
532
        if ($this->inactive()) {
533
            return false;
534
        }
535
536
        $active = $this->getActiveAttribute();
537
538
        if (is_bool($active)) {
0 ignored issues
show
introduced by
The condition is_bool($active) is always false.
Loading history...
539
            return $active;
540
        }
541
542
        if ($active instanceof \Closure) {
0 ignored issues
show
introduced by
$active is never a sub-type of Closure.
Loading history...
543
            return call_user_func($active);
544
        }
545
546
        if ($this->hasRoute()) {
547
            return $this->getActiveStateFromRoute();
548
        }
549
550
        return $this->getActiveStateFromUrl();
551
    }
552
553
    /**
554
     * Determine the current item using route.
555
     *
556
     * @return bool
557
     */
558
    protected function hasRoute()
559
    {
560
        return !empty($this->route);
561
    }
562
563
    /**
564
     * Get active status using route.
565
     *
566
     * @return bool
567
     */
568
    protected function getActiveStateFromRoute()
569
    {
570
        return $this->checkActiveState(str_replace(url('/') . '/', '', $this->getUrl()));
571
    }
572
573
    /**
574
     * Get active status using request url.
575
     *
576
     * @return bool
577
     */
578
    protected function getActiveStateFromUrl()
579
    {
580
        return $this->checkActiveState($this->url);
581
    }
582
583
    /**
584
     * Check the active state.
585
     *
586
     * @return bool
587
     */
588
    protected function checkActiveState($url)
589
    {
590
        if (empty($url) || in_array($url, config('menu.home_urls', ['/']))) {
591
            return Request::is($url);
592
        } else {
593
            return Request::is($url, $url . '/*');
594
        }
595
    }
596
597
    /**
598
     * Set order value.
599
     *
600
     * @param  int $order
601
     * @return self
602
     */
603
    public function order($order)
604
    {
605
        $this->order = $order;
606
607
        return $this;
608
    }
609
610
    /**
611
     * Set hide condition for current menu item.
612
     *
613
     * @param  Closure
614
     * @return boolean
615
     */
616
    public function hideWhen(Closure $callback)
617
    {
618
        $this->hideWhen = $callback;
619
620
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type boolean.
Loading history...
621
    }
622
623
    /**
624
     * Determine whether the menu item is hidden.
625
     *
626
     * @return boolean
627
     */
628
    public function hidden()
629
    {
630
        if (is_null($this->hideWhen)) {
631
            return false;
632
        }
633
634
        return call_user_func($this->hideWhen) == true;
635
    }
636
637
    /**
638
     * Get the instance as an array.
639
     *
640
     * @return array
641
     */
642
    public function toArray()
643
    {
644
        return $this->getProperties();
645
    }
646
647
    /**
648
     * Get property.
649
     *
650
     * @param string $key
651
     *
652
     * @return string|null
653
     */
654
    public function __get($key)
655
    {
656
        return isset($this->$key) ? $this->$key : null;
657
    }
658
}
659