Completed
Push — master ( aba493...5356ed )
by Ruud
315:38 queued 305:00
created

src/Kunstmaan/NodeBundle/Helper/NodeMenu.php (1 issue)

Check that @param annotations have the correct type.

Documentation Informational

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 = array();
71
72
    /**
73
     * @var Node[]
74
     */
75
    private $childNodes = array();
76
77
    /**
78
     * @var Node[]
79
     */
80
    private $nodesByInternalName = array();
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
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 = array();
174
        $this->breadCrumb = null;
175
        $this->childNodes = array();
176
        $this->topNodeMenuItems = null;
177
        $this->nodesByInternalName = array();
178
179
        /* @var NodeRepository $repo */
180
        $repo = $this->em->getRepository('KunstmaanNodeBundle:Node');
181
182
        // Get all possible menu items in one query (also fetch offline nodes)
183
        $nodes = $repo->getChildNodes(
184
            false,
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 = array();
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) {
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 = array();
246
247
            /* @var NodeRepository $repo */
248
            $repo = $this->em->getRepository('KunstmaanNodeBundle:Node');
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()
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)
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 Node $node
306
     * @param bool $includeHiddenFromNav
307
     *
308
     * @return NodeMenuItem[]
309
     */
310
    public function getChildren(Node $node, $includeHiddenFromNav = true)
311
    {
312
        $this->init();
313
        $children = array();
314
315
        if (\array_key_exists($node->getId(), $this->childNodes)) {
316
            $nodes = $this->childNodes[$node->getId()];
317
            /* @var Node $childNode */
318 View Code Duplication
            foreach ($nodes as $childNode) {
319
                $nodeTranslation = $childNode->getNodeTranslation(
320
                    $this->locale,
321
                    $this->includeOffline
322
                );
323
                if (!\is_null($nodeTranslation)) {
324
                    $children[] = new NodeMenuItem(
325
                        $childNode,
326
                        $nodeTranslation,
327
                        false,
328
                        $this
329
                    );
330
                }
331
            }
332
333
            $children = array_filter(
334
                $children,
335 View Code Duplication
                function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
336
                    if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) {
337
                        return false;
338
                    }
339
340
                    return true;
341
                }
342
            );
343
        }
344
345
        return $children;
346
    }
347
348
    /**
349
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
350
     * @param bool                              $includeHiddenFromNav
351
     *
352
     * @return array|\Kunstmaan\NodeBundle\Helper\NodeMenuItem[]
353
     */
354
    public function getSiblings(Node $node, $includeHiddenFromNav = true)
355
    {
356
        $this->init();
357
        $siblings = array();
358
359
        if (false !== $parent = $this->getParent($node)) {
360
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
361
362
            foreach ($siblings as $index => $child) {
363
                if ($child === $node) {
364
                    unset($siblings[$index]);
365
                }
366
            }
367
        }
368
369
        return $siblings;
370
    }
371
372
    /**
373
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
374
     * @param bool                              $includeHiddenFromNav
375
     *
376
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
377
     */
378
    public function getPreviousSibling(Node $node, $includeHiddenFromNav = true)
379
    {
380
        $this->init();
381
382
        if (false !== $parent = $this->getParent($node)) {
383
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
384
385
            foreach ($siblings as $index => $child) {
386
                if ($child->getNode() === $node && ($index - 1 >= 0)) {
387
                    return $siblings[$index - 1];
388
                }
389
            }
390
        }
391
392
        return false;
393
    }
394
395
    /**
396
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
397
     * @param bool                              $includeHiddenFromNav
398
     *
399
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
400
     */
401
    public function getNextSibling(Node $node, $includeHiddenFromNav = true)
402
    {
403
        $this->init();
404
405
        if (false !== $parent = $this->getParent($node)) {
406
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
407
408
            foreach ($siblings as $index => $child) {
409
                if ($child->getNode() === $node && (($index + 1) < \count(
410
                            $siblings
411
                        ))
412
                ) {
413
                    return $siblings[$index + 1];
414
                }
415
            }
416
        }
417
418
        return false;
419
    }
420
421
    /**
422
     * @param Node $node
423
     *
424
     * @return NodeMenuItem
425
     */
426
    public function getParent(Node $node)
427
    {
428
        $this->init();
429
        if ($node->getParent() && \array_key_exists(
430
                $node->getParent()->getId(),
431
                $this->allNodes
432
            )
433
        ) {
434
            return $this->allNodes[$node->getParent()->getId()];
435
        }
436
437
        return false;
438
    }
439
440
    /**
441
     * @param NodeTranslation $parentNode The parent node
442
     * @param string          $slug       The slug
443
     *
444
     * @return NodeTranslation
445
     */
446
    public function getNodeBySlug(NodeTranslation $parentNode, $slug)
447
    {
448
        return $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
449
            ->getNodeTranslationForSlug($slug, $parentNode);
450
    }
451
452
    /**
453
     * @param string                                        $internalName   The
454
     *                                                                      internal
455
     *                                                                      name
456
     * @param NodeTranslation|NodeMenuItem|HasNodeInterface $parent         The
457
     *                                                                      parent
458
     * @param bool                                          $includeOffline
0 ignored issues
show
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...
459
     *
460
     * @return NodeMenuItem|null
461
     */
462
    public function getNodeByInternalName(
463
        $internalName,
464
        $parent = null,
465
        $includeOffline = null
466
    ) {
467
        $this->init();
468
        $resultNode = null;
469
470
        if (\is_null($includeOffline)) {
471
            $includeOffline = $this->includeOffline;
472
        }
473
474
        if (\array_key_exists($internalName, $this->nodesByInternalName)) {
475
            $nodes = $this->nodesByInternalName[$internalName];
476
            $nodes = array_filter(
477
                $nodes,
478
                function (Node $entry) use ($includeOffline) {
479
                    if ($entry->isDeleted() && !$includeOffline) {
480
                        return false;
481
                    }
482
483
                    return true;
484
                }
485
            );
486
487
            if (!\is_null($parent)) {
488
                /** @var Node $parentNode */
489
                if ($parent instanceof NodeTranslation) {
490
                    $parentNode = $parent->getNode();
491
                } elseif ($parent instanceof NodeMenuItem) {
492
                    $parentNode = $parent->getNode();
493
                } elseif ($parent instanceof HasNodeInterface) {
494
                    $repo = $this->em->getRepository(
495
                        'KunstmaanNodeBundle:Node'
496
                    );
497
                    $parentNode = $repo->getNodeFor($parent);
498
                }
499
500
                // Look for a node with the same parent id
501
                /** @var Node $node */
502
                foreach ($nodes as $node) {
503
                    if ($node->getParent()->getId() == $parentNode->getId()) {
504
                        $resultNode = $node;
505
506
                        break;
507
                    }
508
                }
509
510
                // Look for a node that has an ancestor with the same parent id
511
                if (\is_null($resultNode)) {
512
                    /* @var Node $n */
513
                    foreach ($nodes as $node) {
514
                        $tempNode = $node;
515
                        while (\is_null($resultNode) && !\is_null(
516
                                $tempNode->getParent()
517
                            )) {
518
                            $tempParent = $tempNode->getParent();
519
                            if ($tempParent->getId() == $parentNode->getId()) {
520
                                $resultNode = $node;
521
522
                                break;
523
                            }
524
                            $tempNode = $tempParent;
525
                        }
526
                    }
527
                }
528
            } elseif (\count($nodes) > 0) {
529
                $resultNode = $nodes[0];
530
            }
531
        }
532
533
        if ($resultNode) {
534
            $nodeTranslation = $resultNode->getNodeTranslation(
535
                $this->locale,
536
                $includeOffline
537
            );
538
            if (!\is_null($nodeTranslation)) {
539
                return new NodeMenuItem(
540
                    $resultNode,
541
                    $nodeTranslation,
542
                    false,
543
                    $this
544
                );
545
            }
546
        }
547
548
        return null;
549
    }
550
551
    /**
552
     * Returns the current root node menu item
553
     */
554
    public function getRootNodeMenuItem()
555
    {
556
        if (\is_null($this->rootNodeMenuItem)) {
557
            $rootNode = $this->domainConfiguration->getRootNode();
558
            if (!\is_null($rootNode)) {
559
                $nodeTranslation = $rootNode->getNodeTranslation(
560
                    $this->locale,
561
                    $this->includeOffline
562
                );
563
                $this->rootNodeMenuItem = new NodeMenuItem(
564
                    $rootNode,
565
                    $nodeTranslation,
566
                    false,
567
                    $this
568
                );
569
            } else {
570
                $this->rootNodeMenuItem = $this->breadCrumb[0];
571
            }
572
        }
573
574
        return $this->rootNodeMenuItem;
575
    }
576
577
    /**
578
     * @return bool
579
     */
580
    public function isIncludeOffline()
581
    {
582
        return $this->includeOffline;
583
    }
584
585
    /**
586
     * @return string
587
     */
588
    public function getPermission()
589
    {
590
        return $this->permission;
591
    }
592
593
    /**
594
     * @return BaseUser
595
     */
596
    public function getUser()
597
    {
598
        return $this->tokenStorage->getToken()->getUser();
599
    }
600
601
    /**
602
     * @return EntityManagerInterface
603
     */
604
    public function getEntityManager()
605
    {
606
        return $this->em;
607
    }
608
609
    /**
610
     * @return TokenStorageInterface
611
     */
612
    public function getTokenStorage()
613
    {
614
        return $this->tokenStorage;
615
    }
616
617
    /**
618
     * @return AclHelper
619
     */
620
    public function getAclHelper()
621
    {
622
        return $this->aclHelper;
623
    }
624
625
    /**
626
     * @return string
627
     */
628
    public function getLocale()
629
    {
630
        return $this->locale;
631
    }
632
633
    /**
634
     * @return bool
635
     */
636
    public function isIncludeHiddenFromNav()
637
    {
638
        return $this->includeHiddenFromNav;
639
    }
640
641
    /**
642
     * Check if provided slug is in active path
643
     *
644
     * @param string $slug
645
     *
646
     * @return bool
647
     */
648
    public function getActive($slug)
649
    {
650
        $bc = $this->getBreadCrumb();
651
        foreach ($bc as $bcItem) {
652
            if ($bcItem->getSlug() == $slug) {
653
                return true;
654
            }
655
        }
656
657
        return false;
658
    }
659
660
    /**
661
     * @return bool
662
     */
663
    public function isInitialized()
664
    {
665
        return $this->initialized;
666
    }
667
668
    /**
669
     * @return array
670
     */
671
    private function getTopNodeMenuItems()
672
    {
673
        $topNodeMenuItems = array();
674
        $topNodes = $this->childNodes[0];
675
        /* @var Node $topNode */
676 View Code Duplication
        foreach ($topNodes as $topNode) {
677
            $nodeTranslation = $topNode->getNodeTranslation(
678
                $this->locale,
679
                $this->includeOffline
680
            );
681
            if (!\is_null($nodeTranslation)) {
682
                $topNodeMenuItems[] = new NodeMenuItem(
683
                    $topNode,
684
                    $nodeTranslation,
685
                    null,
686
                    $this
687
                );
688
            }
689
        }
690
691
        return $topNodeMenuItems;
692
    }
693
}
694