Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

Repository/NodeTranslationRepository.php (1 issue)

Check for PhpDoc comments which do parse

Documentation Minor

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 Doctrine\ORM\EntityRepository;
6
use Doctrine\ORM\Query\ResultSetMappingBuilder;
7
use Doctrine\ORM\QueryBuilder;
8
use Kunstmaan\AdminBundle\Entity\BaseUser;
9
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
10
use Kunstmaan\NodeBundle\Entity\Node;
11
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
12
use Kunstmaan\NodeBundle\Entity\NodeVersion;
13
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
14
15
/**
16
 * NodeRepository
17
 */
18
class NodeTranslationRepository extends EntityRepository
19
{
20
    /**
21
     * Get the QueryBuilder based on node id and language.
22
     *
23
     * @param int    $nodeId
24
     * @param string $lang
25
     *
26
     * @return array_shift($result)
0 ignored issues
show
The doc-type array_shift($result) could not be parsed: Expected "|" or "end of type", but got "(" at position 11. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
27
     */
28
    public function getNodeTranslationByNodeIdQueryBuilder($nodeId, $lang)
29
    {
30
        $qb = $this->createQueryBuilder('nt')
31
            ->select('nt')
32
            ->innerJoin('nt.node', 'n', 'WITH', 'nt.node = n.id')
33
            ->where('n.deleted != 1')
34
            ->andWhere('nt.online = 1')
35
            ->andWhere('nt.lang = :lang')
36
            ->setParameter('lang', $lang)
37
            ->andWhere('n.id = :node_id')
38
            ->setParameter('node_id', $nodeId)
39
            ->setFirstResult(0)
40
            ->setMaxResults(1);
41
42
        return $qb->getQuery()->getOneOrNullResult();
43
    }
44
45
    /**
46
     * Get max children weight
47
     *
48
     * @param Node   $parentNode
49
     * @param string $lang       (optional) Only return max weight for the
50
     *                           given language
51
     *
52
     * @return int
53
     */
54
    public function getMaxChildrenWeight(Node $parentNode = null, $lang = null)
55
    {
56
        $maxWeight = $this->getNodeTranslationsQueryBuilder($lang)
57
            ->select('max(nt.weight)')
58
            ->andWhere('n.parent = :parentNode')
59
            ->setParameter('parentNode', $parentNode)
60
            ->getQuery()
61
            ->getSingleScalarResult();
62
63
        return (int) $maxWeight;
64
    }
65
66
    /**
67
     * QueryBuilder to fetch node translations (ignoring nodes that have been
68
     * deleted)
69
     *
70
     * @param string $lang (optional) Only return NodeTranslations for the
71
     *                     given language
72
     *
73
     * @return \Doctrine\ORM\QueryBuilder
74
     */
75
    public function getNodeTranslationsQueryBuilder($lang = null)
76
    {
77
        $queryBuilder = $this->createQueryBuilder('nt')
78
            ->select('nt,n,v')
79
            ->innerJoin('nt.node', 'n')
80
            ->leftJoin(
81
                'nt.publicNodeVersion',
82
                'v',
83
                'WITH',
84
                'nt.publicNodeVersion = v.id'
85
            )
86
            ->where('n.deleted = false')
87
            ->orderBy('nt.weight')
88
        ;
89
90
        if (!empty($lang)) {
91
            $queryBuilder
92
                ->andWhere('nt.lang = :lang')
93
                ->setParameter('lang', $lang);
94
        }
95
96
        return $queryBuilder;
97
    }
98
99
    /**
100
     * QueryBuilder to fetch node translations that are currently published
101
     * (ignoring nodes that have been deleted)
102
     *
103
     * @param string $lang (optional) Only return NodeTranslations for the
104
     *                     given language
105
     *
106
     * @return \Doctrine\ORM\QueryBuilder
107
     */
108
    public function getOnlineNodeTranslationsQueryBuilder($lang = null)
109
    {
110
        return $this->getNodeTranslationsQueryBuilder($lang)
111
            ->andWhere('nt.online = true');
112
    }
113
114
    /**
115
     * QueryBuilder to fetch immediate child NodeTranslations for a specific
116
     * node and (optional) language
117
     *
118
     * @return \Doctrine\ORM\QueryBuilder
119
     */
120
    public function getChildrenQueryBuilder(Node $parent, $lang = null)
121
    {
122
        return $this->getNodeTranslationsQueryBuilder($lang)
123
            ->andWhere('n.parent = :parent')
124
            ->setParameter('parent', $parent);
125
    }
126
127
    /**
128
     * QueryBuilder to fetch immediate child NodeTranslations for a specific
129
     * node and (optional) language that are currently published
130
     *
131
     * @return \Doctrine\ORM\QueryBuilder
132
     */
133
    public function getOnlineChildrenQueryBuilder(Node $parent, $lang = null)
134
    {
135
        return $this->getChildrenQueryBuilder($parent, $lang)
136
            ->andWhere('nt.online = true');
137
    }
138
139
    /**
140
     * Get all online child node translations for a given node and (optional)
141
     * language
142
     *
143
     * @param string $lang (optional, if not specified all languages will be
144
     *                     returned)
145
     *
146
     * @return array
147
     */
148
    public function getOnlineChildren(Node $parent, $lang = null)
149
    {
150
        return $this->getOnlineChildrenQueryBuilder($parent, $lang)
151
            ->getQuery()->getResult();
152
    }
153
154
    /**
155
     * Finds all nodetranslations where title is like the given $title parameter
156
     *
157
     * @param string $title
158
     * @param string $lang  (optional, if not specified all languages will be
159
     *                      returned)
160
     *
161
     * @return array
162
     */
163
    public function getNodeTranslationsLikeTitle($title, $lang = null)
164
    {
165
        /** @var QueryBuilder $qb */
166
        $qb = $this->getNodeTranslationsQueryBuilder($lang);
167
        $qb->andWhere('nt.title like :title')
168
            ->setParameter('title', '%' . $title . '%');
169
170
        return $qb->getQuery()->getResult();
171
    }
172
173
    /**
174
     * Get the node translation for a node
175
     *
176
     * @return NodeTranslation
177
     */
178
    public function getNodeTranslationFor(HasNodeInterface $hasNode)
179
    {
180
        /* @var NodeVersion $nodeVersion */
181
        $nodeVersion = $this->getEntityManager()
182
            ->getRepository(NodeVersion::class)
183
            ->getNodeVersionFor($hasNode);
184
185
        if (!\is_null($nodeVersion)) {
186
            return $nodeVersion->getNodeTranslation();
187
        }
188
189
        return null;
190
    }
191
192
    /**
193
     * Get the node translation for a given slug string
194
     *
195
     * @param string               $slug       The slug
196
     * @param NodeTranslation|null $parentNode The parentnode
197
     *
198
     * @return NodeTranslation|null
199
     */
200
    public function getNodeTranslationForSlug(
201
        $slug,
202
        NodeTranslation $parentNode = null
203
    ) {
204
        if (empty($slug)) {
205
            return $this->getNodeTranslationForSlugPart(null, $slug);
206
        }
207
208
        $slugParts = explode('/', $slug);
209
        $result = $parentNode;
210
        foreach ($slugParts as $slugPart) {
211
            $result = $this->getNodeTranslationForSlugPart($result, $slugPart);
212
        }
213
214
        return $result;
215
    }
216
217
    /**
218
     * Returns the node translation for a given slug
219
     *
220
     * @param NodeTranslation|null $parentNode The parentNode
221
     * @param string               $slugPart   The slug part
222
     *
223
     * @return NodeTranslation|null
224
     */
225
    private function getNodeTranslationForSlugPart(
226
        NodeTranslation $parentNode = null,
227
        $slugPart = ''
228
    ) {
229
        $qb = $this->createQueryBuilder('t')
230
            ->select('t', 'v', 'n')
231
            ->innerJoin('t.node', 'n', 'WITH', 't.node = n.id')
232
            ->leftJoin(
233
                't.publicNodeVersion',
234
                'v',
235
                'WITH',
236
                't.publicNodeVersion = v.id'
237
            )
238
            ->where('n.deleted != 1')
239
            ->setFirstResult(0)
240
            ->setMaxResults(1);
241
242
        if ($parentNode !== null) {
243
            $qb->andWhere('t.slug = :slug')
244
                ->andWhere('n.parent = :parent')
245
                ->setParameter('slug', $slugPart)
246
                ->setParameter('parent', $parentNode->getNode()->getId());
247
        } else {
248
            /* if parent is null we should look for slugs that have no parent */
249
            $qb->andWhere('n.parent IS NULL');
250
            if (empty($slugPart)) {
251
                $qb->andWhere('t.slug is NULL');
252
            } else {
253
                $qb->andWhere('t.slug = :slug');
254
                $qb->setParameter('slug', $slugPart);
255
            }
256
        }
257
258
        return $qb->getQuery()->getOneOrNullResult();
259
    }
260
261
    /**
262
     * Get the node translation for a given url
263
     *
264
     * @param string          $urlSlug        The full url
265
     * @param string          $locale         The locale
266
     * @param bool            $includeDeleted Include deleted nodes
267
     * @param NodeTranslation $toExclude      Optional NodeTranslation instance
268
     *                                        you wish to exclude
269
     * @param Node            $rootNode       Optional Root node of the tree you
270
     *                                        wish to use
271
     *
272
     * @return array
273
     */
274
    public function getAllNodeTranslationsForUrl(
275
        $urlSlug,
276
        $locale = '',
277
        $includeDeleted = false,
278
        NodeTranslation $toExclude = null,
279
        Node $rootNode = null
280
    ) {
281
        $qb = $this->createQueryBuilder('b')
282
            ->select('b', 'v')
283
            ->innerJoin('b.node', 'n', 'WITH', 'b.node = n.id')
284
            ->leftJoin(
285
                'b.publicNodeVersion',
286
                'v',
287
                'WITH',
288
                'b.publicNodeVersion = v.id'
289
            )
290
            ->addOrderBy('b.online', 'DESC')
291
            ->setFirstResult(0)
292
            ->setMaxResults(1);
293
294
        if (!$includeDeleted) {
295
            $qb->andWhere('n.deleted = 0');
296
        }
297
298
        if (!empty($locale)) {
299
            $qb->andWhere('b.lang = :lang')
300
                ->setParameter('lang', $locale);
301
        }
302
303
        if (empty($urlSlug)) {
304
            $qb->andWhere('b.url IS NULL');
305
        } else {
306
            $qb->andWhere('b.url = :url');
307
            $qb->setParameter('url', $urlSlug);
308
        }
309
310
        if (!\is_null($toExclude)) {
311
            $qb->andWhere('NOT b.id = :exclude_id')
312
                ->setParameter('exclude_id', $toExclude->getId());
313
        }
314
315
        if ($rootNode) {
316
            $qb->andWhere('n.lft >= :left')
317
                ->andWhere('n.rgt <= :right')
318
                ->setParameter('left', $rootNode->getLeft())
319
                ->setParameter('right', $rootNode->getRight());
320
        }
321
322
        return $qb->getQuery()->getResult();
323
    }
324
325
    /**
326
     * Get the node translation for a given url
327
     *
328
     * @param string          $urlSlug        The full url
329
     * @param string          $locale         The locale
330
     * @param bool            $includeDeleted Include deleted nodes
331
     * @param NodeTranslation $toExclude      Optional NodeTranslation instance
332
     *                                        you wish to exclude
333
     * @param Node            $rootNode       Optional Root node of the tree you
334
     *                                        wish to use
335
     *
336
     * @return NodeTranslation|null
337
     */
338
    public function getNodeTranslationForUrl(
339
        $urlSlug,
340
        $locale = '',
341
        $includeDeleted = false,
342
        NodeTranslation $toExclude = null,
343
        Node $rootNode = null
344
    ) {
345
        $translations = $this->getAllNodeTranslationsForUrl($urlSlug, $locale, $includeDeleted, $toExclude, $rootNode);
346
347
        if (empty($translations)) {
348
            return null;
349
        }
350
351
        return $translations[0];
352
    }
353
354
    /**
355
     * Get all top node translations
356
     *
357
     * @return NodeTranslation[]
358
     */
359
    public function getTopNodeTranslations()
360
    {
361
        $qb = $this->createQueryBuilder('b')
362
            ->select('b', 'v')
363
            ->innerJoin('b.node', 'n', 'WITH', 'b.node = n.id')
364
            ->leftJoin(
365
                'b.publicNodeVersion',
366
                'v',
367
                'WITH',
368
                'b.publicNodeVersion = v.id'
369
            )
370
            ->where('n.parent IS NULL')
371
            ->andWhere('n.deleted != 1');
372
373
        return $qb->getQuery()->getResult();
374
    }
375
376
    /**
377
     * Create a node translation for a given node
378
     *
379
     * @param HasNodeInterface $hasNode The hasNode
380
     * @param string           $lang    The locale
381
     * @param Node             $node    The node
382
     * @param BaseUser         $owner   The user
383
     *
384
     * @throws \InvalidArgumentException
385
     *
386
     * @return NodeTranslation
387
     */
388
    public function createNodeTranslationFor(
389
        HasNodeInterface $hasNode,
390
        $lang,
391
        Node $node,
392
        BaseUser $owner
393
    ) {
394
        $em = $this->getEntityManager();
395
        $className = ClassLookup::getClass($hasNode);
396
        if (!$hasNode->getId() > 0) {
397
            throw new \InvalidArgumentException('The entity of class ' . $className . ' has no id, maybe you forgot to flush first');
398
        }
399
400
        $nodeTranslation = new NodeTranslation();
401
        $nodeTranslation
402
            ->setNode($node)
403
            ->setLang($lang)
404
            ->setTitle($hasNode->getTitle())
405
            ->setOnline(false)
406
            ->setWeight(0);
407
408
        $em->persist($nodeTranslation);
409
410
        $nodeVersion = $em->getRepository(NodeVersion::class)
411
            ->createNodeVersionFor(
412
                $hasNode,
413
                $nodeTranslation,
414
                $owner,
415
                null
416
            );
417
418
        $nodeTranslation->setPublicNodeVersion($nodeVersion);
419
        $em->persist($nodeTranslation);
420
        $em->flush();
421
        $em->refresh($nodeTranslation);
422
        $em->refresh($node);
423
424
        return $nodeTranslation;
425
    }
426
427
    /**
428
     * Add a draft node version for a given node
429
     *
430
     * @param HasNodeInterface $hasNode The hasNode
431
     * @param string           $lang    The locale
432
     * @param Node             $node    The node
433
     * @param BaseUser         $owner   The user
434
     *
435
     * @throws \InvalidArgumentException
436
     *
437
     * @return NodeTranslation
438
     */
439
    public function addDraftNodeVersionFor(
440
        HasNodeInterface $hasNode,
441
        $lang,
442
        Node $node,
443
        BaseUser $owner
444
    ) {
445
        $em = $this->getEntityManager();
446
        $className = ClassLookup::getClass($hasNode);
447
        if (!$hasNode->getId() > 0) {
448
            throw new \InvalidArgumentException('The entity of class ' . $className . ' has no id, maybe you forgot to flush first');
449
        }
450
451
        $nodeTranslation = $em->getRepository(NodeTranslation::class)->findOneBy(['lang' => $lang, 'node' => $node]);
452
453
        $em->getRepository(NodeVersion::class)
454
            ->createNodeVersionFor(
455
                $hasNode,
456
                $nodeTranslation,
457
                $owner,
458
                null,
459
                NodeVersion::DRAFT_VERSION
460
            );
461
462
        $em->refresh($nodeTranslation);
463
        $em->refresh($node);
464
465
        return $nodeTranslation;
466
    }
467
468
    /**
469
     * Find best match for given URL and locale
470
     *
471
     * @param string $urlSlug The slug
472
     * @param string $locale  The locale
473
     *
474
     * @return NodeTranslation
475
     */
476
    public function getBestMatchForUrl($urlSlug, $locale)
477
    {
478
        $em = $this->getEntityManager();
479
480
        $rsm = new ResultSetMappingBuilder($em);
481
        $rsm->addRootEntityFromClassMetadata(
482
            'Kunstmaan\NodeBundle\Entity\NodeTranslation',
483
            'nt'
484
        );
485
486
        $query = $em
487
            ->createNativeQuery(
488
                'select nt.*
489
                from kuma_node_translations nt
490
                join kuma_nodes n on n.id = nt.node_id
491
                where n.deleted = 0 and nt.lang = :lang and locate(nt.url, :url) = 1
492
                order by length(nt.url) desc limit 1',
493
                $rsm
494
            );
495
        $query->setParameter('lang', $locale);
496
        $query->setParameter('url', $urlSlug);
497
498
        return $query->getOneOrNullResult();
499
    }
500
501
    /**
502
     * Test if all parents of the specified NodeTranslation have a node
503
     * translation for the specified language
504
     *
505
     * @param NodeTranslation $nodeTranslation The node translation
506
     * @param string          $language        The locale
507
     *
508
     * @return bool
509
     */
510
    public function hasParentNodeTranslationsForLanguage(
511
        NodeTranslation $nodeTranslation,
512
        $language
513
    ) {
514
        $parentNode = $nodeTranslation->getNode()->getParent();
515
        if ($parentNode !== null) {
516
            $parentNodeTranslation = $parentNode->getNodeTranslation(
517
                $language,
518
                true
519
            );
520
            if ($parentNodeTranslation !== null) {
521
                return $this->hasParentNodeTranslationsForLanguage(
522
                    $parentNodeTranslation,
523
                    $language
524
                );
525
            }
526
527
            return false;
528
        }
529
530
        return true;
531
    }
532
533
    /**
534
     * This will return 1 NodeTranslation by default (if one exists).
535
     * Just give it the internal name as defined on the Node in the database
536
     * and the language.
537
     *
538
     * It'll only return the latest version. It'll also hide deleted & offline
539
     * nodes.
540
     *
541
     * @param $language
542
     * @param $internalName
543
     */
544
    public function getNodeTranslationByLanguageAndInternalName(
545
        $language,
546
        $internalName
547
    ) {
548
        $qb = $this->createQueryBuilder('nt')
549
            ->select('nt', 'v')
550
            ->innerJoin('nt.node', 'n', 'WITH', 'nt.node = n.id')
551
            ->leftJoin(
552
                'nt.publicNodeVersion',
553
                'v',
554
                'WITH',
555
                'nt.publicNodeVersion = v.id'
556
            )
557
            ->where('n.deleted != 1')
558
            ->andWhere('nt.online = 1')
559
            ->setFirstResult(0)
560
            ->setMaxResults(1);
561
562
        $qb->andWhere('nt.lang = :lang')
563
            ->setParameter('lang', $language);
564
565
        $qb->andWhere('n.internalName = :internal_name')
566
            ->setParameter('internal_name', $internalName);
567
568
        return $qb->getQuery()->getOneOrNullResult();
569
    }
570
571
    public function getAllNodeTranslationsByRefEntityName($refEntityName)
572
    {
573
        $qb = $this->createQueryBuilder('nt')
574
            ->select('nt,n')
575
            ->innerJoin('nt.publicNodeVersion', 'nv')
576
            ->innerJoin('nt.node', 'n')
577
            ->where('nv.refEntityName = :refEntityName')
578
            ->setParameter('refEntityName', $refEntityName);
579
580
        return $qb->getQuery()->getResult();
581
    }
582
583
    public function getParentNodeTranslation(NodeTranslation $nodeTranslation)
584
    {
585
        $parent = $nodeTranslation->getNode()->getParent();
586
        if (\is_null($parent)) {
587
            return null;
588
        }
589
590
        $qb = $this->createQueryBuilder('nt')
591
            ->select('nt,n')
592
            ->innerJoin('nt.publicNodeVersion', 'nv')
593
            ->innerJoin('nt.node', 'n')
594
            ->where('nt.node = :parent')
595
            ->andWhere('n.deleted = 0')
596
            ->andWhere('nt.lang = :lang')
597
            ->setParameter('parent', $parent)
598
            ->setParameter('lang', $nodeTranslation->getLang());
599
600
        return $qb->getQuery()->getOneOrNullResult();
601
    }
602
}
603