Completed
Push — master ( d81c19...f57266 )
by Kamil
20s
created

src/Sylius/Component/Product/Model/Product.php (3 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
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Component\Product\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Sylius\Component\Attribute\Model\AttributeValueInterface;
19
use Sylius\Component\Resource\Model\TimestampableTrait;
20
use Sylius\Component\Resource\Model\ToggleableTrait;
21
use Sylius\Component\Resource\Model\TranslatableTrait;
22
use Sylius\Component\Resource\Model\TranslationInterface;
23
use Webmozart\Assert\Assert;
24
25
class Product implements ProductInterface
26
{
27
    use TimestampableTrait, ToggleableTrait;
28
    use TranslatableTrait {
29
        __construct as private initializeTranslationsCollection;
30
        getTranslation as private doGetTranslation;
31
    }
32
33
    /**
34
     * @var mixed
35
     */
36
    protected $id;
37
38
    /**
39
     * @var string
40
     */
41
    protected $code;
42
43
    /**
44
     * @var Collection|AttributeValueInterface[]
45
     */
46
    protected $attributes;
47
48
    /**
49
     * @var Collection|ProductVariantInterface[]
50
     */
51
    protected $variants;
52
53
    /**
54
     * @var Collection|ProductOptionInterface[]
55
     */
56
    protected $options;
57
58
    /**
59
     * @var Collection|ProductAssociationInterface[]
60
     */
61
    protected $associations;
62
63
    public function __construct()
64
    {
65
        $this->initializeTranslationsCollection();
66
67
        $this->createdAt = new \DateTime();
68
        $this->attributes = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...tributeValueInterface>> of property $attributes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
69
        $this->associations = new ArrayCollection();
70
        $this->variants = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...oductVariantInterface>> of property $variants.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
71
        $this->options = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Doctrine\Common\Collections\ArrayCollection() of type object<Doctrine\Common\C...ctions\ArrayCollection> is incompatible with the declared type object<Doctrine\Common\C...roductOptionInterface>> of property $options.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72
    }
73
74
    /**
75
     * @return string
76
     */
77
    public function __toString(): string
78
    {
79
        return (string) $this->getName();
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85
    public function getId()
86
    {
87
        return $this->id;
88
    }
89
90
    /**
91
     * @return string
92
     */
93
    public function getCode(): ?string
94
    {
95
        return $this->code;
96
    }
97
98
    /**
99
     * @param string $code
100
     */
101
    public function setCode(?string $code): void
102
    {
103
        $this->code = $code;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function getName(): ?string
110
    {
111
        return $this->getTranslation()->getName();
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function setName(?string $name): void
118
    {
119
        $this->getTranslation()->setName($name);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    public function getSlug(): ?string
126
    {
127
        return $this->getTranslation()->getSlug();
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function setSlug(?string $slug): void
134
    {
135
        $this->getTranslation()->setSlug($slug);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function getDescription(): ?string
142
    {
143
        return $this->getTranslation()->getDescription();
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149
    public function setDescription(?string $description): void
150
    {
151
        $this->getTranslation()->setDescription($description);
152
    }
153
154
    /**
155
     * {@inheritdoc}
156
     */
157
    public function getMetaKeywords(): ?string
158
    {
159
        return $this->getTranslation()->getMetaKeywords();
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function setMetaKeywords(?string $metaKeywords): void
166
    {
167
        $this->getTranslation()->setMetaKeywords($metaKeywords);
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function getMetaDescription(): ?string
174
    {
175
        return $this->getTranslation()->getMetaDescription();
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function setMetaDescription(?string $metaDescription): void
182
    {
183
        $this->getTranslation()->setMetaDescription($metaDescription);
184
    }
185
186
    public function getAttributes(): Collection
187
    {
188
        return $this->attributes;
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function getAttributesByLocale(
195
        string $localeCode,
196
        string $fallbackLocaleCode,
197
        ?string $baseLocaleCode = null
198
    ): Collection {
199
        if (null === $baseLocaleCode || $baseLocaleCode === $fallbackLocaleCode) {
200
            $baseLocaleCode = $fallbackLocaleCode;
201
            $fallbackLocaleCode = null;
202
        }
203
204
        $attributes = $this->attributes->filter(
205
            function (ProductAttributeValueInterface $attribute) use ($baseLocaleCode) {
206
                return $attribute->getLocaleCode() === $baseLocaleCode;
207
            }
208
        );
209
210
        $attributesWithFallback = [];
211
        foreach ($attributes as $attribute) {
212
            $attributesWithFallback[] = $this->getAttributeInDifferentLocale($attribute, $localeCode, $fallbackLocaleCode);
213
        }
214
215
        return new ArrayCollection($attributesWithFallback);
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221
    public function addAttribute(?AttributeValueInterface $attribute): void
222
    {
223
        /** @var ProductAttributeValueInterface $attribute */
224
        Assert::isInstanceOf(
225
            $attribute,
226
            ProductAttributeValueInterface::class,
227
            'Attribute objects added to a Product object have to implement ProductAttributeValueInterface'
228
        );
229
230
        if (!$this->hasAttribute($attribute)) {
231
            $attribute->setProduct($this);
232
            $this->attributes->add($attribute);
233
        }
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function removeAttribute(?AttributeValueInterface $attribute): void
240
    {
241
        /** @var ProductAttributeValueInterface $attribute */
242
        Assert::isInstanceOf(
243
            $attribute,
244
            ProductAttributeValueInterface::class,
245
            'Attribute objects removed from a Product object have to implement ProductAttributeValueInterface'
246
        );
247
248
        if ($this->hasAttribute($attribute)) {
249
            $this->attributes->removeElement($attribute);
250
            $attribute->setProduct(null);
251
        }
252
    }
253
254
    /**
255
     * {@inheritdoc}
256
     */
257
    public function hasAttribute(AttributeValueInterface $attribute): bool
258
    {
259
        return $this->attributes->contains($attribute);
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265
    public function hasAttributeByCodeAndLocale(string $attributeCode, ?string $localeCode = null): bool
266
    {
267
        $localeCode = $localeCode ?: $this->getTranslation()->getLocale();
268
269
        foreach ($this->attributes as $attribute) {
270
            if ($attribute->getAttribute()->getCode() === $attributeCode
271
                && $attribute->getLocaleCode() === $localeCode) {
272
                return true;
273
            }
274
        }
275
276
        return false;
277
    }
278
279
    /**
280
     * {@inheritdoc}
281
     */
282
    public function getAttributeByCodeAndLocale(string $attributeCode, ?string $localeCode = null): ?AttributeValueInterface
283
    {
284
        if (null === $localeCode) {
285
            $localeCode = $this->getTranslation()->getLocale();
286
        }
287
288
        foreach ($this->attributes as $attribute) {
289
            if ($attribute->getAttribute()->getCode() === $attributeCode &&
290
                $attribute->getLocaleCode() === $localeCode) {
291
                return $attribute;
292
            }
293
        }
294
295
        return null;
296
    }
297
298
    /**
299
     * {@inheritdoc}
300
     */
301
    public function hasVariants(): bool
302
    {
303
        return !$this->getVariants()->isEmpty();
304
    }
305
306
    /**
307
     * {@inheritdoc}
308
     */
309
    public function getVariants(): Collection
310
    {
311
        return $this->variants;
312
    }
313
314
    /**
315
     * {@inheritdoc}
316
     */
317
    public function addVariant(ProductVariantInterface $variant): void
318
    {
319
        if (!$this->hasVariant($variant)) {
320
            $variant->setProduct($this);
321
            $this->variants->add($variant);
322
        }
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    public function removeVariant(ProductVariantInterface $variant): void
329
    {
330
        if ($this->hasVariant($variant)) {
331
            $variant->setProduct(null);
332
            $this->variants->removeElement($variant);
333
        }
334
    }
335
336
    /**
337
     * {@inheritdoc}
338
     */
339
    public function hasVariant(ProductVariantInterface $variant): bool
340
    {
341
        return $this->variants->contains($variant);
342
    }
343
344
    /**
345
     * {@inheritdoc}
346
     */
347
    public function hasOptions(): bool
348
    {
349
        return !$this->options->isEmpty();
350
    }
351
352
    /**
353
     * {@inheritdoc}
354
     */
355
    public function getOptions(): Collection
356
    {
357
        return $this->options;
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363
    public function addOption(ProductOptionInterface $option): void
364
    {
365
        if (!$this->hasOption($option)) {
366
            $this->options->add($option);
367
        }
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     */
373
    public function removeOption(ProductOptionInterface $option): void
374
    {
375
        if ($this->hasOption($option)) {
376
            $this->options->removeElement($option);
377
        }
378
    }
379
380
    /**
381
     * {@inheritdoc}
382
     */
383
    public function hasOption(ProductOptionInterface $option): bool
384
    {
385
        return $this->options->contains($option);
386
    }
387
388
    /**
389
     * {@inheritdoc}
390
     */
391
    public function getAssociations(): Collection
392
    {
393
        return $this->associations;
394
    }
395
396
    /**
397
     * {@inheritdoc}
398
     */
399
    public function addAssociation(ProductAssociationInterface $association): void
400
    {
401
        if (!$this->hasAssociation($association)) {
402
            $this->associations->add($association);
403
            $association->setOwner($this);
404
        }
405
    }
406
407
    /**
408
     * {@inheritdoc}
409
     */
410
    public function removeAssociation(ProductAssociationInterface $association): void
411
    {
412
        if ($this->hasAssociation($association)) {
413
            $association->setOwner(null);
414
            $this->associations->removeElement($association);
415
        }
416
    }
417
418
    /**
419
     * {@inheritdoc}
420
     */
421
    public function hasAssociation(ProductAssociationInterface $association): bool
422
    {
423
        return $this->associations->contains($association);
424
    }
425
426
    /**
427
     * {@inheritdoc}
428
     */
429
    public function isSimple(): bool
430
    {
431
        return 1 === $this->variants->count() && !$this->hasOptions();
432
    }
433
434
    /**
435
     * {@inheritdoc}
436
     */
437
    public function isConfigurable(): bool
438
    {
439
        return !$this->isSimple();
440
    }
441
442
    /**
443
     * @param string|null $locale
444
     *
445
     * @return ProductTranslationInterface
446
     */
447
    public function getTranslation(?string $locale = null): TranslationInterface
448
    {
449
        /** @var ProductTranslationInterface $translation */
450
        $translation = $this->doGetTranslation($locale);
451
452
        return $translation;
453
    }
454
455
    /**
456
     * {@inheritdoc}
457
     */
458
    protected function createTranslation(): ProductTranslationInterface
459
    {
460
        return new ProductTranslation();
461
    }
462
463
    /**
464
     * @param ProductAttributeValueInterface $attributeValue
465
     * @param string $localeCode
466
     * @param string|null $fallbackLocaleCode
467
     *
468
     * @return AttributeValueInterface
469
     */
470
    private function getAttributeInDifferentLocale(
471
        ProductAttributeValueInterface $attributeValue,
472
        string $localeCode,
473
        ?string $fallbackLocaleCode = null
474
    ): AttributeValueInterface {
475
        if (!$this->hasNotEmptyAttributeByCodeAndLocale($attributeValue->getCode(), $localeCode)) {
476
            if (
477
                null !== $fallbackLocaleCode &&
478
                $this->hasNotEmptyAttributeByCodeAndLocale($attributeValue->getCode(), $fallbackLocaleCode)
479
            ) {
480
                return $this->getAttributeByCodeAndLocale($attributeValue->getCode(), $fallbackLocaleCode);
481
            }
482
483
            return $attributeValue;
484
        }
485
486
        return $this->getAttributeByCodeAndLocale($attributeValue->getCode(), $localeCode);
487
    }
488
489
    /**
490
     * @param string $attributeCode
491
     * @param string $localeCode
492
     *
493
     * @return bool
494
     */
495
    private function hasNotEmptyAttributeByCodeAndLocale(string $attributeCode, string $localeCode): bool
496
    {
497
        $attributeValue = $this->getAttributeByCodeAndLocale($attributeCode, $localeCode);
498
        if (null === $attributeValue) {
499
            return false;
500
        }
501
502
        $value = $attributeValue->getValue();
503
        if ('' === $value || null === $value || [] === $value) {
504
            return false;
505
        }
506
507
        return true;
508
    }
509
}
510