TranslatableTrait   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 53
c 3
b 0
f 0
dl 0
loc 186
rs 10
wmc 30

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B getTranslation() 0 31 9
A setCurrentLocale() 0 3 1
A setFallbackLocale() 0 3 1
A hasTranslation() 0 6 3
A removeTranslation() 0 6 3
A matchTranslation() 0 16 3
A addTranslation() 0 7 3
A getTranslationLocales() 0 9 2
A removeTranslationWithLocale() 0 7 3
A getTranslations() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Locastic\ApiPlatformTranslationBundle\Model;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\Common\Collections\Criteria;
10
use Doctrine\Common\Collections\Expr\Comparison;
11
use Doctrine\Persistence\Proxy;
12
13
/**
14
 * @see TranslatableInterface
15
 *
16
 * @author Gonzalo Vilaseca <[email protected]>
17
 * @template T of TranslationInterface
18
 */
19
trait TranslatableTrait
20
{
21
    /**
22
     * Protected to allow access in classes using this Trait or extending provided AbstractTranslatable
23
     * @var Collection<string, TranslationInterface>
24
     * @psalm-var Collection<string, T>
25
     */
26
    protected Collection $translations;
27
28
    /**
29
     * @var array|TranslationInterface[]
30
     * @psalm-var array<string, T>
31
     */
32
    protected array $translationsCache = [];
33
    protected ?string $currentLocale = null;
34
    protected ?string $fallbackLocale = null;
35
36
    /**
37
     * @codeCoverageIgnore
38
     */
39
    public function __construct()
40
    {
41
        $this->translations = new ArrayCollection();
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     *
47
     * @return TranslationInterface
48
     * @psalm-return T
49
     *
50
     * @throws \RuntimeException
51
     */
52
    public function getTranslation(?string $locale = null): TranslationInterface
53
    {
54
        if ($this instanceof Proxy && !$this->__isInitialized()) {
55
            $this->__load();
56
        }
57
58
        $locale = $locale ?: $this->currentLocale;
59
        if (null === $locale) {
60
            throw new \RuntimeException('No locale has been set and current locale is undefined.');
61
        }
62
63
        $translation = $this->matchTranslation($locale);
64
        if (null !== $translation) {
65
            return $translation;
66
        }
67
68
        if ($locale !== $this->fallbackLocale && null !== $this->fallbackLocale) {
69
            $fallbackTranslation = $this->matchTranslation($this->fallbackLocale);
70
            if (null !== $fallbackTranslation) {
71
                return $fallbackTranslation;
72
            }
73
        }
74
75
        $translation = $this->createTranslation();
76
        $translation->setLocale($locale);
77
78
        $this->addTranslation($translation);
79
80
        $this->translationsCache[$locale] = $translation;
81
82
        return $translation;
83
    }
84
85
    /**
86
     * @param string $locale
87
     * @return TranslationInterface|null
88
     * @psalm-return T|null
89
     */
90
    private function matchTranslation(string $locale): ?TranslationInterface
91
    {
92
        if (isset($this->translationsCache[$locale])) {
93
            return $this->translationsCache[$locale];
94
        }
95
96
        $expr = new Comparison('locale', '=', $locale);
97
        $translation = $this->translations->matching(new Criteria($expr))->first();
0 ignored issues
show
Bug introduced by
The method matching() does not exist on Doctrine\Common\Collections\Collection. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Common\Collections\AbstractLazyCollection. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

97
        $translation = $this->translations->/** @scrutinizer ignore-call */ matching(new Criteria($expr))->first();
Loading history...
98
99
        if ($translation instanceof TranslationInterface) {
100
            $this->translationsCache[$locale] = $translation;
101
102
            return $translation;
103
        }
104
105
        return null;
106
    }
107
108
    /**
109
     * @return string[]
110
     */
111
    public function getTranslationLocales(): array
112
    {
113
        $locales = [];
114
115
        foreach ($this->getTranslations() as $translation) {
116
            $locales[] = $translation->getLocale();
117
        }
118
119
        return $locales;
120
    }
121
122
    /**
123
     * @param string $locale
124
     */
125
    public function removeTranslationWithLocale(string $locale): void
126
    {
127
        $translations = $this->getTranslations();
128
129
        foreach ($translations as $translation) {
130
            if ($translation->getLocale() === $locale) {
131
                $this->removeTranslation($translation);
132
            }
133
        }
134
    }
135
136
    /**
137
     * @return Collection<string, TranslationInterface>
138
     * @psalm-return Collection<string, T>
139
     */
140
    public function getTranslations(): Collection
141
    {
142
        return $this->translations;
143
    }
144
145
    /**
146
     * @param TranslationInterface $translation
147
     * @psalm-param T $translation
148
     * @return bool
149
     */
150
    public function hasTranslation(TranslationInterface $translation): bool
151
    {
152
        return null !== $translation->getLocale()
153
            && (
154
                isset($this->translationsCache[$translation->getLocale()])
155
                || $this->translations->containsKey($translation->getLocale())
156
            );
157
    }
158
159
    /**
160
     * @param TranslationInterface $translation
161
     * @psalm-param T $translation
162
     * @return void
163
     */
164
    public function addTranslation(TranslationInterface $translation): void
165
    {
166
        if (!$this->hasTranslation($translation) && null !== $translation->getLocale()) {
167
            $this->translationsCache[$translation->getLocale()] = $translation;
168
169
            $this->translations->set($translation->getLocale(), $translation);
170
            $translation->setTranslatable($this);
0 ignored issues
show
Bug introduced by
$this of type Locastic\ApiPlatformTran...Model\TranslatableTrait is incompatible with the type Locastic\ApiPlatformTran...nslatableInterface|null expected by parameter $translatable of Locastic\ApiPlatformTran...face::setTranslatable(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

170
            $translation->setTranslatable(/** @scrutinizer ignore-type */ $this);
Loading history...
171
        }
172
    }
173
174
    /**
175
     * @param TranslationInterface $translation
176
     * @psalm-param T $translation
177
     * @return void
178
     */
179
    public function removeTranslation(TranslationInterface $translation): void
180
    {
181
        if ($this->translations->removeElement($translation) && null !== $translation->getLocale()) {
182
            unset($this->translationsCache[$translation->getLocale()]);
183
184
            $translation->setTranslatable(null);
185
        }
186
    }
187
188
    public function setCurrentLocale(?string $currentLocale): void
189
    {
190
        $this->currentLocale = $currentLocale;
191
    }
192
193
    public function setFallbackLocale(?string $fallbackLocale): void
194
    {
195
        $this->fallbackLocale = $fallbackLocale;
196
    }
197
198
    /**
199
     * Create resource translation model.
200
     *
201
     * @return TranslationInterface $translation
202
     * @psalm-return T $translation
203
     */
204
    abstract protected function createTranslation(): TranslationInterface;
205
}
206