Passed
Pull Request — master (#59)
by
unknown
02:22
created

TranslatableTrait::addTranslation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
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
12
/**
13
 * @see TranslatableInterface
14
 *
15
 * @author Gonzalo Vilaseca <[email protected]>
16
 * @template T of TranslationInterface
17
 */
18
trait TranslatableTrait
19
{
20
    /**
21
     * Protected to allow access in classes using this Trait or extending provided AbstractTranslatable
22
     * @var Collection<string, TranslationInterface>
23
     * @psalm-var Collection<string, T>
24
     */
25
    protected Collection $translations;
26
27
    /**
28
     * @var array|TranslationInterface[]
29
     * @psalm-var array<string, T>
30
     */
31
    protected array $translationsCache = [];
32
    protected ?string $currentLocale = null;
33
    protected ?string $fallbackLocale = null;
34
35
    /**
36
     * @codeCoverageIgnore
37
     */
38
    public function __construct()
39
    {
40
        $this->translations = new ArrayCollection();
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     *
46
     * @return TranslationInterface
47
     * @psalm-return T
48
     *
49
     * @throws \RuntimeException
50
     */
51
    public function getTranslation(?string $locale = null): TranslationInterface
52
    {
53
        $locale = $locale ?: $this->currentLocale;
54
        if (null === $locale) {
55
            throw new \RuntimeException('No locale has been set and current locale is undefined.');
56
        }
57
58
        $translation = $this->matchTranslation($locale);
59
        if (null !== $translation) {
60
            return $translation;
61
        }
62
63
        if ($locale !== $this->fallbackLocale && null !== $this->fallbackLocale) {
64
            $fallbackTranslation = $this->matchTranslation($this->fallbackLocale);
65
            if (null !== $fallbackTranslation) {
66
                return $fallbackTranslation;
67
            }
68
        }
69
70
        $translation = $this->createTranslation();
71
        $translation->setLocale($locale);
72
73
        $this->addTranslation($translation);
74
75
        $this->translationsCache[$locale] = $translation;
76
77
        return $translation;
78
    }
79
80
    /**
81
     * @param string $locale
82
     * @return TranslationInterface|null
83
     * @psalm-return T|null
84
     */
85
    private function matchTranslation(string $locale): ?TranslationInterface
86
    {
87
        if (isset($this->translationsCache[$locale])) {
88
            return $this->translationsCache[$locale];
89
        }
90
91
        $expr = new Comparison('locale', '=', $locale);
92
        $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

92
        $translation = $this->translations->/** @scrutinizer ignore-call */ matching(new Criteria($expr))->first();
Loading history...
93
94
        if ($translation instanceof TranslationInterface) {
95
            $this->translationsCache[$locale] = $translation;
96
97
            return $translation;
98
        }
99
100
        return null;
101
    }
102
103
    /**
104
     * @return string[]
105
     */
106
    public function getTranslationLocales(): array
107
    {
108
        $locales = [];
109
110
        foreach ($this->getTranslations() as $translation) {
111
            $locales[] = $translation->getLocale();
112
        }
113
114
        return $locales;
115
    }
116
117
    /**
118
     * @param string $locale
119
     */
120
    public function removeTranslationWithLocale(string $locale): void
121
    {
122
        $translations = $this->getTranslations();
123
124
        foreach ($translations as $translation) {
125
            if ($translation->getLocale() === $locale) {
126
                $this->removeTranslation($translation);
127
            }
128
        }
129
    }
130
131
    /**
132
     * @return Collection<string, TranslationInterface>
133
     * @psalm-return Collection<string, T>
134
     */
135
    public function getTranslations(): Collection
136
    {
137
        return $this->translations;
138
    }
139
140
    /**
141
     * @param TranslationInterface $translation
142
     * @psalm-param T $translation
143
     * @return bool
144
     */
145
    public function hasTranslation(TranslationInterface $translation): bool
146
    {
147
        return isset($this->translationsCache[$translation->getLocale()])
148
               || $this->translations->containsKey($translation->getLocale());
149
    }
150
151
    /**
152
     * @param TranslationInterface $translation
153
     * @psalm-param T $translation
154
     * @return void
155
     */
156
    public function addTranslation(TranslationInterface $translation): void
157
    {
158
        if (!$this->hasTranslation($translation)) {
159
            $this->translationsCache[$translation->getLocale()] = $translation;
160
161
            $this->translations->set($translation->getLocale(), $translation);
162
            $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

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