Completed
Push — master ( 6d6774...64f3ed )
by Jeroen
11:23 queued 05:13
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
            } elseif ($r = $this->findOneBy(['slug' => $slugPart])) {
180
                $result = $r;
181
            }
182
        }
183
184
        return $result;
185
    }
186
187
    /**
188
     * @param HasNodeInterface $hasNode      The object to link to
189
     * @param string           $lang         The locale
190
     * @param BaseUser         $owner        The user
191
     * @param string           $internalName The internal name (may be null)
192
     *
193
     * @throws \InvalidArgumentException
194
     *
195
     * @return Node
196
     */
197
    public function createNodeFor(
198
        HasNodeInterface $hasNode,
199
        $lang,
200
        BaseUser $owner,
201
        $internalName = null
202
    ) {
203
        $em = $this->getEntityManager();
204
        $node = new Node();
205
        $node->setRef($hasNode);
206
        if (!$hasNode->getId() > 0) {
207
            throw new \InvalidArgumentException('the entity of class '. $node->getRefEntityName().' has no id, maybe you forgot to flush first');
208
        }
209
        $node->setDeleted(false);
210
        $node->setInternalName($internalName);
211
        $parent = $hasNode->getParent();
212
        if ($parent) {
213
            /* @var NodeVersion $parentNodeVersion */
214
            $parentNodeVersion = $em->getRepository(
215
                'KunstmaanNodeBundle:NodeVersion'
216
            )->findOneBy(
217
                array(
218
                    'refId' => $parent->getId(),
219
                    'refEntityName' => ClassLookup::getClass($parent),
220
                )
221
            );
222
            if ($parentNodeVersion) {
223
                $node->setParent(
224
                    $parentNodeVersion->getNodeTranslation()->getNode()
225
                );
226
            }
227
        }
228
        if ($hasNode instanceof HiddenFromNavInterface) {
229
            $node->setHiddenFromNav($hasNode->isHiddenFromNav());
230
        }
231
        $em->persist($node);
232
        $em->flush();
233
        $em->refresh($node);
234
        $em->getRepository('KunstmaanNodeBundle:NodeTranslation')
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeTranslationFor() 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...
235
            ->createNodeTranslationFor(
236
                $hasNode,
237
                $lang,
238
                $node,
239
                $owner
240
            );
241
242
        return $node;
243
    }
244
245
    /**
246
     * Get all the information needed to build a menu tree with one query.
247
     * We only fetch the fields we need, instead of fetching full objects to
248
     * limit the memory usage.
249
     *
250
     * @param string          $lang                 The locale
251
     * @param string          $permission           The permission (read,
252
     *                                              write, ...)
253
     * @param AclNativeHelper $aclNativeHelper      The acl helper
254
     * @param bool            $includeHiddenFromNav Include nodes hidden from
255
     *                                              navigation or not
256
     * @param Node            $rootNode             The root node of the
257
     *                                              current site
258
     *
259
     * @return array
260
     */
261
    public function getAllMenuNodes(
262
        $lang,
263
        $permission,
264
        AclNativeHelper $aclNativeHelper,
265
        $includeHiddenFromNav = false,
266
        Node $rootNode = null
267
    ) {
268
        $connection = $this->_em->getConnection();
269
        $qb = $connection->createQueryBuilder();
270
        $databasePlatformName = $connection->getDatabasePlatform()->getName();
271
        $createIfStatement = function (
272
            $expression,
273
            $trueValue,
274
            $falseValue
275
        ) use ($databasePlatformName) {
276
            switch ($databasePlatformName) {
277
                case 'sqlite':
278
                    $statement = 'CASE WHEN %s THEN %s ELSE %s END';
279
280
                    break;
281
282
                default:
283
                    $statement = 'IF(%s, %s, %s)';
284
            }
285
286
            return sprintf($statement, $expression, $trueValue, $falseValue);
287
        };
288
289
        $sql = <<<SQL
290
n.id, n.parent_id AS parent, t.url, t.id AS nt_id,
291
{$createIfStatement('t.weight IS NULL', 'v.weight', 't.weight')} AS weight,
292
{$createIfStatement('t.title IS NULL', 'v.title', 't.title')} AS title,
293
{$createIfStatement('t.online IS NULL', '0', 't.online')} AS online,
294
n.hidden_from_nav AS hidden,
295
n.ref_entity_name AS ref_entity_name
296
SQL;
297
298
        $qb->select($sql)
299
            ->from('kuma_nodes', 'n')
300
            ->leftJoin(
301
                'n',
302
                'kuma_node_translations',
303
                't',
304
                '(t.node_id = n.id AND t.lang = :lang)'
305
            )
306
            ->leftJoin(
307
                'n',
308
                'kuma_node_translations',
309
                'v',
310
                '(v.node_id = n.id AND v.lang <> :lang)'
311
            )
312
            ->where('n.deleted = 0')
313
            ->addGroupBy('n.id')
314
            ->addOrderBy('t.weight', 'ASC')
315
            ->addOrderBy('t.title', 'ASC');
316
317
        if (!$includeHiddenFromNav) {
318
            $qb->andWhere('n.hidden_from_nav <> 0');
319
        }
320
321
        if (!\is_null($rootNode)) {
322
            $qb->andWhere('n.lft >= :left')
323
                ->andWhere('n.rgt <= :right');
324
        }
325
326
        $permissionDef = new PermissionDefinition(array($permission));
327
        $permissionDef->setEntity('Kunstmaan\NodeBundle\Entity\Node');
328
        $permissionDef->setAlias('n');
329
        $qb = $aclNativeHelper->apply($qb, $permissionDef);
330
331
        $stmt = $this->_em->getConnection()->prepare($qb->getSQL());
332
        $stmt->bindValue(':lang', $lang);
333
        if (!\is_null($rootNode)) {
334
            $stmt->bindValue(':left', $rootNode->getLeft());
335
            $stmt->bindValue(':right', $rootNode->getRight());
336
        }
337
        $stmt->execute();
338
339
        return $stmt->fetchAll();
340
    }
341
342
    /**
343
     * Get all parents of a given node. We can go multiple levels up.
344
     *
345
     * @param Node   $node
346
     * @param string $lang
347
     *
348
     * @return Node[]
349
     */
350 View Code Duplication
    public function getAllParents(Node $node = null, $lang = null)
351
    {
352
        if (\is_null($node)) {
353
            return array();
354
        }
355
356
        $qb = $this->createQueryBuilder('node');
357
358
        // Directly hydrate the nodeTranslation and nodeVersion
359
        $qb->select('node', 't', 'v')
360
            ->innerJoin('node.nodeTranslations', 't')
361
            ->leftJoin(
362
                't.publicNodeVersion',
363
                'v',
364
                'WITH',
365
                't.publicNodeVersion = v.id'
366
            )
367
            ->where('node.deleted = 0');
368
369
        if ($lang) {
370
            $qb->andWhere('t.lang = :lang')
371
                ->setParameter('lang', $lang);
372
        }
373
374
        $qb->andWhere(
375
            $qb->expr()->andX(
376
                $qb->expr()->lte('node.lft', $node->getLeft()),
377
                $qb->expr()->gte('node.rgt', $node->getRight())
378
            )
379
        );
380
381
        $qb->addOrderBy('node.lft', 'ASC');
382
383
        return $qb->getQuery()->getResult();
384
    }
385
386
    /**
387
     * Get the root node of a given node.
388
     *
389
     * @param Node   $node
390
     * @param string $lang
391
     *
392
     * @return Node
393
     */
394 View Code Duplication
    public function getRootNodeFor(Node $node = null, $lang = null)
395
    {
396
        if (\is_null($node)) {
397
            return null;
398
        }
399
400
        $qb = $this->createQueryBuilder('node');
401
402
        // Directly hydrate the nodeTranslation and nodeVersion
403
        $qb->select('node', 't', 'v')
404
            ->innerJoin('node.nodeTranslations', 't')
405
            ->leftJoin(
406
                't.publicNodeVersion',
407
                'v',
408
                'WITH',
409
                't.publicNodeVersion = v.id'
410
            )
411
            ->where('node.deleted = 0')
412
            ->andWhere('node.parent IS NULL');
413
414
        if ($lang) {
415
            $qb->andWhere('t.lang = :lang')
416
                ->setParameter('lang', $lang);
417
        }
418
419
        $qb->andWhere(
420
            $qb->expr()->andX(
421
                $qb->expr()->lte('node.lft', $node->getLeft()),
422
                $qb->expr()->gte('node.rgt', $node->getRight())
423
            )
424
        );
425
426
        return $qb->getQuery()->getOneOrNullResult();
427
    }
428
429
    /**
430
     * @return Node[]
431
     */
432
    public function getAllTopNodes()
433
    {
434
        $qb = $this->createQueryBuilder('b')
435
            ->select('b', 't', 'v')
436
            ->leftJoin('b.nodeTranslations', 't')
437
            ->leftJoin(
438
                't.publicNodeVersion',
439
                'v',
440
                'WITH',
441
                't.publicNodeVersion = v.id'
442
            )
443
            ->where('b.deleted = 0')
444
            ->andWhere('b.parent IS NULL');
445
446
        return $qb->getQuery()->getResult();
447
    }
448
449
    /**
450
     * Get an array of Nodes based on the internal name.
451
     *
452
     * @param string        $internalName   The internal name of the node
453
     * @param string        $lang           The locale
454
     * @param int|null|bool $parentId       The parent id
455
     * @param bool          $includeOffline Include offline nodes
456
     *
457
     * @return Node[]
458
     */
459
    public function getNodesByInternalName(
460
        $internalName,
461
        $lang,
462
        $parentId = false,
463
        $includeOffline = false
464
    ) {
465
        $qb = $this->createQueryBuilder('n')
466
            ->select('n', 't', 'v')
467
            ->innerJoin('n.nodeTranslations', 't')
468
            ->leftJoin(
469
                't.publicNodeVersion',
470
                'v',
471
                'WITH',
472
                't.publicNodeVersion = v.id'
473
            )
474
            ->where('n.deleted = 0')
475
            ->andWhere('n.internalName = :internalName')
476
            ->setParameter('internalName', $internalName)
477
            ->andWhere('t.lang = :lang')
478
            ->setParameter('lang', $lang)
479
            ->addOrderBy('t.weight', 'ASC')
480
            ->addOrderBy('t.title', 'ASC');
481
482
        if (!$includeOffline) {
483
            $qb->andWhere('t.online = true');
484
        }
485
486 View Code Duplication
        if (\is_null($parentId)) {
487
            $qb->andWhere('n.parent is NULL');
488
        } elseif ($parentId === false) {
489
            // Do nothing
490
        } else {
491
            $qb->andWhere('n.parent = :parent')
492
                ->setParameter('parent', $parentId);
493
        }
494
495
        $query = $qb->getQuery();
496
497
        return $query->getResult();
498
    }
499
500
    /**
501
     * Get a single node by internal name.
502
     *
503
     * @param string $internalName The internal name of the node
504
     *
505
     * @return Node
506
     */
507
    public function getNodeByInternalName($internalName)
508
    {
509
        $qb = $this->createQueryBuilder('n')
510
            ->select('n')
511
            ->where('n.deleted = 0')
512
            ->andWhere('n.internalName = :internalName')
513
            ->setParameter('internalName', $internalName);
514
515
        return $qb->getQuery()->getOneOrNullResult();
516
    }
517
518
    /**
519
     * Finds all different page classes currently registered as nodes
520
     *
521
     * @return string[]
522
     */
523
    public function findAllDistinctPageClasses()
524
    {
525
        $qb = $this->createQueryBuilder('n')
526
            ->select('n.refEntityName')
527
            ->where('n.deleted = 0')
528
            ->distinct(true);
529
530
        return $qb->getQuery()->getArrayResult();
531
    }
532
533
    public function getChildCount(Node $node, bool $direct = false, bool $includeDeleted = false): int
534
    {
535
        $qb = $this->getChildrenQueryBuilder($node, $direct);
536
        $qb->resetDQLPart('orderBy');
537
538
        $aliases = $qb->getRootAliases();
539
        $alias = $aliases[0];
540
541
        $qb->select('COUNT('.$alias.')');
542
543
        if (false === $includeDeleted) {
544
            $qb->andWhere($alias.'.deleted = 0');
545
        }
546
547
        return (int) $qb->getQuery()->getSingleScalarResult();
548
    }
549
}
550