Completed
Push — master ( 843223...7f5174 )
by
unknown
08:52
created

SecondaryMenuGroupTrait::addLink()   C

Complexity

Conditions 12
Paths 50

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 32
nc 50
nop 2
dl 0
loc 55
rs 6.8009
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Charcoal\Admin\Ui\SecondaryMenu;
4
5
use ArrayIterator;
6
use InvalidArgumentException;
7
8
// From 'charcoal-admin'
9
use Charcoal\Admin\Widget\SecondaryMenuWidgetInterface;
10
11
/**
12
 * Provides an implementation of {@see \Charcoal\Admin\Ui\SecondaryMenu\SecondaryMenuGroupInterface}.
13
 */
14
trait SecondaryMenuGroupTrait
15
{
16
    /**
17
     * Store a reference to the parent secondary menu widget.
18
     *
19
     * @var SecondaryMenuWidgetInterface
20
     */
21
    protected $secondaryMenu;
22
23
    /**
24
     * The secondary menu group ID.
25
     *
26
     * @var string
27
     */
28
    protected $groupId;
29
30
    /**
31
     * The group's display type.
32
     *
33
     * @var string
34
     */
35
    protected $displayType;
36
37
    /**
38
     * Whether the item is selected or not.
39
     *
40
     * @var boolean
41
     */
42
    protected $isSelected = false;
43
44
    /**
45
     * Whether the group is collapsible or not.
46
     *
47
     * @var boolean
48
     */
49
    private $collapsible = false;
50
51
    /**
52
     * Whether the group is collapsed or not.
53
     *
54
     * @var boolean
55
     */
56
    private $collapsed;
57
58
    /**
59
     * Whether the group has siblings or not.
60
     *
61
     * @var boolean
62
     */
63
    private $parented = false;
64
65
    /**
66
     * The group's links.
67
     *
68
     * @var array
69
     */
70
    protected $links;
71
72
    /**
73
     * The group's identifier.
74
     *
75
     * @var string
76
     */
77
    private $ident;
78
79
    /**
80
     * Whether the group is active.
81
     *
82
     * @var boolean
83
     */
84
    private $active = true;
85
86
    /**
87
     * Set the secondary menu widget.
88
     *
89
     * @param  SecondaryMenuWidgetInterface $menu The related secondary menu widget.
90
     * @return self
91
     */
92
    public function setSecondaryMenu(SecondaryMenuWidgetInterface $menu)
93
    {
94
        $this->secondaryMenu = $menu;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Retrieve the secondary menu widget.
101
     *
102
     * @return SecondaryMenuWidgetInterface
103
     */
104
    public function secondaryMenu()
105
    {
106
        return $this->secondaryMenu;
107
    }
108
109
    /**
110
     * Set the group ID.
111
     *
112
     * @param  string $id The group ID.
113
     * @throws InvalidArgumentException If the group ID argument is not a string.
114
     * @return self
115
     */
116
    public function setGroupId($id)
117
    {
118
        if (!is_string($id)) {
0 ignored issues
show
introduced by
The condition is_string($id) is always true.
Loading history...
119
            throw new InvalidArgumentException(
120
                'Group ID must be a string'
121
            );
122
        }
123
124
        $this->groupId = $id;
125
126
        return $this;
127
    }
128
129
    /**
130
     * Retrieve the group ID.
131
     *
132
     * @return string
133
     */
134
    public function groupId()
135
    {
136
        if ($this->groupId === null) {
137
            $this->groupId = uniqid('secondary_menu_group_');
138
        }
139
140
        return $this->groupId;
141
    }
142
143
    /**
144
     * Set the display type of the group.
145
     *
146
     * @param  mixed $type The display type.
147
     * @throws InvalidArgumentException If the display type is invalid.
148
     * @return self
149
     */
150
    public function setDisplayType($type)
151
    {
152
        if (!is_string($type)) {
153
            throw new InvalidArgumentException('The display type must be a string.');
154
        }
155
156
        $this->displayType = $type;
157
158
        return $this;
159
    }
160
161
    /**
162
     * Retrieve the display type of the group.
163
     *
164
     * @return string|null
165
     */
166
    public function displayType()
167
    {
168
        return $this->displayType;
169
    }
170
171
    /**
172
     * Set the secondary menu links.
173
     *
174
     * @param  array $links A collection of link objects.
175
     * @return self
176
     */
177
    public function setLinks(array $links)
178
    {
179
        $this->links = new ArrayIterator();
0 ignored issues
show
Documentation Bug introduced by
It seems like new ArrayIterator() of type ArrayIterator is incompatible with the declared type array of property $links.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
180
181
        foreach ($links as $linkIdent => $link) {
182
            $this->addLink($linkIdent, $link);
183
        }
184
185
        return $this;
186
    }
187
188
    /**
189
     * Set the secondary menu links.
190
     *
191
     * @param  string       $linkIdent The link identifier.
192
     * @param  array|object $link      The link object or structure.
193
     * @throws InvalidArgumentException If the link is invalid.
194
     * @return self
195
     */
196
    public function addLink($linkIdent, $link)
197
    {
198
        if (!is_string($linkIdent) && !is_numeric($linkIdent)) {
0 ignored issues
show
introduced by
The condition is_string($linkIdent) is always true.
Loading history...
199
            throw new InvalidArgumentException(
200
                'Link identifier must be a string or '
201
            );
202
        }
203
204
        if (is_array($link)) {
205
            $active = true;
206
            $name = null;
207
            $url = null;
208
            $permissions = [];
209
210
            if (isset($link['active'])) {
211
                $active = !!$link['active'];
212
            }
213
214
            if (isset($link['name'])) {
215
                $name = $this->translator()->translation($link['name']);
0 ignored issues
show
Bug introduced by
It seems like translator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

215
                $name = $this->/** @scrutinizer ignore-call */ translator()->translation($link['name']);
Loading history...
216
            }
217
218
            if (isset($link['url'])) {
219
                $url = $this->translator()->translation($link['url']);
220
            }
221
222
            if (isset($link['required_acl_permissions'])) {
223
                $permissions = $link['required_acl_permissions'];
224
            }
225
226
            if ($name === null && $url === null) {
227
                return $this;
228
            }
229
230
            $isSelected = $this->secondaryMenu()->isCurrentItem([ $linkIdent, (string)$url ]);
0 ignored issues
show
Bug introduced by
The method isCurrentItem() does not exist on Charcoal\Admin\Widget\SecondaryMenuWidgetInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Charcoal\Admin\Widget\SecondaryMenuWidgetInterface. ( Ignorable by Annotation )

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

230
            $isSelected = $this->secondaryMenu()->/** @scrutinizer ignore-call */ isCurrentItem([ $linkIdent, (string)$url ]);
Loading history...
231
232
            if ($isSelected) {
233
                $this->isSelected(true);
234
            }
235
236
            $this->links[$linkIdent] = [
237
                'active'   => $active,
238
                'name'     => $name,
239
                'url'      => $url,
240
                'selected' => $isSelected,
241
                'required_acl_permissions' => $permissions
242
            ];
243
        } else {
244
            throw new InvalidArgumentException(sprintf(
245
                'Link must be an associative array, received %s',
246
                (is_object($link) ? get_class($link) : gettype($link))
247
            ));
248
        }
249
250
        return $this;
251
    }
252
253
    /**
254
     * Retrieve the secondary menu links.
255
     *
256
     * @return ArrayIterator|\Generator
257
     */
258
    public function links()
259
    {
260
        if (!is_array($this->links) && !($this->links instanceof \Traversable)) {
0 ignored issues
show
introduced by
The condition is_array($this->links) is always true.
Loading history...
261
            $this->links = [];
262
        }
263
264
        foreach ($this->links as $link) {
265
            if (isset($link['active']) && !$link['active']) {
266
                continue;
267
            }
268
269
            if (isset($link['required_acl_permissions'])) {
270
                $link['permissions'] = $link['required_acl_permissions'];
271
                unset($link['required_acl_permissions']);
272
            }
273
274
            if (isset($link['permissions'])) {
275
                if ($this->hasPermissions($link['permissions']) === false) {
0 ignored issues
show
Bug introduced by
It seems like hasPermissions() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

275
                if ($this->/** @scrutinizer ignore-call */ hasPermissions($link['permissions']) === false) {
Loading history...
276
                    continue;
277
                }
278
            }
279
280
            yield $link;
281
        }
282
    }
283
284
    /**
285
     * Determine if the secondary menu has any links.
286
     *
287
     * @return boolean
288
     */
289
    public function hasLinks()
290
    {
291
        return !!$this->numLinks();
292
    }
293
294
    /**
295
     * Count the number of secondary menu links.
296
     *
297
     * @return integer
298
     */
299
    public function numLinks()
300
    {
301
        if (!is_array($this->links) && !($this->links instanceof \Traversable)) {
0 ignored issues
show
introduced by
The condition is_array($this->links) is always true.
Loading history...
302
            return 0;
303
        }
304
305
        $links = array_filter($this->links, function ($link) {
0 ignored issues
show
Bug introduced by
It seems like $this->links can also be of type Traversable; however, parameter $input of array_filter() does only seem to accept array, 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

305
        $links = array_filter(/** @scrutinizer ignore-type */ $this->links, function ($link) {
Loading history...
306
            if (isset($link['active']) && !$link['active']) {
307
                return false;
308
            }
309
310
            if (isset($link['required_acl_permissions'])) {
311
                $link['permissions'] = $link['required_acl_permissions'];
312
                unset($link['required_acl_permissions']);
313
            }
314
315
            if (isset($link['permissions'])) {
316
                if ($this->hasPermissions($link['permissions']) === false) {
317
                    return false;
318
                }
319
            }
320
321
            return true;
322
        });
323
324
        return count($links);
325
    }
326
327
    /**
328
     * Set whether the item is selected or not.
329
     *
330
     * @param  boolean|null $flag Whether the item is selected or not.
331
     * @return boolean
332
     */
333
    public function isSelected($flag = null)
334
    {
335
        if ($flag !== null) {
336
            $this->isSelected = !!$flag;
337
338
            $this->setCollapsed(!$flag);
339
        }
340
341
        return $this->isSelected;
342
    }
343
344
    /**
345
     * Determine if the secondary groups should be displayed as panels.
346
     *
347
     * @return boolean
348
     */
349
    public function displayAsPanel()
350
    {
351
        return in_array($this->displayType(), [ 'panel', 'collapsible' ]);
352
    }
353
354
    /**
355
     * Determine if the group is collapsible.
356
     *
357
     * @return boolean
358
     */
359
    public function collapsible()
360
    {
361
        return ($this->displayType() === 'collapsible');
362
    }
363
364
    /**
365
     * Set whether the group is collapsed or not.
366
     *
367
     * @param  boolean $flag Whether the group is collapsed or not.
368
     * @return self
369
     */
370
    public function setCollapsed($flag)
371
    {
372
        $this->collapsed = !!$flag;
373
374
        return $this;
375
    }
376
377
    /**
378
     * Determine if the group is collapsed.
379
     *
380
     * @return boolean
381
     */
382
    public function collapsed()
383
    {
384
        $collapsed = $this->collapsible();
385
386
        if (is_bool($this->collapsed)) {
0 ignored issues
show
introduced by
The condition is_bool($this->collapsed) is always true.
Loading history...
387
            $collapsed = $this->collapsed;
388
        }
389
390
        if (is_bool($this->isSelected())) {
0 ignored issues
show
introduced by
The condition is_bool($this->isSelected()) is always true.
Loading history...
391
            $collapsed = !$this->isSelected;
392
        }
393
394
        return $collapsed;
395
    }
396
397
    /**
398
     * Set whether the group is related to other groups.
399
     *
400
     * @param  boolean $flag Whether the group has siblings or not.
401
     * @return self
402
     */
403
    public function setParented($flag)
404
    {
405
        $this->parented = !!$flag;
406
407
        return $this;
408
    }
409
410
    /**
411
     * Determine if the group is related to other groups.
412
     *
413
     * @return boolean
414
     */
415
    public function parented()
416
    {
417
        return $this->parented;
418
    }
419
420
    /**
421
     * Set the identifier of the group.
422
     *
423
     * @param  string $ident The group identifier.
424
     * @return self
425
     */
426
    public function setIdent($ident)
427
    {
428
        $this->ident = $ident;
429
430
        return $this;
431
    }
432
433
    /**
434
     * Retrieve the idenfitier of the group.
435
     *
436
     * @return string
437
     */
438
    public function ident()
439
    {
440
        return $this->ident;
441
    }
442
443
    /**
444
     * Set whether the group is active or not.
445
     *
446
     * @param  boolean $active The active flag.
447
     * @return self
448
     */
449
    public function setActive($active)
450
    {
451
        $this->active = !!$active;
452
453
        return $this;
454
    }
455
456
    /**
457
     * Determine if the group is active or not.
458
     *
459
     * @return boolean
460
     */
461
    public function active()
462
    {
463
        return $this->active;
464
    }
465
}
466