Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

NodeMenu   F

Complexity

Total Complexity 89

Size/Duplication

Total Lines 674
Duplicated Lines 9.64 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 89
lcom 2
cbo 7
dl 65
loc 674
ccs 0
cts 245
cp 0
rs 1.926
c 0
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A setLocale() 0 4 1
A setCurrentNode() 0 4 1
A setPermission() 0 8 2
A setIncludeOffline() 0 4 1
A setIncludeHiddenFromNav() 0 8 2
B init() 0 40 5
A getTopNodes() 9 26 5
A getCurrent() 10 10 2
A getActiveForDepth() 9 9 2
A getBreadCrumb() 0 33 4
B getChildren() 23 37 6
A getTopNodeMenuItems() 14 22 3
A getSiblings() 0 17 4
A getPreviousSibling() 0 16 5
A getNextSibling() 0 19 5
A getParent() 0 13 3
A getNodeBySlug() 0 5 1
F getNodeByInternalName() 0 89 21
A getRootNodeMenuItem() 0 22 3
A isIncludeOffline() 0 4 1
A getPermission() 0 4 1
A getUser() 0 4 1
A getEntityManager() 0 4 1
A getTokenStorage() 0 4 1
A getAclHelper() 0 4 1
A getLocale() 0 4 1
A isIncludeHiddenFromNav() 0 4 1
A getActive() 0 11 3
A isInitialized() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like NodeMenu often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NodeMenu, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kunstmaan\NodeBundle\Helper;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface;
7
use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper;
8
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionMap;
9
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
10
use Kunstmaan\NodeBundle\Entity\Node;
11
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
12
use Kunstmaan\NodeBundle\Repository\NodeRepository;
13
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
14
15
class NodeMenu
16
{
17
    /**
18
     * @var EntityManagerInterface
19
     */
20
    private $em;
21
22
    /**
23
     * @var TokenStorageInterface
24
     */
25
    private $tokenStorage;
26
27
    /**
28
     * @var AclHelper
29
     */
30
    private $aclHelper;
31
32
    /**
33
     * @var string
34
     */
35
    private $locale;
36
37
    /**
38
     * @var Node
39
     */
40
    private $currentNode;
41
42
    /**
43
     * @var string
44
     */
45
    private $permission = PermissionMap::PERMISSION_VIEW;
46
47
    /**
48
     * @var bool
49
     */
50
    private $includeOffline = false;
51
52
    /**
53
     * @var bool
54
     */
55
    private $includeHiddenFromNav = false;
56
57
    /**
58
     * @var NodeMenuItem[]
59
     */
60
    private $topNodeMenuItems;
61
62
    /**
63
     * @var NodeMenuItem[]
64
     */
65
    private $breadCrumb;
66
67
    /**
68
     * @var Node[]
69
     */
70
    private $allNodes = [];
71
72
    /**
73
     * @var Node[]
74
     */
75
    private $childNodes = [];
76
77
    /**
78
     * @var Node[]
79
     */
80
    private $nodesByInternalName = [];
81
82
    /**
83
     * @var bool
84
     */
85
    private $initialized = false;
86
87
    /**
88
     * @var NodeMenuItem
89
     */
90
    private $rootNodeMenuItem;
91
92
    /**
93
     * @var DomainConfigurationInterface
94
     */
95
    private $domainConfiguration;
96
97
    /**
98
     * @param EntityManagerInterface       $em                  The entity manager
99
     * @param TokenStorageInterface        $tokenStorage        The security token storage
100
     * @param AclHelper                    $aclHelper           The ACL helper pages
101
     * @param DomainConfigurationInterface $domainConfiguration The current domain configuration
102
     */
103
    public function __construct(
104
        EntityManagerInterface $em,
105
        TokenStorageInterface $tokenStorage,
106
        AclHelper $aclHelper,
107
        DomainConfigurationInterface $domainConfiguration
108
    ) {
109
        $this->em = $em;
110
        $this->tokenStorage = $tokenStorage;
111
        $this->aclHelper = $aclHelper;
112
        $this->domainConfiguration = $domainConfiguration;
113
    }
114
115
    /**
116
     * @param string $locale
117
     */
118
    public function setLocale($locale)
119
    {
120
        $this->locale = $locale;
121
    }
122
123
    /**
124
     * @param Node $currentNode
0 ignored issues
show
Documentation introduced by
Should the type for parameter $currentNode not be null|Node?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
125
     */
126
    public function setCurrentNode(Node $currentNode = null)
127
    {
128
        $this->currentNode = $currentNode;
129
    }
130
131
    /**
132
     * @param string $permission
133
     */
134
    public function setPermission($permission)
135
    {
136
        if ($this->permission !== $permission) {
137
            // For now reset initialized flag when cached data has to be reset ...
138
            $this->initialized = false;
139
        }
140
        $this->permission = $permission;
141
    }
142
143
    /**
144
     * @param bool $includeOffline
145
     */
146
    public function setIncludeOffline($includeOffline)
147
    {
148
        $this->includeOffline = $includeOffline;
149
    }
150
151
    /**
152
     * @param bool $includeHiddenFromNav
153
     */
154
    public function setIncludeHiddenFromNav($includeHiddenFromNav)
155
    {
156
        if ($this->includeHiddenFromNav !== $includeHiddenFromNav) {
157
            // For now reset initialized flag when cached data has to be reset ...
158
            $this->initialized = false;
159
        }
160
        $this->includeHiddenFromNav = $includeHiddenFromNav;
161
    }
162
163
    /**
164
     * This method initializes the nodemenu only once, the method may be
165
     * executed multiple times
166
     */
167
    private function init()
168
    {
169
        if ($this->initialized) {
170
            return;
171
        }
172
173
        $this->allNodes = [];
174
        $this->breadCrumb = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<Kun...e\Helper\NodeMenuItem>> of property $breadCrumb.

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...
175
        $this->childNodes = [];
176
        $this->topNodeMenuItems = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<Kun...e\Helper\NodeMenuItem>> of property $topNodeMenuItems.

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...
177
        $this->nodesByInternalName = [];
178
179
        /* @var NodeRepository $repo */
180
        $repo = $this->em->getRepository(Node::class);
181
182
        // Get all possible menu items in one query (also fetch offline nodes)
183
        $nodes = $repo->getChildNodes(
184
            false,
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a integer|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
185
            $this->locale,
186
            $this->permission,
187
            $this->aclHelper,
188
            $this->includeHiddenFromNav,
189
            true,
190
            $this->domainConfiguration->getRootNode()
191
        );
192
        foreach ($nodes as $node) {
193
            $this->allNodes[$node->getId()] = $node;
194
195
            if ($node->getParent()) {
196
                $this->childNodes[$node->getParent()->getId()][] = $node;
197
            } else {
198
                $this->childNodes[0][] = $node;
199
            }
200
            $internalName = $node->getInternalName();
201
            if ($internalName) {
202
                $this->nodesByInternalName[$internalName][] = $node;
203
            }
204
        }
205
        $this->initialized = true;
206
    }
207
208
    /**
209
     * @return NodeMenuItem[]
210
     */
211
    public function getTopNodes()
212
    {
213
        $this->init();
214
        if (!\is_array($this->topNodeMenuItems)) {
215
            $this->topNodeMenuItems = [];
216
217
            // To be backwards compatible we need to create the top node MenuItems
218
            if (\array_key_exists(0, $this->childNodes)) {
219
                $topNodeMenuItems = $this->getTopNodeMenuItems();
220
221
                $includeHiddenFromNav = $this->includeHiddenFromNav;
222
                $this->topNodeMenuItems = array_filter(
223
                    $topNodeMenuItems,
224 View Code Duplication
                    function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
                        if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) {
226
                            return false;
227
                        }
228
229
                        return true;
230
                    }
231
                );
232
            }
233
        }
234
235
        return $this->topNodeMenuItems;
236
    }
237
238
    /**
239
     * @return NodeMenuItem[]
240
     */
241
    public function getBreadCrumb()
242
    {
243
        $this->init();
244
        if (!\is_array($this->breadCrumb)) {
245
            $this->breadCrumb = [];
246
247
            /* @var NodeRepository $repo */
248
            $repo = $this->em->getRepository(Node::class);
249
250
            // Generate breadcrumb MenuItems - fetch *all* languages so you can link translations if needed
251
            $parentNodes = $repo->getAllParents($this->currentNode);
252
            $parentNodeMenuItem = null;
253
            /* @var Node $parentNode */
254
            foreach ($parentNodes as $parentNode) {
255
                $nodeTranslation = $parentNode->getNodeTranslation(
256
                    $this->locale,
257
                    $this->includeOffline
258
                );
259
                if (!\is_null($nodeTranslation)) {
260
                    $nodeMenuItem = new NodeMenuItem(
261
                        $parentNode,
262
                        $nodeTranslation,
263
                        $parentNodeMenuItem,
264
                        $this
265
                    );
266
                    $this->breadCrumb[] = $nodeMenuItem;
267
                    $parentNodeMenuItem = $nodeMenuItem;
268
                }
269
            }
270
        }
271
272
        return $this->breadCrumb;
273
    }
274
275
    /**
276
     * @return NodeMenuItem|null
277
     */
278 View Code Duplication
    public function getCurrent()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
    {
280
        $this->init();
281
        $breadCrumb = $this->getBreadCrumb();
282
        if (\count($breadCrumb) > 0) {
283
            return $breadCrumb[\count($breadCrumb) - 1];
284
        }
285
286
        return null;
287
    }
288
289
    /**
290
     * @param int $depth
291
     *
292
     * @return NodeMenuItem|null
293
     */
294 View Code Duplication
    public function getActiveForDepth($depth)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
295
    {
296
        $breadCrumb = $this->getBreadCrumb();
297
        if (\count($breadCrumb) >= $depth) {
298
            return $breadCrumb[$depth - 1];
299
        }
300
301
        return null;
302
    }
303
304
    /**
305
     * @param bool $includeHiddenFromNav
306
     *
307
     * @return NodeMenuItem[]
308
     */
309
    public function getChildren(Node $node, $includeHiddenFromNav = true)
310
    {
311
        $this->init();
312
        $children = [];
313
314
        if (\array_key_exists($node->getId(), $this->childNodes)) {
315
            $nodes = $this->childNodes[$node->getId()];
316
            /* @var Node $childNode */
317 View Code Duplication
            foreach ($nodes as $childNode) {
0 ignored issues
show
Bug introduced by
The expression $nodes of type object<Kunstmaan\NodeBundle\Entity\Node> is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
318
                $nodeTranslation = $childNode->getNodeTranslation(
319
                    $this->locale,
320
                    $this->includeOffline
321
                );
322
                if (!\is_null($nodeTranslation)) {
323
                    $children[] = new NodeMenuItem(
324
                        $childNode,
325
                        $nodeTranslation,
326
                        false,
327
                        $this
328
                    );
329
                }
330
            }
331
332
            $children = array_filter(
333
                $children,
334 View Code Duplication
                function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
335
                    if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) {
336
                        return false;
337
                    }
338
339
                    return true;
340
                }
341
            );
342
        }
343
344
        return $children;
345
    }
346
347
    /**
348
     * @param bool $includeHiddenFromNav
349
     *
350
     * @return array|\Kunstmaan\NodeBundle\Helper\NodeMenuItem[]
351
     */
352
    public function getSiblings(Node $node, $includeHiddenFromNav = true)
353
    {
354
        $this->init();
355
        $siblings = [];
356
357
        if (false !== $parent = $this->getParent($node)) {
358
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
359
360
            foreach ($siblings as $index => $child) {
361
                if ($child === $node) {
362
                    unset($siblings[$index]);
363
                }
364
            }
365
        }
366
367
        return $siblings;
368
    }
369
370
    /**
371
     * @param bool $includeHiddenFromNav
372
     *
373
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NodeMenuItem|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
374
     */
375
    public function getPreviousSibling(Node $node, $includeHiddenFromNav = true)
376
    {
377
        $this->init();
378
379
        if (false !== $parent = $this->getParent($node)) {
380
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
381
382
            foreach ($siblings as $index => $child) {
383
                if ($child->getNode() === $node && ($index - 1 >= 0)) {
384
                    return $siblings[$index - 1];
385
                }
386
            }
387
        }
388
389
        return false;
390
    }
391
392
    /**
393
     * @param bool $includeHiddenFromNav
394
     *
395
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NodeMenuItem|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
396
     */
397
    public function getNextSibling(Node $node, $includeHiddenFromNav = true)
398
    {
399
        $this->init();
400
401
        if (false !== $parent = $this->getParent($node)) {
402
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
403
404
            foreach ($siblings as $index => $child) {
405
                if ($child->getNode() === $node && (($index + 1) < \count(
406
                            $siblings
407
                        ))
408
                ) {
409
                    return $siblings[$index + 1];
410
                }
411
            }
412
        }
413
414
        return false;
415
    }
416
417
    /**
418
     * @return NodeMenuItem
0 ignored issues
show
Documentation introduced by
Should the return type not be Node|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
419
     */
420
    public function getParent(Node $node)
421
    {
422
        $this->init();
423
        if ($node->getParent() && \array_key_exists(
424
                $node->getParent()->getId(),
425
                $this->allNodes
426
            )
427
        ) {
428
            return $this->allNodes[$node->getParent()->getId()];
429
        }
430
431
        return false;
432
    }
433
434
    /**
435
     * @param NodeTranslation $parentNode The parent node
436
     * @param string          $slug       The slug
437
     *
438
     * @return NodeTranslation
439
     */
440
    public function getNodeBySlug(NodeTranslation $parentNode, $slug)
441
    {
442
        return $this->em->getRepository(NodeTranslation::class)
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getNodeTranslationForSlug() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
443
            ->getNodeTranslationForSlug($slug, $parentNode);
444
    }
445
446
    /**
447
     * @param string                                        $internalName   The
448
     *                                                                      internal
449
     *                                                                      name
450
     * @param NodeTranslation|NodeMenuItem|HasNodeInterface $parent         The
0 ignored issues
show
Documentation introduced by
Should the type for parameter $parent not be NodeTranslation|NodeMenuItem|HasNodeInterface|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
451
     *                                                                      parent
452
     * @param bool                                          $includeOffline
0 ignored issues
show
Documentation introduced by
Should the type for parameter $includeOffline not be boolean|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
453
     *
454
     * @return NodeMenuItem|null
455
     */
456
    public function getNodeByInternalName(
457
        $internalName,
458
        $parent = null,
459
        $includeOffline = null
460
    ) {
461
        $this->init();
462
        $resultNode = null;
463
464
        if (\is_null($includeOffline)) {
465
            $includeOffline = $this->includeOffline;
466
        }
467
468
        if (\array_key_exists($internalName, $this->nodesByInternalName)) {
469
            $nodes = $this->nodesByInternalName[$internalName];
470
            $nodes = array_filter(
471
                $nodes,
472
                function (Node $entry) use ($includeOffline) {
473
                    if ($entry->isDeleted() && !$includeOffline) {
474
                        return false;
475
                    }
476
477
                    return true;
478
                }
479
            );
480
481
            if (!\is_null($parent)) {
482
                $parentNode = null;
483
                /** @var Node $parentNode */
484
                if ($parent instanceof NodeTranslation) {
485
                    $parentNode = $parent->getNode();
486
                } elseif ($parent instanceof NodeMenuItem) {
487
                    $parentNode = $parent->getNode();
488
                } elseif ($parent instanceof HasNodeInterface) {
489
                    $repo = $this->em->getRepository(
490
                        Node::class
491
                    );
492
                    $parentNode = $repo->getNodeFor($parent);
493
                }
494
495
                // Look for a node with the same parent id
496
                /** @var Node $node */
497
                foreach ($nodes as $node) {
498
                    if ($parentNode && $node->getParent()->getId() == $parentNode->getId()) {
499
                        $resultNode = $node;
500
501
                        break;
502
                    }
503
                }
504
505
                // Look for a node that has an ancestor with the same parent id
506
                if (\is_null($resultNode)) {
507
                    /* @var Node $n */
508
                    foreach ($nodes as $node) {
509
                        $tempNode = $node;
510
                        while (\is_null($resultNode) && !\is_null(
511
                                $tempNode->getParent()
512
                            )) {
513
                            $tempParent = $tempNode->getParent();
514
                            if ($parentNode && $tempParent->getId() == $parentNode->getId()) {
515
                                $resultNode = $node;
516
517
                                break;
518
                            }
519
                            $tempNode = $tempParent;
520
                        }
521
                    }
522
                }
523
            } elseif (\count($nodes) > 0) {
524
                $resultNode = $nodes[0];
525
            }
526
        }
527
528
        if ($resultNode) {
529
            $nodeTranslation = $resultNode->getNodeTranslation(
530
                $this->locale,
531
                $includeOffline
532
            );
533
            if (!\is_null($nodeTranslation)) {
534
                return new NodeMenuItem(
535
                    $resultNode,
536
                    $nodeTranslation,
537
                    false,
538
                    $this
539
                );
540
            }
541
        }
542
543
        return null;
544
    }
545
546
    /**
547
     * Returns the current root node menu item
548
     */
549
    public function getRootNodeMenuItem()
550
    {
551
        if (\is_null($this->rootNodeMenuItem)) {
552
            $rootNode = $this->domainConfiguration->getRootNode();
553
            if (!\is_null($rootNode)) {
554
                $nodeTranslation = $rootNode->getNodeTranslation(
555
                    $this->locale,
556
                    $this->includeOffline
557
                );
558
                $this->rootNodeMenuItem = new NodeMenuItem(
559
                    $rootNode,
560
                    $nodeTranslation,
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $rootNode->getNodeTransl... $this->includeOffline) on line 554 can be null; however, Kunstmaan\NodeBundle\Hel...MenuItem::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
561
                    false,
562
                    $this
563
                );
564
            } else {
565
                $this->rootNodeMenuItem = $this->breadCrumb[0];
566
            }
567
        }
568
569
        return $this->rootNodeMenuItem;
570
    }
571
572
    /**
573
     * @return bool
574
     */
575
    public function isIncludeOffline()
576
    {
577
        return $this->includeOffline;
578
    }
579
580
    /**
581
     * @return string
582
     */
583
    public function getPermission()
584
    {
585
        return $this->permission;
586
    }
587
588
    /**
589
     * @return BaseUser
590
     */
591
    public function getUser()
592
    {
593
        return $this->tokenStorage->getToken()->getUser();
594
    }
595
596
    /**
597
     * @return EntityManagerInterface
598
     */
599
    public function getEntityManager()
600
    {
601
        return $this->em;
602
    }
603
604
    /**
605
     * @return TokenStorageInterface
606
     */
607
    public function getTokenStorage()
608
    {
609
        return $this->tokenStorage;
610
    }
611
612
    /**
613
     * @return AclHelper
614
     */
615
    public function getAclHelper()
616
    {
617
        return $this->aclHelper;
618
    }
619
620
    /**
621
     * @return string
622
     */
623
    public function getLocale()
624
    {
625
        return $this->locale;
626
    }
627
628
    /**
629
     * @return bool
630
     */
631
    public function isIncludeHiddenFromNav()
632
    {
633
        return $this->includeHiddenFromNav;
634
    }
635
636
    /**
637
     * Check if provided slug is in active path
638
     *
639
     * @param string $slug
640
     *
641
     * @return bool
642
     */
643
    public function getActive($slug)
644
    {
645
        $bc = $this->getBreadCrumb();
646
        foreach ($bc as $bcItem) {
647
            if ($bcItem->getSlug() == $slug) {
648
                return true;
649
            }
650
        }
651
652
        return false;
653
    }
654
655
    /**
656
     * @return bool
657
     */
658
    public function isInitialized()
659
    {
660
        return $this->initialized;
661
    }
662
663
    /**
664
     * @return array
665
     */
666
    private function getTopNodeMenuItems()
667
    {
668
        $topNodeMenuItems = [];
669
        $topNodes = $this->childNodes[0];
670
        /* @var Node $topNode */
671 View Code Duplication
        foreach ($topNodes as $topNode) {
0 ignored issues
show
Bug introduced by
The expression $topNodes of type object<Kunstmaan\NodeBundle\Entity\Node> is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
672
            $nodeTranslation = $topNode->getNodeTranslation(
673
                $this->locale,
674
                $this->includeOffline
675
            );
676
            if (!\is_null($nodeTranslation)) {
677
                $topNodeMenuItems[] = new NodeMenuItem(
678
                    $topNode,
679
                    $nodeTranslation,
680
                    null,
681
                    $this
682
                );
683
            }
684
        }
685
686
        return $topNodeMenuItems;
687
    }
688
}
689