MenuBuilder::disableOrdering()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Spinzar\Menu;
4
5
use Countable;
6
use Illuminate\Contracts\Config\Repository;
7
use Illuminate\View\Factory as ViewFactory;
8
use Illuminate\Support\Arr;
9
10
class MenuBuilder implements Countable
11
{
12
    /**
13
     * Menu name.
14
     *
15
     * @var string
16
     */
17
    protected $menu;
18
19
    /**
20
     * Array menu items.
21
     *
22
     * @var array
23
     */
24
    protected $items = [];
25
26
    /**
27
     * Default presenter class.
28
     *
29
     * @var string
30
     */
31
    protected $presenter = Presenters\Bootstrap3\Navbar::class;
32
33
    /**
34
     * Style name for each presenter.
35
     *
36
     * @var array
37
     */
38
    protected $styles = [];
39
40
    /**
41
     * Prefix URL.
42
     *
43
     * @var string|null
44
     */
45
    protected $prefixUrl;
46
47
    /**
48
     * The name of view presenter.
49
     *
50
     * @var string
51
     */
52
    protected $view;
53
54
    /**
55
     * The laravel view factory instance.
56
     *
57
     * @var \Illuminate\View\Factory
58
     */
59
    protected $views;
60
61
    /**
62
     * Determine whether the ordering feature is enabled or not.
63
     *
64
     * @var boolean
65
     */
66
    protected $ordering = false;
67
68
    /**
69
     * Resolved item binding map.
70
     *
71
     * @var array
72
     */
73
    protected $bindings = [];
74
    /**
75
     * @var Repository
76
     */
77
    private $config;
78
79
    /**
80
     * Constructor.
81
     *
82
     * @param string $menu
83
     * @param Repository $config
84
     */
85
    public function __construct($menu, Repository $config)
86
    {
87
        $this->menu = $menu;
88
        $this->config = $config;
89
    }
90
91
    /**
92
     * Get menu name.
93
     *
94
     * @return string
95
     */
96
    public function getName()
97
    {
98
        return $this->menu;
99
    }
100
101
    /**
102
     * Find menu item by title.
103
     *
104
     * @param  string        $title
105
     * @param  callable|null $callback
106
     * @return mixed
107
     */
108
    public function whereTitle($title, callable $callback = null)
109
    {
110
        $item = $this->findBy('title', $title);
111
112
        if (is_callable($callback)) {
113
            return call_user_func($callback, $item);
0 ignored issues
show
Bug introduced by
It seems like $callback can also be of type null; however, parameter $callback of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
            return call_user_func(/** @scrutinizer ignore-type */ $callback, $item);
Loading history...
114
        }
115
116
        return $item;
117
    }
118
119
    /**
120
     * Find menu item by key and value.
121
     *
122
     * @param  string $key
123
     * @param  string $value
124
     * @return \Spinzar\Menu\MenuItem
125
     */
126
    public function findBy($key, $value)
127
    {
128
        return collect($this->items)->filter(function ($item) use ($key, $value) {
129
            return $item->{$key} == $value;
130
        })->first();
131
    }
132
133
    /**
134
     * Remove menu item by title.
135
     *
136
     * @param  string $title
137
     * @return void
138
     */
139
    public function removeByTitle($title)
140
    {
141
        $this->removeBy('title', $title);
142
    }
143
144
    /**
145
     * Remove menu item by key and value.
146
     *
147
     * @param  string $key
148
     * @param  string $value
149
     * @return void
150
     */
151
    public function removeBy($key, $value)
152
    {
153
        $this->items = collect($this->items)->reject(function ($item) use ($key, $value) {
154
            return $item->{$key} == $value;
155
        })->values()->all();
156
    }
157
158
    /**
159
     * Set view factory instance.
160
     *
161
     * @param ViewFactory $views
162
     *
163
     * @return $this
164
     */
165
    public function setViewFactory(ViewFactory $views)
166
    {
167
        $this->views = $views;
168
169
        return $this;
170
    }
171
172
    /**
173
     * Set view.
174
     *
175
     * @param string $view
176
     *
177
     * @return $this
178
     */
179
    public function setView($view)
180
    {
181
        $this->view = $view;
182
183
        return $this;
184
    }
185
186
    /**
187
     * Set Prefix URL.
188
     *
189
     * @param string $prefixUrl
190
     *
191
     * @return $this
192
     */
193
    public function setPrefixUrl($prefixUrl)
194
    {
195
        $this->prefixUrl = $prefixUrl;
196
197
        return $this;
198
    }
199
200
    /**
201
     * Set styles.
202
     *
203
     * @param array $styles
204
     */
205
    public function setStyles(array $styles)
206
    {
207
        $this->styles = $styles;
208
    }
209
210
    /**
211
     * Set new presenter class.
212
     *
213
     * @param string $presenter
214
     */
215
    public function setPresenter($presenter)
216
    {
217
        $this->presenter = $presenter;
218
    }
219
220
    /**
221
     * Get presenter instance.
222
     *
223
     * @return \Spinzar\Menu\Presenters\PresenterInterface
224
     */
225
    public function getPresenter()
226
    {
227
        return new $this->presenter();
228
    }
229
230
    /**
231
     * Set new presenter class by given style name.
232
     *
233
     * @param string $name
234
     *
235
     * @return self
236
     */
237
    public function style($name)
238
    {
239
        if ($this->hasStyle($name)) {
240
            $this->setPresenter($this->getStyle($name));
241
        }
242
243
        return $this;
244
    }
245
246
    /**
247
     * Determine if the given name in the presenter style.
248
     *
249
     * @param $name
250
     *
251
     * @return bool
252
     */
253
    public function hasStyle($name)
254
    {
255
        return array_key_exists($name, $this->getStyles());
256
    }
257
258
    /**
259
     * Get style aliases.
260
     *
261
     * @return mixed
262
     */
263
    public function getStyles()
264
    {
265
        return $this->styles ?: $this->config->get('menu.styles');
266
    }
267
268
    /**
269
     * Get the presenter class name by given alias name.
270
     *
271
     * @param $name
272
     *
273
     * @return mixed
274
     */
275
    public function getStyle($name)
276
    {
277
        $style = $this->getStyles();
278
279
        return $style[$name];
280
    }
281
282
    /**
283
     * Set new presenter class from given alias name.
284
     *
285
     * @param $name
286
     */
287
    public function setPresenterFromStyle($name)
288
    {
289
        $this->setPresenter($this->getStyle($name));
290
    }
291
292
    /**
293
     * Set the resolved item bindings
294
     *
295
     * @param array $bindings
296
     * @return $this
297
     */
298
    public function setBindings(array $bindings)
299
    {
300
        $this->bindings = $bindings;
301
302
        return $this;
303
    }
304
305
    /**
306
     * Resolves a key from the bindings array.
307
     *
308
     * @param  string|array $key
309
     * @return mixed
310
     */
311
    public function resolve($key)
312
    {
313
        if (is_array($key)) {
314
            foreach ($key as $k => $v) {
315
                $key[$k] = $this->resolve($v);
316
            }
317
        } elseif (is_string($key)) {
0 ignored issues
show
introduced by
The condition is_string($key) is always true.
Loading history...
318
            $matches = array();
319
320
            preg_match_all('/{[\s]*?([^\s]+)[\s]*?}/i', $key, $matches, PREG_SET_ORDER);
321
322
            foreach ($matches as $match) {
323
                if (array_key_exists($match[1], $this->bindings)) {
324
                    $key = preg_replace('/' . $match[0] . '/', $this->bindings[$match[1]], $key, 1);
325
                }
326
            }
327
        }
328
329
        return $key;
330
    }
331
332
    /**
333
     * Resolves an array of menu items properties.
334
     *
335
     * @param  array  &$items
336
     * @return void
337
     */
338
    protected function resolveItems(array &$items)
339
    {
340
        $resolver = function ($property) {
341
            return $this->resolve($property) ?: $property;
342
        };
343
344
        $totalItems = count($items);
345
        for ($i = 0; $i < $totalItems; $i++) {
346
            $items[$i]->fill(array_map($resolver, $items[$i]->getProperties()));
347
        }
348
    }
349
350
    /**
351
     * Add new child menu.
352
     *
353
     * @param array $attributes
354
     *
355
     * @return \Spinzar\Menu\MenuItem
356
     */
357
    public function add(array $attributes = array())
358
    {
359
        $item = MenuItem::make($attributes);
360
361
        $this->items[] = $item;
362
363
        return $item;
364
    }
365
366
    /**
367
     * Create new menu with dropdown.
368
     *
369
     * @param $title
370
     * @param callable $callback
371
     * @param array    $attributes
372
     *
373
     * @return $this
374
     */
375
    public function dropdown($title, \Closure $callback, $order = null, array $attributes = array())
376
    {
377
        $properties = compact('title', 'order', 'attributes');
378
379
        if (func_num_args() == 3) {
380
            $arguments = func_get_args();
381
382
            $title = Arr::get($arguments, 0);
383
            $attributes = Arr::get($arguments, 2);
384
385
            $properties = compact('title', 'attributes');
386
        }
387
388
        $item = MenuItem::make($properties);
389
390
        call_user_func($callback, $item);
391
392
        $this->items[] = $item;
393
394
        return $item;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $item returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
395
    }
396
397
    /**
398
     * Register new menu item using registered route.
399
     *
400
     * @param $route
401
     * @param $title
402
     * @param array $parameters
403
     * @param array $attributes
404
     *
405
     * @return static
406
     */
407
    public function route($route, $title, $parameters = array(), $order = null, $attributes = array())
408
    {
409
        if (func_num_args() == 4) {
410
            $arguments = func_get_args();
411
412
            return $this->add([
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->add(array(...r::get($arguments, 3))) returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
413
                'route' => [Arr::get($arguments, 0), Arr::get($arguments, 2)],
414
                'title' => Arr::get($arguments, 1),
415
                'attributes' => Arr::get($arguments, 3),
416
            ]);
417
        }
418
419
        $route = array($route, $parameters);
420
421
        $item = MenuItem::make(
422
            compact('route', 'title', 'parameters', 'attributes', 'order')
423
        );
424
425
        $this->items[] = $item;
426
427
        return $item;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $item returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
428
    }
429
430
    /**
431
     * Format URL.
432
     *
433
     * @param string $url
434
     *
435
     * @return string
436
     */
437
    protected function formatUrl($url)
438
    {
439
        $uri = !is_null($this->prefixUrl) ? $this->prefixUrl . $url : $url;
440
441
        return $uri == '/' ? '/' : ltrim(rtrim($uri, '/'), '/');
442
    }
443
444
    /**
445
     * Register new menu item using url.
446
     *
447
     * @param $url
448
     * @param $title
449
     * @param array $attributes
450
     *
451
     * @return static
452
     */
453
    public function url($url, $title, $order = 0, $attributes = array())
454
    {
455
        if (func_num_args() == 3) {
456
            $arguments = func_get_args();
457
458
            return $this->add([
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->add(array(...r::get($arguments, 2))) returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
459
                'url' => $this->formatUrl(Arr::get($arguments, 0)),
460
                'title' => Arr::get($arguments, 1),
461
                'attributes' => Arr::get($arguments, 2),
462
            ]);
463
        }
464
465
        $url = $this->formatUrl($url);
466
467
        $item = MenuItem::make(compact('url', 'title', 'order', 'attributes'));
468
469
        $this->items[] = $item;
470
471
        return $item;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $item returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
472
    }
473
474
    /**
475
     * Add new divider item.
476
     *
477
     * @param int $order
478
     * @return \Spinzar\Menu\MenuItem
479
     */
480
    public function addDivider($order = null)
481
    {
482
        $this->items[] = new MenuItem(array('name' => 'divider', 'order' => $order));
483
484
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Spinzar\Menu\MenuBuilder which is incompatible with the documented return type Spinzar\Menu\MenuItem.
Loading history...
485
    }
486
487
    /**
488
     * Add new header item.
489
     *
490
     * @return \Spinzar\Menu\MenuItem
491
     */
492
    public function addHeader($title, $order = null)
493
    {
494
        $this->items[] = new MenuItem(array(
495
            'name' => 'header',
496
            'title' => $title,
497
            'order' => $order,
498
        ));
499
500
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Spinzar\Menu\MenuBuilder which is incompatible with the documented return type Spinzar\Menu\MenuItem.
Loading history...
501
    }
502
503
    /**
504
     * Alias for "addHeader" method.
505
     *
506
     * @param string $title
507
     *
508
     * @return $this
509
     */
510
    public function header($title)
511
    {
512
        return $this->addHeader($title);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->addHeader($title) returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
513
    }
514
515
    /**
516
     * Alias for "addDivider" method.
517
     *
518
     * @return $this
519
     */
520
    public function divider()
521
    {
522
        return $this->addDivider();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->addDivider() returns the type Spinzar\Menu\MenuItem which is incompatible with the documented return type Spinzar\Menu\MenuBuilder.
Loading history...
523
    }
524
525
    /**
526
     * Get items count.
527
     *
528
     * @return int
529
     */
530
    public function count()
531
    {
532
        return count($this->items);
533
    }
534
535
    /**
536
     * Empty the current menu items.
537
     */
538
    public function destroy()
539
    {
540
        $this->items = array();
541
542
        return $this;
543
    }
544
545
    /**
546
     * Render the menu to HTML tag.
547
     *
548
     * @param string $presenter
549
     *
550
     * @return string
551
     */
552
    public function render($presenter = null)
553
    {
554
        $this->resolveItems($this->items);
555
556
        if (!is_null($this->view)) {
0 ignored issues
show
introduced by
The condition is_null($this->view) is always false.
Loading history...
557
            return $this->renderView($presenter);
558
        }
559
560
        if ($this->hasStyle($presenter)) {
561
            $this->setPresenterFromStyle($presenter);
562
        }
563
564
        if (!is_null($presenter) && !$this->hasStyle($presenter)) {
565
            $this->setPresenter($presenter);
566
        }
567
568
        return $this->renderMenu();
569
    }
570
571
    /**
572
     * Render menu via view presenter.
573
     *
574
     * @return \Illuminate\View\View
575
     */
576
    public function renderView($presenter = null)
577
    {
578
        return $this->views->make($presenter ?: $this->view, [
579
            'items' => $this->getOrderedItems(),
580
        ]);
581
    }
582
583
    /**
584
     * Get original items.
585
     *
586
     * @return array
587
     */
588
    public function getItems()
589
    {
590
        return $this->items;
591
    }
592
593
    /**
594
     * Get menu items as laravel collection instance.
595
     *
596
     * @return \Illuminate\Support\Collection
597
     */
598
    public function toCollection()
599
    {
600
        return collect($this->items);
601
    }
602
603
    /**
604
     * Get menu items as array.
605
     *
606
     * @return array
607
     */
608
    public function toArray()
609
    {
610
        return $this->toCollection()->toArray();
611
    }
612
613
    /**
614
     * Enable menu ordering.
615
     *
616
     * @return self
617
     */
618
    public function enableOrdering()
619
    {
620
        $this->ordering = true;
621
622
        return $this;
623
    }
624
625
    /**
626
     * Disable menu ordering.
627
     *
628
     * @return self
629
     */
630
    public function disableOrdering()
631
    {
632
        $this->ordering = false;
633
634
        return $this;
635
    }
636
637
    /**
638
     * Get menu items and order it by 'order' key.
639
     *
640
     * @return array
641
     */
642
    public function getOrderedItems()
643
    {
644
        if (config('menu.ordering') || $this->ordering) {
645
            return $this->toCollection()->sortBy(function ($item) {
646
                return $item->order;
647
            })->all();
648
        }
649
650
        return $this->items;
651
    }
652
653
    /**
654
     * Render the menu.
655
     *
656
     * @return string
657
     */
658
    protected function renderMenu()
659
    {
660
        $presenter = $this->getPresenter();
661
        $menu = $presenter->getOpenTagWrapper();
662
663
        foreach ($this->getOrderedItems() as $item) {
664
            if ($item->hidden()) {
665
                continue;
666
            }
667
668
            if ($item->hasSubMenu()) {
669
                $menu .= $presenter->getMenuWithDropDownWrapper($item);
670
            } elseif ($item->isHeader()) {
671
                $menu .= $presenter->getHeaderWrapper($item);
672
            } elseif ($item->isDivider()) {
673
                $menu .= $presenter->getDividerWrapper();
674
            } else {
675
                $menu .= $presenter->getMenuWithoutDropdownWrapper($item);
676
            }
677
        }
678
679
        $menu .= $presenter->getCloseTagWrapper();
680
681
        return $menu;
682
    }
683
}
684