Completed
Push — master ( 91fdab...75a7b9 )
by
unknown
13:37
created

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

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 = null;
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 = null;
61
62
    /**
63
     * @var NodeMenuItem[]
64
     */
65
    private $breadCrumb = null;
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 = null;
91
92
    /**
93
     * @var DomainConfigurationInterface
94
     */
95
    private $domainConfiguration = null;
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(
226
                            ) && !$includeHiddenFromNav
227
                        ) {
228
                            return false;
229
                        }
230
231
                        return true;
232
                    }
233
                );
234
            }
235
        }
236
237
        return $this->topNodeMenuItems;
238
    }
239
240
    /**
241
     * @return NodeMenuItem[]
242
     */
243
    public function getBreadCrumb()
244
    {
245
        $this->init();
246
        if (!is_array($this->breadCrumb)) {
247
            $this->breadCrumb = array();
248
249
            /* @var NodeRepository $repo */
250
            $repo = $this->em->getRepository('KunstmaanNodeBundle:Node');
251
252
            // Generate breadcrumb MenuItems - fetch *all* languages so you can link translations if needed
253
            $parentNodes        = $repo->getAllParents($this->currentNode);
254
            $parentNodeMenuItem = null;
255
            /* @var Node $parentNode */
256
            foreach ($parentNodes as $parentNode) {
257
                $nodeTranslation = $parentNode->getNodeTranslation(
258
                    $this->locale,
259
                    $this->includeOffline
260
                );
261
                if (!is_null($nodeTranslation)) {
262
                    $nodeMenuItem       = new NodeMenuItem(
263
                        $parentNode,
264
                        $nodeTranslation,
265
                        $parentNodeMenuItem,
266
                        $this
267
                    );
268
                    $this->breadCrumb[] = $nodeMenuItem;
269
                    $parentNodeMenuItem = $nodeMenuItem;
270
                }
271
            }
272
        }
273
274
        return $this->breadCrumb;
275
    }
276
277
    /**
278
     * @return NodeMenuItem|null
279
     */
280 View Code Duplication
    public function getCurrent()
281
    {
282
        $this->init();
283
        $breadCrumb = $this->getBreadCrumb();
284
        if (count($breadCrumb) > 0) {
285
            return $breadCrumb[count($breadCrumb) - 1];
286
        }
287
288
        return null;
289
    }
290
291
    /**
292
     * @param int $depth
293
     *
294
     * @return NodeMenuItem|null
295
     */
296 View Code Duplication
    public function getActiveForDepth($depth)
297
    {
298
        $breadCrumb = $this->getBreadCrumb();
299
        if (count($breadCrumb) >= $depth) {
300
            return $breadCrumb[$depth - 1];
301
        }
302
303
        return null;
304
    }
305
306
    /**
307
     * @param Node $node
308
     * @param bool $includeHiddenFromNav
309
     *
310
     * @return NodeMenuItem[]
311
     */
312
    public function getChildren(Node $node, $includeHiddenFromNav = true)
313
    {
314
        $this->init();
315
        $children = array();
316
317
        if (array_key_exists($node->getId(), $this->childNodes)) {
318
            $nodes = $this->childNodes[$node->getId()];
319
            /* @var Node $childNode */
320 View Code Duplication
            foreach ($nodes as $childNode) {
321
                $nodeTranslation = $childNode->getNodeTranslation(
322
                    $this->locale,
323
                    $this->includeOffline
324
                );
325
                if (!is_null($nodeTranslation)) {
326
                    $children[] = new NodeMenuItem(
327
                        $childNode,
328
                        $nodeTranslation,
329
                        false,
330
                        $this
331
                    );
332
                }
333
            }
334
335
            $children = array_filter(
336
                $children,
337 View Code Duplication
                function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
338
                    if ($entry->getNode()->isHiddenFromNav(
339
                        ) && !$includeHiddenFromNav
340
                    ) {
341
                        return false;
342
                    }
343
344
                    return true;
345
                }
346
            );
347
        }
348
349
        return $children;
350
    }
351
352
    /**
353
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
354
     * @param bool                              $includeHiddenFromNav
355
     *
356
     * @return array|\Kunstmaan\NodeBundle\Helper\NodeMenuItem[]
357
     */
358
    public function getSiblings(Node $node, $includeHiddenFromNav = true)
359
    {
360
        $this->init();
361
        $siblings = array();
362
363
        if (false !== $parent = $this->getParent($node)) {
364
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
365
366
            foreach ($siblings as $index => $child) {
367
                if ($child === $node) {
368
                    unset($siblings[$index]);
369
                }
370
            }
371
        }
372
373
        return $siblings;
374
    }
375
376
    /**
377
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
378
     * @param bool                              $includeHiddenFromNav
379
     *
380
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
381
     */
382
    public function getPreviousSibling(Node $node, $includeHiddenFromNav = true)
383
    {
384
        $this->init();
385
386
        if (false !== $parent = $this->getParent($node)) {
387
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
388
389
            foreach ($siblings as $index => $child) {
390
                if ($child->getNode() === $node && ($index - 1 >= 0)) {
391
                    return $siblings[$index - 1];
392
                }
393
            }
394
        }
395
396
        return false;
397
    }
398
399
    /**
400
     * @param \Kunstmaan\NodeBundle\Entity\Node $node
401
     * @param bool                              $includeHiddenFromNav
402
     *
403
     * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
404
     */
405
    public function getNextSibling(Node $node, $includeHiddenFromNav = true)
406
    {
407
        $this->init();
408
409
        if (false !== $parent = $this->getParent($node)) {
410
            $siblings = $this->getChildren($parent, $includeHiddenFromNav);
411
412
            foreach ($siblings as $index => $child) {
413
                if ($child->getNode() === $node && (($index + 1) < count(
414
                            $siblings
415
                        ))
416
                ) {
417
                    return $siblings[$index + 1];
418
                }
419
            }
420
        }
421
422
        return false;
423
    }
424
425
    /**
426
     * @param Node $node
427
     *
428
     * @return NodeMenuItem
429
     */
430
    public function getParent(Node $node)
431
    {
432
        $this->init();
433
        if ($node->getParent() && array_key_exists(
434
                $node->getParent()->getId(),
435
                $this->allNodes
436
            )
437
        ) {
438
            return $this->allNodes[$node->getParent()->getId()];
439
        }
440
441
        return false;
442
    }
443
444
    /**
445
     * @param NodeTranslation $parentNode The parent node
446
     * @param string          $slug       The slug
447
     *
448
     * @return NodeTranslation
449
     */
450
    public function getNodeBySlug(NodeTranslation $parentNode, $slug)
451
    {
452
        return $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
453
            ->getNodeTranslationForSlug($slug, $parentNode);
454
    }
455
456
    /**
457
     * @param string                                        $internalName The
458
     *                                                                    internal
459
     *                                                                    name
460
     * @param NodeTranslation|NodeMenuItem|HasNodeInterface $parent       The
461
     *                                                                    parent
462
     * @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...
463
     *
464
     * @return NodeMenuItem|null
465
     */
466
    public function getNodeByInternalName(
467
        $internalName,
468
        $parent = null,
469
        $includeOffline = null
470
    ) {
471
        $this->init();
472
        $resultNode = null;
473
474
        if (is_null($includeOffline)) {
475
            $includeOffline = $this->includeOffline;
476
        }
477
478
        if (array_key_exists($internalName, $this->nodesByInternalName)) {
479
            $nodes = $this->nodesByInternalName[$internalName];
480
            $nodes = array_filter(
481
                $nodes,
482
                function (Node $entry) use ($includeOffline) {
483
                    if ($entry->isDeleted() && !$includeOffline) {
484
                        return false;
485
                    }
486
487
                    return true;
488
                }
489
            );
490
491
            if (!is_null($parent)) {
492
                /** @var Node $parentNode */
493
                if ($parent instanceof NodeTranslation) {
494
                    $parentNode = $parent->getNode();
495
                } elseif ($parent instanceof NodeMenuItem) {
496
                    $parentNode = $parent->getNode();
497
                } elseif ($parent instanceof HasNodeInterface) {
498
                    $repo       = $this->em->getRepository(
499
                        'KunstmaanNodeBundle:Node'
500
                    );
501
                    $parentNode = $repo->getNodeFor($parent);
502
                }
503
504
                // Look for a node with the same parent id
505
                /** @var Node $node */
506
                foreach ($nodes as $node) {
507
                    if ($node->getParent()->getId() == $parentNode->getId()) {
508
                        $resultNode = $node;
509
                        break;
510
                    }
511
                }
512
513
                // Look for a node that has an ancestor with the same parent id
514
                if (is_null($resultNode)) {
515
                    /* @var Node $n */
516
                    foreach ($nodes as $node) {
517
                        $tempNode = $node;
518
                        while (is_null($resultNode) && !is_null(
519
                                $tempNode->getParent()
520
                            )) {
521
                            $tempParent = $tempNode->getParent();
522
                            if ($tempParent->getId() == $parentNode->getId()) {
523
                                $resultNode = $node;
524
                                break;
525
                            }
526
                            $tempNode = $tempParent;
527
                        }
528
                    }
529
                }
530
            } else {
531
                if (count($nodes) > 0) {
532
                    $resultNode = $nodes[0];
533
                }
534
            }
535
        }
536
537
        if ($resultNode) {
538
            $nodeTranslation = $resultNode->getNodeTranslation(
539
                $this->locale,
540
                $includeOffline
541
            );
542
            if (!is_null($nodeTranslation)) {
543
                return new NodeMenuItem(
544
                    $resultNode,
545
                    $nodeTranslation,
546
                    false,
547
                    $this
548
                );
549
            }
550
        }
551
552
        return null;
553
    }
554
555
    /**
556
     * Returns the current root node menu item
557
     */
558
    public function getRootNodeMenuItem()
559
    {
560
        if (is_null($this->rootNodeMenuItem)) {
561
            $rootNode               = $this->domainConfiguration->getRootNode();
562
            if (!is_null($rootNode)) {
563
                $nodeTranslation        = $rootNode->getNodeTranslation(
564
                    $this->locale,
565
                    $this->includeOffline
566
                );
567
                $this->rootNodeMenuItem = new NodeMenuItem(
568
                    $rootNode,
569
                    $nodeTranslation,
570
                    false,
571
                    $this
572
                );
573
            } else {
574
                $this->rootNodeMenuItem = $this->breadCrumb[0];
575
            }
576
        }
577
578
        return $this->rootNodeMenuItem;
579
    }
580
581
    /**
582
     * @return bool
583
     */
584
    public function isIncludeOffline()
585
    {
586
        return $this->includeOffline;
587
    }
588
589
    /**
590
     * @return string
591
     */
592
    public function getPermission()
593
    {
594
        return $this->permission;
595
    }
596
597
    /**
598
     * @return BaseUser
599
     */
600
    public function getUser()
601
    {
602
        return $this->tokenStorage->getToken()->getUser();
603
    }
604
605
    /**
606
     * @return EntityManagerInterface
607
     */
608
    public function getEntityManager()
609
    {
610
        return $this->em;
611
    }
612
613
    /**
614
     * @return TokenStorageInterface
615
     */
616
    public function getTokenStorage()
617
    {
618
        return $this->tokenStorage;
619
    }
620
621
    /**
622
     * @return AclHelper
623
     */
624
    public function getAclHelper()
625
    {
626
        return $this->aclHelper;
627
    }
628
629
    /**
630
     * @return string
631
     */
632
    public function getLocale()
633
    {
634
        return $this->locale;
635
    }
636
637
    /**
638
     * @return bool
639
     */
640
    public function isIncludeHiddenFromNav()
641
    {
642
        return $this->includeHiddenFromNav;
643
    }
644
645
    /**
646
     * Check if provided slug is in active path
647
     *
648
     * @param string $slug
649
     *
650
     * @return bool
651
     */
652
    public function getActive($slug)
653
    {
654
        $bc = $this->getBreadCrumb();
655
        foreach ($bc as $bcItem) {
656
            if ($bcItem->getSlug() == $slug) {
657
                return true;
658
            }
659
        }
660
661
        return false;
662
    }
663
664
    /**
665
     * @return bool
666
     */
667
    public function isInitialized()
668
    {
669
        return $this->initialized;
670
    }
671
672
    /**
673
     * @return array
674
     */
675
    private function getTopNodeMenuItems()
676
    {
677
        $topNodeMenuItems = array();
678
        $topNodes         = $this->childNodes[0];
679
        /* @var Node $topNode */
680 View Code Duplication
        foreach ($topNodes as $topNode) {
681
            $nodeTranslation = $topNode->getNodeTranslation(
682
                $this->locale,
683
                $this->includeOffline
684
            );
685
            if (!is_null($nodeTranslation)) {
686
                $topNodeMenuItems[] = new NodeMenuItem(
687
                    $topNode,
688
                    $nodeTranslation,
689
                    null,
690
                    $this
691
                );
692
            }
693
        }
694
695
        return $topNodeMenuItems;
696
    }
697
}
698