Completed
Push — master ( 91fdab...75a7b9 )
by
unknown
13:37
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(array($permission))
108
        );
109
110
        return $query->getResult();
111
    }
112
113
    /**
114
     * @param HasNodeInterface $hasNode
115
     *
116
     * @return Node|null
117
     */
118
    public function getNodeFor(HasNodeInterface $hasNode)
119
    {
120
        /* @var NodeVersion $nodeVersion */
121
        $nodeVersion = $this->getEntityManager()->getRepository(
122
            'KunstmaanNodeBundle:NodeVersion'
123
        )->getNodeVersionFor(
124
            $hasNode
125
        );
126
        if (!is_null($nodeVersion)) {
127
            /* @var NodeTranslation $nodeTranslation */
128
            $nodeTranslation = $nodeVersion->getNodeTranslation();
129
            if (!is_null($nodeTranslation)) {
130
                return $nodeTranslation->getNode();
131
            }
132
        }
133
134
        return null;
135
    }
136
137
    /**
138
     * @param int    $id         The id
139
     * @param string $entityName The class name
140
     *
141
     * @return Node|null
142
     */
143
    public function getNodeForIdAndEntityname($id, $entityName)
144
    {
145
        /* @var NodeVersion $nodeVersion */
146
        $nodeVersion = $this->getEntityManager()->getRepository(
147
            'KunstmaanNodeBundle:NodeVersion'
148
        )->findOneBy(
149
            array('refId' => $id, 'refEntityName' => $entityName)
150
        );
151
        if ($nodeVersion) {
152
            return $nodeVersion->getNodeTranslation()->getNode();
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * @param Node   $parentNode The parent node (may be null)
160
     * @param string $slug       The slug
161
     *
162
     * @return Node|null
163
     */
164
    public function getNodeForSlug(Node $parentNode, $slug)
165
    {
166
        $slugParts = explode("/", $slug);
167
        $result    = null;
168
        foreach ($slugParts as $slugPart) {
169
            if ($parentNode) {
170
                if ($r = $this->findOneBy(
171
                    array(
172
                        'slug'          => $slugPart,
173
                        'parent.parent' => $parentNode->getId()
174
                    )
175
                )
176
                ) {
177
                    $result = $r;
178
                }
179
            } else {
180
                if ($r = $this->findOneBy(array('slug' => $slugPart))) {
181
                    $result = $r;
182
                }
183
            }
184
        }
185
186
        return $result;
187
    }
188
189
    /**
190
     * @param HasNodeInterface $hasNode      The object to link to
191
     * @param string           $lang         The locale
192
     * @param BaseUser         $owner        The user
193
     * @param string           $internalName The internal name (may be null)
194
     *
195
     * @throws \InvalidArgumentException
196
     *
197
     * @return Node
198
     */
199
    public function createNodeFor(
200
        HasNodeInterface $hasNode,
201
        $lang,
202
        BaseUser $owner,
203
        $internalName = null
204
    ) {
205
        $em   = $this->getEntityManager();
206
        $node = new Node();
207
        $node->setRef($hasNode);
208
        if (!$hasNode->getId() > 0) {
209
            throw new \InvalidArgumentException(
210
                "the entity of class ".
211
                $node->getRefEntityName(
212
                )." has no id, maybe you forgot to flush first"
213
            );
214
        }
215
        $node->setDeleted(false);
216
        $node->setInternalName($internalName);
217
        $parent = $hasNode->getParent();
218
        if ($parent) {
219
            /* @var NodeVersion $parentNodeVersion */
220
            $parentNodeVersion = $em->getRepository(
221
                'KunstmaanNodeBundle:NodeVersion'
222
            )->findOneBy(
223
                array(
224
                    'refId'         => $parent->getId(),
225
                    'refEntityName' => ClassLookup::getClass($parent)
226
                )
227
            );
228
            if ($parentNodeVersion) {
229
                $node->setParent(
230
                    $parentNodeVersion->getNodeTranslation()->getNode()
231
                );
232
            }
233
        }
234
        if ($hasNode instanceof HiddenFromNavInterface) {
235
            $node->setHiddenFromNav($hasNode->isHiddenFromNav());
236
        }
237
        $em->persist($node);
238
        $em->flush();
239
        $em->refresh($node);
240
        $em->getRepository('KunstmaanNodeBundle:NodeTranslation')
241
            ->createNodeTranslationFor(
242
                $hasNode,
243
                $lang,
244
                $node,
245
                $owner
246
            );
247
248
        return $node;
249
    }
250
251
    /**
252
     * Get all the information needed to build a menu tree with one query.
253
     * We only fetch the fields we need, instead of fetching full objects to
254
     * limit the memory usage.
255
     *
256
     * @param string          $lang                 The locale
257
     * @param string          $permission           The permission (read,
258
     *                                              write, ...)
259
     * @param AclNativeHelper $aclNativeHelper      The acl helper
260
     * @param bool            $includeHiddenFromNav Include nodes hidden from
261
     *                                              navigation or not
262
     * @param Node            $rootNode             The root node of the
263
     *                                              current site
264
     *
265
     * @return array
266
     */
267
    public function getAllMenuNodes(
268
        $lang,
269
        $permission,
270
        AclNativeHelper $aclNativeHelper,
271
        $includeHiddenFromNav = false,
272
        Node $rootNode = null
273
    ) {
274
        $connection           = $this->_em->getConnection();
275
        $qb                   = $connection->createQueryBuilder();
276
        $databasePlatformName = $connection->getDatabasePlatform()->getName();
277
        $createIfStatement    = function (
278
            $expression,
279
            $trueValue,
280
            $falseValue
281
        ) use ($databasePlatformName) {
282
            switch ($databasePlatformName) {
283
                case 'sqlite':
284
                    $statement = 'CASE WHEN %s THEN %s ELSE %s END';
285
                    break;
286
287
                default:
288
                    $statement = 'IF(%s, %s, %s)';
289
            }
290
291
            return sprintf($statement, $expression, $trueValue, $falseValue);
292
        };
293
294
        $sql = <<<SQL
295
n.id, n.parent_id AS parent, t.url, t.id AS nt_id,
296
{$createIfStatement('t.weight IS NULL', 'v.weight', 't.weight')} AS weight,
297
{$createIfStatement('t.title IS NULL', 'v.title', 't.title')} AS title,
298
{$createIfStatement('t.online IS NULL', '0', 't.online')} AS online,
299
n.hidden_from_nav AS hidden,
300
n.ref_entity_name AS ref_entity_name
301
SQL;
302
303
304
        $qb->select($sql)
305
            ->from('kuma_nodes', 'n')
306
            ->leftJoin(
307
                'n',
308
                'kuma_node_translations',
309
                't',
310
                '(t.node_id = n.id AND t.lang = :lang)'
311
            )
312
            ->leftJoin(
313
                'n',
314
                'kuma_node_translations',
315
                'v',
316
                '(v.node_id = n.id AND v.lang <> :lang)'
317
            )
318
            ->where('n.deleted = 0')
319
            ->addGroupBy('n.id')
320
            ->addOrderBy('t.weight', 'ASC')
321
            ->addOrderBy('t.title', 'ASC');
322
323
        if (!$includeHiddenFromNav) {
324
            $qb->andWhere('n.hidden_from_nav <> 0');
325
        }
326
327
        if (!is_null($rootNode)) {
328
            $qb->andWhere('n.lft >= :left')
329
                ->andWhere('n.rgt <= :right');
330
        }
331
332
        $permissionDef = new PermissionDefinition(array($permission));
333
        $permissionDef->setEntity('Kunstmaan\NodeBundle\Entity\Node');
334
        $permissionDef->setAlias('n');
335
        $qb = $aclNativeHelper->apply($qb, $permissionDef);
336
337
        $stmt = $this->_em->getConnection()->prepare($qb->getSQL());
338
        $stmt->bindValue(':lang', $lang);
339
        if (!is_null($rootNode)) {
340
            $stmt->bindValue(':left', $rootNode->getLeft());
341
            $stmt->bindValue(':right', $rootNode->getRight());
342
        }
343
        $stmt->execute();
344
345
        return $stmt->fetchAll();
346
    }
347
348
    /**
349
     * Get all parents of a given node. We can go multiple levels up.
350
     *
351
     * @param Node   $node
352
     * @param string $lang
353
     *
354
     * @return Node[]
355
     */
356 View Code Duplication
    public function getAllParents(Node $node = null, $lang = null)
357
    {
358
        if (is_null($node)) {
359
            return array();
360
        }
361
362
        $qb = $this->createQueryBuilder('node');
363
364
        // Directly hydrate the nodeTranslation and nodeVersion
365
        $qb->select('node', 't', 'v')
366
            ->innerJoin('node.nodeTranslations', 't')
367
            ->leftJoin(
368
                't.publicNodeVersion',
369
                'v',
370
                'WITH',
371
                't.publicNodeVersion = v.id'
372
            )
373
            ->where('node.deleted = 0');
374
375
        if ($lang) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
376
            $qb->andWhere('t.lang = :lang')
377
                ->setParameter('lang', $lang);
378
        }
379
380
        $qb->andWhere(
381
            $qb->expr()->andX(
382
                $qb->expr()->lte('node.lft', $node->getLeft()),
383
                $qb->expr()->gte('node.rgt', $node->getRight())
384
            )
385
        );
386
387
        $qb->addOrderBy('node.lft', 'ASC');
388
389
        return $qb->getQuery()->getResult();
390
    }
391
392
    /**
393
     * Get the root node of a given node.
394
     *
395
     * @param Node   $node
396
     * @param string $lang
397
     *
398
     * @return Node
399
     */
400 View Code Duplication
    public function getRootNodeFor(Node $node = null, $lang = null)
401
    {
402
        if (is_null($node)) {
403
            return null;
404
        }
405
406
        $qb = $this->createQueryBuilder('node');
407
408
        // Directly hydrate the nodeTranslation and nodeVersion
409
        $qb->select('node', 't', 'v')
410
            ->innerJoin('node.nodeTranslations', 't')
411
            ->leftJoin(
412
                't.publicNodeVersion',
413
                'v',
414
                'WITH',
415
                't.publicNodeVersion = v.id'
416
            )
417
            ->where('node.deleted = 0')
418
            ->andWhere('node.parent IS NULL');
419
420
        if ($lang) {
421
            $qb->andWhere('t.lang = :lang')
422
                ->setParameter('lang', $lang);
423
        }
424
425
        $qb->andWhere(
426
            $qb->expr()->andX(
427
                $qb->expr()->lte('node.lft', $node->getLeft()),
428
                $qb->expr()->gte('node.rgt', $node->getRight())
429
            )
430
        );
431
432
        return $qb->getQuery()->getOneOrNullResult();
433
    }
434
435
    /**
436
     * @return Node[]
437
     */
438
    public function getAllTopNodes()
439
    {
440
        $qb = $this->createQueryBuilder('b')
441
            ->select('b', 't', 'v')
442
            ->leftJoin('b.nodeTranslations', 't')
443
            ->leftJoin(
444
                't.publicNodeVersion',
445
                'v',
446
                'WITH',
447
                't.publicNodeVersion = v.id'
448
            )
449
            ->where('b.deleted = 0')
450
            ->andWhere('b.parent IS NULL');
451
452
        $result = $qb->getQuery()->getResult();
453
454
        return $result;
455
    }
456
457
    /**
458
     * Get an array of Nodes based on the internal name.
459
     *
460
     * @param string        $internalName   The internal name of the node
461
     * @param string        $lang           The locale
462
     * @param int|null|bool $parentId       The parent id
463
     * @param bool          $includeOffline Include offline nodes
464
     *
465
     * @return Node[]
466
     */
467
    public function getNodesByInternalName(
468
        $internalName,
469
        $lang,
470
        $parentId = false,
471
        $includeOffline = false
472
    ) {
473
        $qb = $this->createQueryBuilder('n')
474
            ->select('n', 't', 'v')
475
            ->innerJoin('n.nodeTranslations', 't')
476
            ->leftJoin(
477
                't.publicNodeVersion',
478
                'v',
479
                'WITH',
480
                't.publicNodeVersion = v.id'
481
            )
482
            ->where('n.deleted = 0')
483
            ->andWhere('n.internalName = :internalName')
484
            ->setParameter('internalName', $internalName)
485
            ->andWhere('t.lang = :lang')
486
            ->setParameter('lang', $lang)
487
            ->addOrderBy('t.weight', 'ASC')
488
            ->addOrderBy('t.title', 'ASC');
489
490
        if (!$includeOffline) {
491
            $qb->andWhere('t.online = true');
492
        }
493
494 View Code Duplication
        if (is_null($parentId)) {
495
            $qb->andWhere('n.parent is NULL');
496
        } elseif ($parentId === false) {
497
            // Do nothing
498
        } else {
499
            $qb->andWhere('n.parent = :parent')
500
                ->setParameter('parent', $parentId);
501
        }
502
503
        $query = $qb->getQuery();
504
505
        return $query->getResult();
506
    }
507
508
    /**
509
     * Get a single node by internal name.
510
     *
511
     * @param string $internalName The internal name of the node
512
     *
513
     * @return Node
514
     */
515
    public function getNodeByInternalName($internalName)
516
    {
517
        $qb = $this->createQueryBuilder('n')
518
            ->select('n')
519
            ->where('n.deleted = 0')
520
            ->andWhere('n.internalName = :internalName')
521
            ->setParameter('internalName', $internalName);
522
523
        return $qb->getQuery()->getOneOrNullResult();
524
    }
525
526
    /**
527
     * Finds all different page classes currently registered as nodes
528
     *
529
     * @return string[]
530
     */
531
    public function findAllDistinctPageClasses()
532
    {
533
        $qb = $this->createQueryBuilder('n')
534
            ->select('n.refEntityName')
535
            ->distinct(true);
536
537
        return $qb->getQuery()->getArrayResult();
538
    }
539
}
540