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

Repository/NodeTranslationRepository.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 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