Completed
Push — 5.6 ( 2839de...185d99 )
by Jeroen
11:17
created

Kunstmaan/NodeBundle/Repository/NodeRepository.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\Repository;
4
5
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
6
use Kunstmaan\AdminBundle\Entity\BaseUser;
7
use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper;
8
use Kunstmaan\AdminBundle\Helper\Security\Acl\AclNativeHelper;
9
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionDefinition;
10
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
11
use Kunstmaan\NodeBundle\Entity\Node;
12
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
13
use Kunstmaan\NodeBundle\Entity\NodeVersion;
14
use Kunstmaan\NodeBundle\Helper\HiddenFromNavInterface;
15
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
16
17
/**
18
 * NodeRepository
19
 */
20
class NodeRepository extends NestedTreeRepository
21
{
22
    /**
23
     * @param string    $lang                 The locale
24
     * @param string    $permission           The permission (read, write, ...)
25
     * @param AclHelper $aclHelper            The acl helper
26
     * @param bool      $includeHiddenFromNav include the hiddenfromnav nodes
27
     *                                        or not
28
     *
29
     * @return Node[]
30
     */
31
    public function getTopNodes(
32
        $lang,
33
        $permission,
34
        AclHelper $aclHelper,
35
        $includeHiddenFromNav = false
36
    ) {
37
        $result = $this->getChildNodes(
38
            null,
39
            $lang,
40
            $permission,
41
            $aclHelper,
42
            $includeHiddenFromNav
43
        );
44
45
        return $result;
46
    }
47
48
    /**
49
     * @param int|null  $parentId             The parent node id
50
     * @param string    $lang                 The locale
51
     * @param string    $permission           The permission (read, write, ...)
52
     * @param AclHelper $aclHelper            The acl helper
53
     * @param bool      $includeHiddenFromNav Include nodes hidden from
54
     *                                        navigation or not
55
     * @param Node      $rootNode             Root node of the current tree
56
     *
57
     * @return Node[]
58
     */
59
    public function getChildNodes(
60
        $parentId,
61
        $lang,
62
        $permission,
63
        AclHelper $aclHelper,
64
        $includeHiddenFromNav = false,
65
        $includeHiddenWithInternalName = false,
66
        $rootNode = null
67
    ) {
68
        $qb = $this->createQueryBuilder('b')
69
            ->select('b', 't', 'v')
70
            ->leftJoin('b.nodeTranslations', 't', 'WITH', 't.lang = :lang')
71
            ->leftJoin(
72
                't.publicNodeVersion',
73
                'v',
74
                'WITH',
75
                't.publicNodeVersion = v.id'
76
            )
77
            ->where('b.deleted = 0')
78
            ->setParameter('lang', $lang)
79
            ->addOrderBy('t.weight', 'ASC')
80
            ->addOrderBy('t.title', 'ASC');
81
82
        if (!$includeHiddenFromNav) {
83
            if ($includeHiddenWithInternalName) {
84
                $qb->andWhere(
85
                    '(b.hiddenFromNav != true OR b.internalName IS NOT NULL)'
86
                );
87
            } else {
88
                $qb->andWhere('b.hiddenFromNav != true');
89
            }
90
        }
91
92 View Code Duplication
        if (\is_null($parentId)) {
93
            $qb->andWhere('b.parent is NULL');
94
        } elseif ($parentId !== false) {
95
            $qb->andWhere('b.parent = :parent')
96
                ->setParameter('parent', $parentId);
97
        }
98
        if ($rootNode) {
99
            $qb->andWhere('b.lft >= :left')
100
                ->andWhere('b.rgt <= :right')
101
                ->setParameter('left', $rootNode->getLeft())
102
                ->setParameter('right', $rootNode->getRight());
103
        }
104
105
        $query = $aclHelper->apply(
106
            $qb,
107
            new PermissionDefinition([$permission])
108
        );
109
110
        return $query->getResult();
111
    }
112
113
    /**
114
     * @return Node|null
115
     */
116
    public function getNodeFor(HasNodeInterface $hasNode)
117
    {
118
        /* @var NodeVersion $nodeVersion */
119
        $nodeVersion = $this->getEntityManager()->getRepository(
120
            NodeVersion::class
121
        )->getNodeVersionFor(
122
            $hasNode
123
        );
124
        if (!\is_null($nodeVersion)) {
125
            /* @var NodeTranslation $nodeTranslation */
126
            $nodeTranslation = $nodeVersion->getNodeTranslation();
127
            if (!\is_null($nodeTranslation)) {
128
                return $nodeTranslation->getNode();
129
            }
130
        }
131
132
        return null;
133
    }
134
135
    /**
136
     * @param int    $id         The id
137
     * @param string $entityName The class name
138
     *
139
     * @return Node|null
140
     */
141
    public function getNodeForIdAndEntityname($id, $entityName)
142
    {
143
        /* @var NodeVersion $nodeVersion */
144
        $nodeVersion = $this->getEntityManager()->getRepository(
145
            NodeVersion::class
146
        )->findOneBy(
147
            ['refId' => $id, 'refEntityName' => $entityName]
148
        );
149
        if ($nodeVersion) {
150
            return $nodeVersion->getNodeTranslation()->getNode();
151
        }
152
153
        return null;
154
    }
155
156
    /**
157
     * @param Node   $parentNode The parent node (may be null)
158
     * @param string $slug       The slug
159
     *
160
     * @return Node|null
161
     */
162
    public function getNodeForSlug(Node $parentNode, $slug)
163
    {
164
        $slugParts = explode('/', $slug);
165
        $result = null;
166
        foreach ($slugParts as $slugPart) {
167
            if ($parentNode) {
168
                if ($r = $this->findOneBy(
169
                    [
170
                        'slug' => $slugPart,
171
                        'parent.parent' => $parentNode->getId(),
172
                    ]
173
                )
174
                ) {
175
                    $result = $r;
176
                }
177
            } elseif ($r = $this->findOneBy(['slug' => $slugPart])) {
178
                $result = $r;
179
            }
180
        }
181
182
        return $result;
183
    }
184
185
    /**
186
     * @param HasNodeInterface $hasNode      The object to link to
187
     * @param string           $lang         The locale
188
     * @param BaseUser         $owner        The user
189
     * @param string           $internalName The internal name (may be null)
190
     *
191
     * @throws \InvalidArgumentException
192
     *
193
     * @return Node
194
     */
195
    public function createNodeFor(
196
        HasNodeInterface $hasNode,
197
        $lang,
198
        BaseUser $owner,
199
        $internalName = null
200
    ) {
201
        $em = $this->getEntityManager();
202
        $node = new Node();
203
        $node->setRef($hasNode);
204
        if (!$hasNode->getId() > 0) {
205
            throw new \InvalidArgumentException('the entity of class ' . $node->getRefEntityName() . ' has no id, maybe you forgot to flush first');
206
        }
207
        $node->setDeleted(false);
208
        $node->setInternalName($internalName);
209
        $parent = $hasNode->getParent();
210
        if ($parent) {
211
            /* @var NodeVersion $parentNodeVersion */
212
            $parentNodeVersion = $em->getRepository(
213
                NodeVersion::class
214
            )->findOneBy(
215
                [
216
                    'refId' => $parent->getId(),
217
                    'refEntityName' => ClassLookup::getClass($parent),
218
                ]
219
            );
220
            if ($parentNodeVersion) {
221
                $node->setParent(
222
                    $parentNodeVersion->getNodeTranslation()->getNode()
223
                );
224
            }
225
        }
226
        if ($hasNode instanceof HiddenFromNavInterface) {
227
            $node->setHiddenFromNav($hasNode->isHiddenFromNav());
228
        }
229
        $em->persist($node);
230
        $em->flush();
231
        $em->refresh($node);
232
        $em->getRepository(NodeTranslation::class)
233
            ->createNodeTranslationFor(
234
                $hasNode,
235
                $lang,
236
                $node,
237
                $owner
238
            );
239
240
        return $node;
241
    }
242
243
    /**
244
     * Get all the information needed to build a menu tree with one query.
245
     * We only fetch the fields we need, instead of fetching full objects to
246
     * limit the memory usage.
247
     *
248
     * @param string          $lang                 The locale
249
     * @param string          $permission           The permission (read,
250
     *                                              write, ...)
251
     * @param AclNativeHelper $aclNativeHelper      The acl helper
252
     * @param bool            $includeHiddenFromNav Include nodes hidden from
253
     *                                              navigation or not
254
     * @param Node            $rootNode             The root node of the
255
     *                                              current site
256
     *
257
     * @return array
258
     */
259
    public function getAllMenuNodes(
260
        $lang,
261
        $permission,
262
        AclNativeHelper $aclNativeHelper,
263
        $includeHiddenFromNav = false,
264
        Node $rootNode = null
265
    ) {
266
        $connection = $this->_em->getConnection();
267
        $qb = $connection->createQueryBuilder();
268
        $databasePlatformName = $connection->getDatabasePlatform()->getName();
269
        $createIfStatement = function (
270
            $expression,
271
            $trueValue,
272
            $falseValue
273
        ) use ($databasePlatformName) {
274
            switch ($databasePlatformName) {
275
                case 'sqlite':
276
                    $statement = 'CASE WHEN %s THEN %s ELSE %s END';
277
278
                    break;
279
280
                default:
281
                    $statement = 'IF(%s, %s, %s)';
282
            }
283
284
            return sprintf($statement, $expression, $trueValue, $falseValue);
285
        };
286
287
        $sql = <<<SQL
288
n.id, n.parent_id AS parent, t.url, t.id AS nt_id,
289
{$createIfStatement('t.weight IS NULL', 'v.weight', 't.weight')} AS weight,
290
{$createIfStatement('t.title IS NULL', 'v.title', 't.title')} AS title,
291
{$createIfStatement('t.online IS NULL', '0', 't.online')} AS online,
292
n.hidden_from_nav AS hidden,
293
n.ref_entity_name AS ref_entity_name
294
SQL;
295
296
        $qb->select($sql)
297
            ->from('kuma_nodes', 'n')
298
            ->leftJoin(
299
                'n',
300
                'kuma_node_translations',
301
                't',
302
                '(t.node_id = n.id AND t.lang = :lang)'
303
            )
304
            ->leftJoin(
305
                'n',
306
                'kuma_node_translations',
307
                'v',
308
                '(v.node_id = n.id AND v.lang <> :lang)'
309
            )
310
            ->where('n.deleted = 0')
311
            ->addGroupBy('n.id')
312
            ->addOrderBy('t.weight', 'ASC')
313
            ->addOrderBy('t.title', 'ASC');
314
315
        if (!$includeHiddenFromNav) {
316
            $qb->andWhere('n.hidden_from_nav <> 0');
317
        }
318
319
        if (!\is_null($rootNode)) {
320
            $qb->andWhere('n.lft >= :left')
321
                ->andWhere('n.rgt <= :right');
322
        }
323
324
        $permissionDef = new PermissionDefinition([$permission]);
325
        $permissionDef->setEntity('Kunstmaan\NodeBundle\Entity\Node');
326
        $permissionDef->setAlias('n');
327
        $qb = $aclNativeHelper->apply($qb, $permissionDef);
328
329
        $stmt = $this->_em->getConnection()->prepare($qb->getSQL());
330
        $stmt->bindValue(':lang', $lang);
331
        if (!\is_null($rootNode)) {
332
            $stmt->bindValue(':left', $rootNode->getLeft());
333
            $stmt->bindValue(':right', $rootNode->getRight());
334
        }
335
        $stmt->execute();
336
337
        return $stmt->fetchAll();
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\DBAL\Statement::fetchAll() has been deprecated with message: Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
338
    }
339
340
    /**
341
     * Get all parents of a given node. We can go multiple levels up.
342
     *
343
     * @param Node   $node
344
     * @param string $lang
345
     *
346
     * @return Node[]
347
     */
348 View Code Duplication
    public function getAllParents(Node $node = null, $lang = null)
349
    {
350
        if (\is_null($node)) {
351
            return [];
352
        }
353
354
        $qb = $this->createQueryBuilder('node');
355
356
        // Directly hydrate the nodeTranslation and nodeVersion
357
        $qb->select('node', 't', 'v')
358
            ->innerJoin('node.nodeTranslations', 't')
359
            ->leftJoin(
360
                't.publicNodeVersion',
361
                'v',
362
                'WITH',
363
                't.publicNodeVersion = v.id'
364
            )
365
            ->where('node.deleted = 0');
366
367
        if ($lang) {
368
            $qb->andWhere('t.lang = :lang')
369
                ->setParameter('lang', $lang);
370
        }
371
372
        $qb->andWhere(
373
            $qb->expr()->andX(
374
                $qb->expr()->lte('node.lft', $node->getLeft()),
375
                $qb->expr()->gte('node.rgt', $node->getRight())
376
            )
377
        );
378
379
        $qb->addOrderBy('node.lft', 'ASC');
380
381
        return $qb->getQuery()->getResult();
382
    }
383
384
    /**
385
     * Get the root node of a given node.
386
     *
387
     * @param Node   $node
388
     * @param string $lang
389
     *
390
     * @return Node
391
     */
392 View Code Duplication
    public function getRootNodeFor(Node $node = null, $lang = null)
393
    {
394
        if (\is_null($node)) {
395
            return null;
396
        }
397
398
        $qb = $this->createQueryBuilder('node');
399
400
        // Directly hydrate the nodeTranslation and nodeVersion
401
        $qb->select('node', 't', 'v')
402
            ->innerJoin('node.nodeTranslations', 't')
403
            ->leftJoin(
404
                't.publicNodeVersion',
405
                'v',
406
                'WITH',
407
                't.publicNodeVersion = v.id'
408
            )
409
            ->where('node.deleted = 0')
410
            ->andWhere('node.parent IS NULL');
411
412
        if ($lang) {
413
            $qb->andWhere('t.lang = :lang')
414
                ->setParameter('lang', $lang);
415
        }
416
417
        $qb->andWhere(
418
            $qb->expr()->andX(
419
                $qb->expr()->lte('node.lft', $node->getLeft()),
420
                $qb->expr()->gte('node.rgt', $node->getRight())
421
            )
422
        );
423
424
        return $qb->getQuery()->getOneOrNullResult();
425
    }
426
427
    /**
428
     * @return Node[]
429
     */
430
    public function getAllTopNodes()
431
    {
432
        $qb = $this->createQueryBuilder('b')
433
            ->select('b', 't', 'v')
434
            ->leftJoin('b.nodeTranslations', 't')
435
            ->leftJoin(
436
                't.publicNodeVersion',
437
                'v',
438
                'WITH',
439
                't.publicNodeVersion = v.id'
440
            )
441
            ->where('b.deleted = 0')
442
            ->andWhere('b.parent IS NULL');
443
444
        return $qb->getQuery()->getResult();
445
    }
446
447
    /**
448
     * Get an array of Nodes based on the internal name.
449
     *
450
     * @param string        $internalName   The internal name of the node
451
     * @param string        $lang           The locale
452
     * @param int|bool|null $parentId       The parent id
453
     * @param bool          $includeOffline Include offline nodes
454
     *
455
     * @return Node[]
456
     */
457
    public function getNodesByInternalName(
458
        $internalName,
459
        $lang,
460
        $parentId = false,
461
        $includeOffline = false
462
    ) {
463
        $qb = $this->createQueryBuilder('n')
464
            ->select('n', 't', 'v')
465
            ->innerJoin('n.nodeTranslations', 't')
466
            ->leftJoin(
467
                't.publicNodeVersion',
468
                'v',
469
                'WITH',
470
                't.publicNodeVersion = v.id'
471
            )
472
            ->where('n.deleted = 0')
473
            ->andWhere('n.internalName = :internalName')
474
            ->setParameter('internalName', $internalName)
475
            ->andWhere('t.lang = :lang')
476
            ->setParameter('lang', $lang)
477
            ->addOrderBy('t.weight', 'ASC')
478
            ->addOrderBy('t.title', 'ASC');
479
480
        if (!$includeOffline) {
481
            $qb->andWhere('t.online = true');
482
        }
483
484 View Code Duplication
        if (\is_null($parentId)) {
485
            $qb->andWhere('n.parent is NULL');
486
        } elseif ($parentId === false) {
487
            // Do nothing
488
        } else {
489
            $qb->andWhere('n.parent = :parent')
490
                ->setParameter('parent', $parentId);
491
        }
492
493
        $query = $qb->getQuery();
494
495
        return $query->getResult();
496
    }
497
498
    /**
499
     * Get a single node by internal name.
500
     *
501
     * @param string $internalName The internal name of the node
502
     *
503
     * @return Node|null
504
     */
505
    public function getNodeByInternalName($internalName)
506
    {
507
        $qb = $this->createQueryBuilder('n')
508
            ->select('n')
509
            ->where('n.deleted = 0')
510
            ->andWhere('n.internalName = :internalName')
511
            ->setParameter('internalName', $internalName);
512
513
        return $qb->getQuery()->getOneOrNullResult();
514
    }
515
516
    /**
517
     * Finds all different page classes currently registered as nodes
518
     *
519
     * @return string[]
520
     */
521
    public function findAllDistinctPageClasses()
522
    {
523
        $qb = $this->createQueryBuilder('n')
524
            ->select('n.refEntityName')
525
            ->where('n.deleted = 0')
526
            ->distinct(true);
527
528
        return $qb->getQuery()->getArrayResult();
529
    }
530
531
    public function getChildCount(Node $node, bool $direct = false, bool $includeDeleted = false): int
532
    {
533
        $qb = $this->getChildrenQueryBuilder($node, $direct);
534
        $qb->resetDQLPart('orderBy');
535
536
        $aliases = $qb->getRootAliases();
537
        $alias = $aliases[0];
538
539
        $qb->select('COUNT(' . $alias . ')');
540
541
        if (false === $includeDeleted) {
542
            $qb->andWhere($alias . '.deleted = 0');
543
        }
544
545
        return (int) $qb->getQuery()->getSingleScalarResult();
546
    }
547
}
548