Passed
Pull Request — master (#57)
by Kyle
07:54
created

MenuItem::hasRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 1
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace KyleMassacre\Menus;
4
5
use AllowDynamicProperties;
0 ignored issues
show
Bug introduced by
The type AllowDynamicProperties was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Collective\Html\HtmlFacade as HTML;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Facades\Request;
9
use KyleMassacre\Menus\Contracts\MenuItemContract;
10
use KyleMassacre\Menus\Traits\CanHide;
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
 * @property array badge
23
 */
24
#[AllowDynamicProperties] class MenuItem extends MenuItemContract
25
{
26
    use CanHide;
27
28
    /**
29
     * Constructor.
30
     *
31
     * @param array $properties
32
     */
33
    public function __construct(array $properties = array())
34
    {
35
        $this->properties = $properties;
36
        $this->fill($properties);
37
    }
38
39
    /**
40
     * Fill the attributes.
41
     *
42
     * @param array $attributes
43
     */
44
    public function fill(array $attributes): void
45
    {
46
        foreach ($attributes as $key => $value) {
47
            if (in_array($key, $this->fillable)) {
48
                $this->{$key} = $value;
49
            }
50
        }
51
    }
52
53
    /**
54
     * Create new menu child item using array.
55
     *
56
     * @param array $attributes
57
     *
58
     * @return self
59
     */
60
    public function child(array $attributes): self
61
    {
62
        $this->childs[] = static::make($attributes);
63
64
        return $this;
65
    }
66
67
    /**
68
     * Register new child menu with dropdown.
69 24
     *
70
     * @param $title
71 24
     * @param \Closure $callback
72 24
     * @param int $order
73 24
     * @param array $attributes
74
     * @return $this
75
     */
76
    public function dropdown(string $title, \Closure $callback, int $order = 0, array $attributes = array()): static
77
    {
78
        $properties = compact('title', 'order', 'attributes');
79
80
        if (func_num_args() === 3) {
81
            $arguments = func_get_args();
82 24
83
            $title = Arr::get($arguments, 0);
84 24
            $attributes = Arr::get($arguments, 2);
85 24
86 1
            $properties = compact('title', 'attributes');
87
        }
88 1
89
        $child = static::make($properties);
90 1
91
        call_user_func($callback, $child);
92
93 23
        $this->childs[] = $child;
94
95
        return $child;
96
    }
97
98
    /**
99
     * Create new menu item and set the action to route.
100
     *
101
     * @param $route
102
     * @param $title
103
     * @param array $parameters
104
     * @param array $attributes
105
     *
106
     * @return MenuItemContract
107
     */
108
    public function route($route, $title, array $parameters = array(), $order = 0, array $attributes = array()): static
109
    {
110
        if (func_num_args() === 4) {
111
            $arguments = func_get_args();
112
113
            return $this->add([
114
                'route' => [Arr::get($arguments, 0), Arr::get($arguments, 2)],
115 24
                'title' => Arr::get($arguments, 1),
116
                'attributes' => Arr::get($arguments, 3),
117 24
            ]);
118
        }
119 24
120
        $route = array($route, $parameters);
121
122
        return $this->add(compact('route', 'title', 'order', 'attributes'));
123
    }
124
125
    /**
126
     * Create new menu item  and set the action to url.
127 24
     *
128
     * @param $url
129 24
     * @param $title
130 23
     * @param array $attributes
131 23
     *
132
     * @return static
133
     */
134 24
    public function url($url, $title, $order = 0, array $attributes = array()): static
135
    {
136
        if (func_num_args() === 3) {
137
            $arguments = func_get_args();
138
139
            return $this->add([
140
                'url' => Arr::get($arguments, 0),
141
                'title' => Arr::get($arguments, 1),
142
                'attributes' => Arr::get($arguments, 2),
143 2
            ]);
144
        }
145 2
146
        return $this->add(compact('url', 'title', 'order', 'attributes'));
147 2
    }
148
149
    /**
150
     * Add new divider.
151
     *
152
     * @param int $order
153
     *
154
     * @return self
155
     */
156
    public function addDivider($order = null): static
157
    {
158 9
        $item = static::make(array('name' => 'divider', 'order' => $order));
159
160 9
        $this->childs[] = $item;
161
162 9
        return $item;
163
    }
164
165
    /**
166
     * Alias method instead "addDivider".
167
     *
168
     * @param int $order
169
     *
170
     * @return MenuItem
171 9
     */
172
    public function divider($order = null): static
173 9
    {
174
        return $this->addDivider($order);
175 9
    }
176
177 9
    /**
178
     * Add dropdown header.
179
     *
180
     * @param $title
181
     *
182
     * @return $this
183
     */
184
    public function addHeader($title): static
185
    {
186
        $item = static::make(array(
187
            'name' => 'header',
188
            'title' => $title,
189
        ));
190 3
191
        $this->childs[] = $item;
192 3
193
        return $item;
194
    }
195
196
    /**
197
     * Same with "addHeader" method.
198
     *
199
     * @param $title
200
     *
201
     * @return $this
202 3
     */
203
    public function header($title): static
204 3
    {
205
        return $this->addHeader($title);
206
    }
207
208
    public function addBadge(string $type, $text): static
209
    {
210
        $properties = array(
211
            'type' => $type,
212
            'text' => $text,
213
            'name' => 'badge',
214
        );
215
        $item = static::make($properties);
216 6
        $this->badge = $properties;
217
218 6
        return $item;
219
    }
220
221
    /**
222
     * @deprecated See `getChildren`
223
     * @return array
224
     */
225
    public function getChilds(): array
226
    {
227
        return $this->getChildren();
228 6
    }
229
230
    /**
231
     * Get childs.
232
     *
233
     * @return array
234
     */
235
    public function getChildren(): array
236
    {
237
        if (config('menus.ordering')) {
238 9
            return collect($this->childs)->sortBy('order')->all();
0 ignored issues
show
Bug introduced by
$this->childs of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

238
            return collect(/** @scrutinizer ignore-type */ $this->childs)->sortBy('order')->all();
Loading history...
239
        }
240 9
241
        return $this->childs;
242 9
    }
243
244 9
    /**
245
     * Get url.
246
     *
247
     * @return string
248
     */
249
    public function getUrl(): string
250
    {
251
        if ($this->route !== null) {
252
            return route($this->route[0], $this->route[1]);
253
        }
254 1
255
        if (empty($this->url)) {
256 1
            return url('/#');
257
        }
258 1
259
        return url($this->url);
260 1
    }
261
262
    /**
263
     * Get request url.
264
     *
265
     * @return string
266
     */
267
    public function getRequest(): string
268
    {
269
        return ltrim(str_replace(url('/'), '', $this->getUrl()), '/');
270 1
    }
271
272 1
    /**
273
     * @return string
274
     */
275
    public function getBadge(): string
276
    {
277
        if($this->hasBadge()) {
278
            extract($this->badge);
279
280
            return '<span class="' . $type . '">' . $text . '</span>';
281
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 277 is false. This is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
282 2
    }
283
284 2
    /**
285 2
     * Get icon.
286 2
     *
287
     * @param string|null $default
288
     *
289 2
     * @return string|null
290
     */
291 2
    public function getIcon(string $default = null): ?string
292
    {
293
        if ($this->icon !== null && $this->icon !== '') {
294
            return '<i class="' . $this->icon . '"></i>';
295
        }
296
        if ($default === null) {
297
            return $default;
298
        }
299
300
        return '<i class="' . $default . '"></i>';
301 2
    }
302
303 2
    /**
304
     * Get properties.
305
     *
306
     * @return array
307
     */
308
    public function getProperties(): array
309
    {
310
        return $this->properties;
311 10
    }
312
313 10
    /**
314 1
     * Get HTML attribute data.
315
     *
316
     * @return mixed
317 9
     */
318
    public function getAttributes(): mixed
319
    {
320
        $attributes = $this->attributes ?: [];
321
322
        Arr::forget($attributes, ['active', 'icon']);
323
324
        return HTML::attributes($attributes);
325 3
    }
326
327 3
    /**
328 1
     * Check is the current item divider.
329
     *
330
     * @return bool
331 2
     */
332
    public function isDivider(): bool
333
    {
334
        return $this->is('divider');
335 2
    }
336
337
    /**
338
     * Check is the current item divider.
339
     *
340
     * @return bool
341
     */
342
    public function isHeader(): bool
343 1
    {
344
        return $this->is('header');
345 1
    }
346
347
    /**
348
     * Check is the current item divider.
349
     *
350
     * @param $name
351
     *
352
     * @return bool
353
     */
354
    public function is($name): bool
355 3
    {
356
        return $this->name == $name;
357 3
    }
358 1
359
    /**
360 2
     * Check is the current item has sub menu .
361 1
     *
362
     * @return bool
363
     */
364 1
    public function hasSubMenu(): bool
365
    {
366
        return !empty($this->childs);
367
    }
368
369
    /**
370
     * @deprecated Same with hasSubMenu.
371
     *
372 2
     * @return bool
373
     */
374 2
    public function hasChilds(): bool
375
    {
376
        return $this->hasSubMenu();
377
    }
378
379
    /**
380
     * Check the active state for current menu.
381
     *
382 1
     * @return mixed
383
     */
384 1
    public function hasActiveOnChild(): mixed
385
    {
386 1
        if ($this->inactive()) {
387
            return false;
388 1
        }
389
390
        return $this->hasSubMenu() && $this->getActiveStateFromChildren();
391
    }
392
393
    public function getActiveStateFromChildren(): bool
394
    {
395
        foreach ($this->getChildren() as $child) {
396 1
            if ($child->inactive()) {
397
                continue;
398 1
            }
399
            if ($child->hasChilds()) {
400
                if ($child->getActiveStateFromChilds()) {
401
                    return true;
402
                }
403
            } elseif ($child->isActive()) {
404
                return true;
405
            } elseif ($child->hasRoute() && $child->getActiveStateFromRoute()) {
406 1
                return true;
407
            } elseif ($child->getActiveStateFromUrl()) {
408 1
                return true;
409
            }
410
        }
411
412
        return false;
413
    }
414
415
    /**
416
     * @deprecated See `getActiveStateFromChildren()`
417
     * Get active state from child menu items.
418 2
     *
419
     * @return bool
420 2
     */
421
    public function getActiveStateFromChilds(): bool
422
    {
423
        return $this->getActiveStateFromChildren();
424
    }
425
426
    /**
427
     * Get inactive state.
428 1
     *
429
     * @return bool
430 1
     */
431
    public function inactive(): bool
432
    {
433
        $inactive = $this->getInactiveAttribute();
434
435
        if (is_bool($inactive)) {
0 ignored issues
show
introduced by
The condition is_bool($inactive) is always false.
Loading history...
436
            return $inactive;
437
        }
438 1
439
        if ($inactive instanceof \Closure) {
0 ignored issues
show
introduced by
$inactive is never a sub-type of Closure.
Loading history...
440 1
            return call_user_func($inactive);
441
        }
442
443
        return false;
444
    }
445
446
    /**
447
     * Get active attribute.
448
     *
449
     * @return string
450
     */
451
    public function getActiveAttribute(): string
452
    {
453
        return Arr::get($this->attributes, 'active');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...->attributes, 'active') could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
454
    }
455
456
    /**
457
     * Get inactive attribute.
458
     *
459
     * @return string
460
     */
461
    public function getInactiveAttribute(): string
462
    {
463
        return Arr::get($this->attributes, 'inactive');
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Suppor...attributes, 'inactive') could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
464
    }
465
466
    /**
467
     * Get active state for current item.
468
     *
469
     * @return mixed
470
     */
471
    public function isActive(): mixed
472
    {
473
        if ($this->inactive()) {
474
            return false;
475
        }
476
477
        $active = $this->getActiveAttribute();
478
479
        if (is_bool($active)) {
0 ignored issues
show
introduced by
The condition is_bool($active) is always false.
Loading history...
480
            return $active;
481
        }
482
483
        if ($active instanceof \Closure) {
0 ignored issues
show
introduced by
$active is never a sub-type of Closure.
Loading history...
484
            return call_user_func($active);
485
        }
486
487
        if ($this->hasRoute()) {
488
            return $this->getActiveStateFromRoute();
489
        }
490
491
        return $this->getActiveStateFromUrl();
492
    }
493
494
    /**
495
     * Determine the current item using route.
496
     *
497
     * @return bool
498
     */
499
    protected function hasRoute(): bool
500
    {
501
        return !empty($this->route);
502
    }
503
504
    /**
505
     * Get active status using route.
506
     *
507
     * @return bool
508
     */
509
    protected function getActiveStateFromRoute(): bool
510
    {
511
        return Request::is(str_replace(url('/') . '/', '', $this->getUrl()));
512
    }
513
514
    /**
515
     * Get active status using request url.
516
     *
517
     * @return bool
518
     */
519
    protected function getActiveStateFromUrl(): bool
520
    {
521
        return Request::is($this->url);
522
    }
523
524
    /**
525
     * Set order value.
526
     *
527
     * @param  int $order
528
     * @return self
529
     */
530
    public function order(int $order): self
531
    {
532
        $this->order = $order;
533
534
        return $this;
535
    }
536
537
    public function hasBadge(): bool
538
    {
539
        return !empty($this->badge);
540
    }
541
}
542