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

src/Sylius/Component/Product/Model/Product.php (3 issues)

assigning incompatible types to properties.

Bug Documentation Major

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