TranslationExtension::applyToCollection()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 5
dl 0
loc 8
rs 10
c 3
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProjetNormandie\ArticleBundle\Doctrine;
6
7
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
8
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
9
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
10
use ApiPlatform\Metadata\Operation;
11
use Doctrine\ORM\QueryBuilder;
12
use ProjetNormandie\ArticleBundle\Entity\Article;
13
use Symfony\Component\HttpFoundation\RequestStack;
14
15
/**
16
 * Doctrine ORM extension that automatically joins and preloads Article translations.
17
 * This extension optimizes database queries by eagerly loading translation data
18
 * and preparing both requested and fallback locales for efficient retrieval.
19
 */
20
final class TranslationExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
21
{
22
    private RequestStack $requestStack;
23
    private string $defaultLocale;
24
25
    public function __construct(RequestStack $requestStack, string $defaultLocale = 'en')
26
    {
27
        $this->requestStack = $requestStack;
28
        $this->defaultLocale = $defaultLocale;
29
    }
30
31
    /**
32
     * Applies translation joins to collection queries.
33
     * Ensures all Article collections have their translations preloaded.
34
     */
35
    public function applyToCollection(
36
        QueryBuilder $queryBuilder,
37
        QueryNameGeneratorInterface $queryNameGenerator,
38
        string $resourceClass,
39
        ?Operation $operation = null,
40
        array $context = []
41
    ): void {
42
        $this->addWhere($queryBuilder, $resourceClass);
43
    }
44
45
    /**
46
     * Applies translation joins to single item queries.
47
     * Ensures individual Article entities have their translations preloaded.
48
     */
49
    public function applyToItem(
50
        QueryBuilder $queryBuilder,
51
        QueryNameGeneratorInterface $queryNameGenerator,
52
        string $resourceClass,
53
        array $identifiers,
54
        ?Operation $operation = null,
55
        array $context = []
56
    ): void {
57
        $this->addWhere($queryBuilder, $resourceClass);
58
    }
59
60
    /**
61
     * Adds translation joins and selects to the query builder.
62
     * Optimizes database access by preloading translation data and
63
     * specifically preparing requested and default locale translations.
64
     */
65
    private function addWhere(QueryBuilder $queryBuilder, string $resourceClass): void
66
    {
67
        if ($resourceClass !== Article::class) {
68
            return;
69
        }
70
71
        $request = $this->requestStack->getCurrentRequest();
72
        $requestedLocale = $request ? $request->getLocale() : $this->defaultLocale;
73
74
        $rootAlias = $queryBuilder->getRootAliases()[0];
75
76
        // Join all available translations
77
        $queryBuilder->leftJoin($rootAlias . '.translations', 't')
78
            ->addSelect('t');
79
80
        // Optional: you can also preload specifically the requested translation
81
        // and default translation for optimal performance
82
        $queryBuilder
83
            ->leftJoin(
84
                $rootAlias . '.translations',
85
                't_requested',
86
                'WITH',
87
                't_requested.locale = :requestedLocale'
88
            )
89
            ->leftJoin(
90
                $rootAlias . '.translations',
91
                't_default',
92
                'WITH',
93
                't_default.locale = :defaultLocale'
94
            )
95
            ->addSelect('t_requested')
96
            ->addSelect('t_default')
97
            ->setParameter('requestedLocale', $requestedLocale)
98
            ->setParameter('defaultLocale', $this->defaultLocale);
99
    }
100
}
101