Passed
Push — develop ( 94d0ee...15e467 )
by BENARD
02:27
created

Article::getPublishedAt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProjetNormandie\ArticleBundle\Entity;
6
7
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
8
use ApiPlatform\Metadata\ApiFilter;
9
use ApiPlatform\Metadata\ApiResource;
10
use ApiPlatform\Metadata\GetCollection;
11
use ApiPlatform\Metadata\Get;
12
use Doctrine\Common\Collections\ArrayCollection;
13
use Doctrine\Common\Collections\Collection;
14
use Doctrine\ORM\Mapping as ORM;
15
use Gedmo\Timestampable\Traits\TimestampableEntity;
16
use ProjetNormandie\ArticleBundle\Repository\ArticleRepository;
17
use ProjetNormandie\ArticleBundle\ValueObject\ArticleStatus;
18
use Symfony\Component\Serializer\Attribute\Groups;
19
use Symfony\Component\Validator\Constraints as Assert;
20
use DateTime;
21
22
#[ORM\Table(name:'pna_article')]
23
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
24
#[ORM\EntityListeners(["ProjetNormandie\ArticleBundle\EventListener\Entity\ArticleListener"])]
25
#[ApiResource(
26
    operations: [
27
        new GetCollection(),
28
        new Get(),
29
    ],
30
    normalizationContext: ['groups' => ['article:read', 'article:author', 'user:read']],
31
    order: ['publishedAt' => 'DESC']
32
)]
33
#[ApiFilter(
34
    SearchFilter::class,
35
    properties: [
36
        'status' => 'exact',
37
    ]
38
)]
39
class Article
40
{
41
    use TimestampableEntity;
42
43
    // FALLBACK language
44
    private const string DEFAULT_LOCALE = 'en';
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 44 at column 25
Loading history...
45
46
    #[Groups(['article:read'])]
47
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
48
    private ?int $id = null;
49
50
    #[ORM\Column(length: 30, nullable: false)]
51
    private string $status = ArticleStatus::UNDER_CONSTRUCTION;
52
53
    #[Groups(['article:read'])]
54
    #[ORM\Column(nullable: false, options: ['default' => 0])]
55
    private int $nbComment = 0;
56
57
    #[Groups(['article:author'])]
58
    #[ORM\ManyToOne(targetEntity: UserInterface::class, fetch: 'EAGER')]
59
    #[ORM\JoinColumn(name:'author_id', referencedColumnName:'id', nullable:false)]
60
    private $author;
61
62
    #[Groups(['article:read'])]
63
    #[ORM\Column(nullable: true)]
64
    private ?DateTime $publishedAt = null;
65
66
    /**
67
     * @var Collection<Comment>
68
     */
69
    #[ORM\OneToMany(mappedBy: 'article', targetEntity: Comment::class)]
70
    private Collection $comments;
71
72
    /** @var Collection<ArticleTranslation> */
73
    #[ORM\OneToMany(
74
        mappedBy: 'translatable',
75
        targetEntity: ArticleTranslation::class,
76
        cascade: ['persist', 'remove'],
77
        orphanRemoval: true,
78
        indexBy: 'locale'
79
    )]
80
    private Collection $translations;
81
82
    #[Groups(['article:read'])]
83
    #[ORM\Column(length: 255, unique: false)]
84
    private string $slug;
85
86
    private ?string $currentLocale = null;
87
88
    public function __construct()
89
    {
90
        $this->comments = new ArrayCollection();
91
        $this->translations = new ArrayCollection();
92
    }
93
94
    public function __toString()
95
    {
96
        return sprintf('%s [%s]', $this->getDefaultTitle(), $this->id);
97
    }
98
99
    public function getDefaultTitle(): string
100
    {
101
        return $this->getTitle(self::DEFAULT_LOCALE) ?: 'Untitled';
102
    }
103
104
    public function getDefaultContent(): string
105
    {
106
        return $this->getContent(self::DEFAULT_LOCALE) ?: '';
107
    }
108
109
    public function setId(int $id): void
110
    {
111
        $this->id = $id;
112
    }
113
114
    public function getId(): ?int
115
    {
116
        return $this->id;
117
    }
118
119
    public function setStatus(string $status): void
120
    {
121
        $this->status = $status;
122
    }
123
124
    public function getStatus(): string
125
    {
126
        return $this->status;
127
    }
128
129
    public function getArticleStatus(): ArticleStatus
130
    {
131
        return new ArticleStatus($this->status);
132
    }
133
134
    public function setNbComment(int $nbComment): void
135
    {
136
        $this->nbComment = $nbComment;
137
    }
138
139
    public function getNbComment(): int
140
    {
141
        return $this->nbComment;
142
    }
143
144
    public function getAuthor()
145
    {
146
        return $this->author;
147
    }
148
149
    public function setAuthor($author): void
150
    {
151
        $this->author = $author;
152
    }
153
154
    public function getPublishedAt(): ?DateTime
155
    {
156
        return $this->publishedAt;
157
    }
158
159
    public function setPublishedAt(?DateTime $publishedAt = null): void
160
    {
161
        $this->publishedAt = $publishedAt;
162
    }
163
164
    public function setComments(Collection $comments): void
165
    {
166
        $this->comments = $comments;
167
    }
168
169
    public function getComments(): Collection
170
    {
171
        return $this->comments;
172
    }
173
174
    public function getSlug(): ?string
175
    {
176
        return $this->slug;
177
    }
178
179
    public function setSlug(string $slug): void
180
    {
181
        $this->slug = $slug;
182
    }
183
184
    // Translation methods for A2lix compatibility
185
    public function getTranslations(): Collection
186
    {
187
        return $this->translations;
188
    }
189
190
    public function setTranslations(Collection $translations): self
191
    {
192
        $this->translations = $translations;
193
        return $this;
194
    }
195
196
    public function addTranslation(ArticleTranslation $translation): self
197
    {
198
        if (!$this->translations->contains($translation)) {
199
            $translation->setTranslatable($this);
200
            $this->translations->set($translation->getLocale(), $translation);
201
        }
202
203
        return $this;
204
    }
205
206
    public function removeTranslation(ArticleTranslation $translation): self
207
    {
208
        if ($this->translations->removeElement($translation)) {
209
            $translation->setTranslatable(null);
210
        }
211
212
        return $this;
213
    }
214
215
    /**
216
     * Retrieves a translation with intelligent fallback logic.
217
     * Ensures content quality by checking for non-empty translations.
218
     */
219
    public function translate(?string $locale = null, bool $fallbackToDefault = true): ?ArticleTranslation
220
    {
221
        $locale = $locale ?: $this->currentLocale ?: self::DEFAULT_LOCALE;
222
223
        // If translation exists for requested locale
224
        if ($this->translations->containsKey($locale)) {
225
            $translation = $this->translations->get($locale);
226
            // Check that translation is not empty
227
            if (!empty($translation->getTitle()) || !empty($translation->getContent())) {
228
                return $translation;
229
            }
230
        }
231
232
        // Fallback to default locale if enabled and different from requested locale
233
        if ($fallbackToDefault && $locale !== self::DEFAULT_LOCALE && $this->translations->containsKey(self::DEFAULT_LOCALE)) {
234
            $translation = $this->translations->get(self::DEFAULT_LOCALE);
235
            if (!empty($translation->getTitle()) || !empty($translation->getContent())) {
236
                return $translation;
237
            }
238
        }
239
240
        // If no valid translation exists, return first non-empty available
241
        foreach ($this->translations as $translation) {
242
            if (!empty($translation->getTitle()) || !empty($translation->getContent())) {
243
                return $translation;
244
            }
245
        }
246
247
        // Last resort: return first translation even if empty
248
        return $this->translations->first() ?: null;
249
    }
250
251
    /**
252
     * Vérifie si une traduction existe pour une locale donnée
253
     */
254
    public function hasTranslation(string $locale): bool
255
    {
256
        return $this->translations->containsKey($locale);
257
    }
258
259
    /**
260
     * Retourne toutes les locales disponibles pour cet article
261
     */
262
    public function getAvailableLocales(): array
263
    {
264
        return $this->translations->getKeys();
265
    }
266
267
    public function setCurrentLocale(string $locale): void
268
    {
269
        $this->currentLocale = $locale;
270
    }
271
272
    public function getCurrentLocale(): ?string
273
    {
274
        return $this->currentLocale;
275
    }
276
277
    public function setTitle(string $title, ?string $locale = null): void
278
    {
279
        $locale = $locale ?: $this->currentLocale ?: self::DEFAULT_LOCALE;
280
281
        if (!$this->translations->containsKey($locale)) {
282
            $translation = new ArticleTranslation();
283
            $translation->setTranslatable($this);
284
            $translation->setLocale($locale);
285
            $this->translations->set($locale, $translation);
286
        }
287
288
        $this->translations->get($locale)->setTitle($title);
289
    }
290
291
    #[Groups(['article:read'])]
292
    public function getTitle(?string $locale = null): ?string
293
    {
294
        $translation = $this->translate($locale);
295
        return $translation ? $translation->getTitle() : null;
296
    }
297
298
    public function setContent(string $content, ?string $locale = null): void
299
    {
300
        $locale = $locale ?: $this->currentLocale ?: self::DEFAULT_LOCALE;
301
302
        if (!$this->translations->containsKey($locale)) {
303
            $translation = new ArticleTranslation();
304
            $translation->setTranslatable($this);
305
            $translation->setLocale($locale);
306
            $this->translations->set($locale, $translation);
307
        }
308
309
        $this->translations->get($locale)->setContent($content);
310
    }
311
312
    #[Groups(['article:read'])]
313
    public function getContent(?string $locale = null): ?string
314
    {
315
        $translation = $this->translate($locale);
316
        return $translation?->getContent();
317
    }
318
319
    // Old methods for backward compatibility
320
    public function mergeNewTranslations(): void
321
    {
322
        // Not needed anymore
323
    }
324
}
325