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