Completed
Push — master ( aba493...5356ed )
by Ruud
315:38 queued 305:00
created

Repository/NodeTranslationRepository.php (2 issues)

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)
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
0 ignored issues
show
Should the type for parameter $parentNode not be null|Node?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
49
     * @param string $lang       (optional) Only return max weight for the
0 ignored issues
show
Should the type for parameter $lang not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

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