Completed
Push — master ( 32d5c7...38a92b )
by Kamil
103:05 queued 77:43
created

Product::hasNotEmptyAttributeByCodeAndLocale()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 8
nc 3
nop 2
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 Webmozart\Assert\Assert;
23
24
/**
25
 * @author Paweł Jędrzejewski <[email protected]>
26
 * @author Gonzalo Vilaseca <[email protected]>
27
 */
28
class Product implements ProductInterface
29
{
30
    use TimestampableTrait, ToggleableTrait;
31
    use TranslatableTrait {
32
        __construct as private initializeTranslationsCollection;
33
    }
34
35
    /**
36
     * @var mixed
37
     */
38
    protected $id;
39
40
    /**
41
     * @var string
42
     */
43
    protected $code;
44
45
    /**
46
     * @var Collection|AttributeValueInterface[]
47
     */
48
    protected $attributes;
49
50
    /**
51
     * @var Collection|ProductVariantInterface[]
52
     */
53
    protected $variants;
54
55
    /**
56
     * @var Collection|ProductOptionInterface[]
57
     */
58
    protected $options;
59
60
    /**
61
     * @var Collection|ProductAssociationInterface[]
62
     */
63
    protected $associations;
64
65
    public function __construct()
66
    {
67
        $this->initializeTranslationsCollection();
68
69
        $this->createdAt = new \DateTime();
70
        $this->attributes = new ArrayCollection();
71
        $this->associations = new ArrayCollection();
72
        $this->variants = new ArrayCollection();
73
        $this->options = new ArrayCollection();
74
    }
75
76
    /**
77
     * @return string
78
     */
79
    public function __toString(): string
80
    {
81
        return (string) $this->getName();
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function getId()
88
    {
89
        return $this->id;
90
    }
91
92
    /**
93
     * @return string
94
     */
95
    public function getCode(): ?string
96
    {
97
        return $this->code;
98
    }
99
100
    /**
101
     * @param string $code
102
     */
103
    public function setCode(?string $code): void
104
    {
105
        $this->code = $code;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function getName(): ?string
112
    {
113
        return $this->getTranslation()->getName();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getName() does only exist in the following implementations of said interface: Sylius\Component\Attribu...el\AttributeTranslation, Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product...ociationTypeTranslation, Sylius\Component\Product...uctAttributeTranslation, Sylius\Component\Product...roductOptionTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Product...oductVariantTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function setName(?string $name): void
120
    {
121
        $this->getTranslation()->setName($name);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method setName() does only exist in the following implementations of said interface: Sylius\Component\Attribu...el\AttributeTranslation, Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product...ociationTypeTranslation, Sylius\Component\Product...uctAttributeTranslation, Sylius\Component\Product...roductOptionTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Product...oductVariantTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function getSlug(): ?string
128
    {
129
        return $this->getTranslation()->getSlug();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getSlug() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function setSlug(?string $slug): void
136
    {
137
        $this->getTranslation()->setSlug($slug);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method setSlug() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function getDescription(): ?string
144
    {
145
        return $this->getTranslation()->getDescription();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getDescription() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function setDescription(?string $description): void
152
    {
153
        $this->getTranslation()->setDescription($description);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method setDescription() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Payment...aymentMethodTranslation, Sylius\Component\Product\Model\ProductTranslation, Sylius\Component\Shippin...ippingMethodTranslation, Sylius\Component\Taxonomy\Model\TaxonTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function getMetaKeywords(): ?string
160
    {
161
        return $this->getTranslation()->getMetaKeywords();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getMetaKeywords() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
162
    }
163
164
    /**
165
     * {@inheritdoc}
166
     */
167
    public function setMetaKeywords(?string $metaKeywords): void
168
    {
169
        $this->getTranslation()->setMetaKeywords($metaKeywords);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method setMetaKeywords() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
170
    }
171
172
    /**
173
     * {@inheritdoc}
174
     */
175
    public function getMetaDescription(): ?string
176
    {
177
        return $this->getTranslation()->getMetaDescription();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method getMetaDescription() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
178
    }
179
180
    /**
181
     * {@inheritdoc}
182
     */
183
    public function setMetaDescription(?string $metaDescription): void
184
    {
185
        $this->getTranslation()->setMetaDescription($metaDescription);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Resourc...el\TranslationInterface as the method setMetaDescription() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\ProductTranslation, Sylius\Component\Product\Model\ProductTranslation.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
186
    }
187
188
    public function getAttributes(): Collection
189
    {
190
        return $this->attributes;
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196
    public function getAttributesByLocale(
197
        string $localeCode,
198
        string $fallbackLocaleCode,
199
        ?string $baseLocaleCode = null
200
    ): Collection {
201
        if (null === $baseLocaleCode || $baseLocaleCode === $fallbackLocaleCode) {
202
            $baseLocaleCode = $fallbackLocaleCode;
203
            $fallbackLocaleCode = null;
204
        }
205
206
        $attributes = $this->attributes->filter(
207
            function (ProductAttributeValueInterface $attribute) use ($baseLocaleCode) {
208
                return $attribute->getLocaleCode() === $baseLocaleCode;
209
            }
210
        );
211
212
        $attributesWithFallback = [];
213
        foreach ($attributes as $attribute) {
214
            $attributesWithFallback[] = $this->getAttributeInDifferentLocale($attribute, $localeCode, $fallbackLocaleCode);
215
        }
216
217
        return new ArrayCollection($attributesWithFallback);
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223
    public function addAttribute(?AttributeValueInterface $attribute): void
224
    {
225
        Assert::isInstanceOf(
226
            $attribute,
227
            ProductAttributeValueInterface::class,
228
            'Attribute objects added to a Product object have to implement ProductAttributeValueInterface'
229
        );
230
231
        if (!$this->hasAttribute($attribute)) {
232
            $attribute->setProduct($this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Attribu...AttributeValueInterface as the method setProduct() does only exist in the following implementations of said interface: Sylius\Component\Product...l\ProductAttributeValue.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
233
            $this->attributes->add($attribute);
234
        }
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function removeAttribute(?AttributeValueInterface $attribute): void
241
    {
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);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Attribu...AttributeValueInterface as the method setProduct() does only exist in the following implementations of said interface: Sylius\Component\Product...l\ProductAttributeValue.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
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
     * {@inheritdoc}
444
     */
445
    protected function createTranslation(): ProductTranslationInterface
446
    {
447
        return new ProductTranslation();
448
    }
449
450
    /**
451
     * @param ProductAttributeValueInterface $attributeValue
452
     * @param string $localeCode
453
     * @param string|null $fallbackLocaleCode
454
     *
455
     * @return AttributeValueInterface
456
     */
457
    private function getAttributeInDifferentLocale(
458
        ProductAttributeValueInterface $attributeValue,
459
        string $localeCode,
460
        ?string $fallbackLocaleCode = null
461
    ): AttributeValueInterface {
462
        if (!$this->hasNotEmptyAttributeByCodeAndLocale($attributeValue->getCode(), $localeCode)) {
463
            if (
464
                null !== $fallbackLocaleCode &&
465
                $this->hasNotEmptyAttributeByCodeAndLocale($attributeValue->getCode(), $fallbackLocaleCode))
466
            {
467
                return $this->getAttributeByCodeAndLocale($attributeValue->getCode(), $fallbackLocaleCode);
468
            }
469
470
            return $attributeValue;
471
        }
472
473
        return $this->getAttributeByCodeAndLocale($attributeValue->getCode(), $localeCode);
474
    }
475
476
    /**
477
     * @param string $attributeCode
478
     * @param string $localeCode
479
     *
480
     * @return bool
481
     */
482
    private function hasNotEmptyAttributeByCodeAndLocale(string $attributeCode, string $localeCode): bool
483
    {
484
        $attributeValue = $this->getAttributeByCodeAndLocale($attributeCode, $localeCode);
485
        if (null === $attributeValue) {
486
            return false;
487
        }
488
489
        $value = $attributeValue->getValue();
490
        if ('' === $value || null === $value || [] === $value) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !('' === $value |...|| array() === $value);.
Loading history...
491
            return false;
492
        }
493
494
        return true;
495
    }
496
}
497