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