Completed
Push — 23.x ( b390a3...481f59 )
by Tim
01:55
created

MediaGalleryObserver::mergeEntity()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8

Duplication

Lines 8
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 8
loc 8
ccs 0
cts 8
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 3
crap 6
1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Media\Observers\MediaGalleryObserver
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import-product-media
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Media\Observers;
22
23
use TechDivision\Import\Utils\EntityStatus;
24
use TechDivision\Import\Utils\BackendTypeKeys;
25
use TechDivision\Import\Product\Media\Utils\ColumnKeys;
26
use TechDivision\Import\Product\Media\Utils\MemberNames;
27
use TechDivision\Import\Product\Media\Utils\EntityTypeCodes;
28
use TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface;
29
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
30
use TechDivision\Import\Observers\StateDetectorInterface;
31
use TechDivision\Import\Observers\AttributeLoaderInterface;
32
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
33
use TechDivision\Import\Observers\EntityMergers\EntityMergerInterface;
34
35
/**
36
 * Observer that creates/updates the product's media gallery information.
37
 *
38
 * @author    Tim Wagner <[email protected]>
39
 * @copyright 2016 TechDivision GmbH <[email protected]>
40
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
41
 * @link      https://github.com/techdivision/import-product-media
42
 * @link      http://www.techdivision.com
43
 */
44
class MediaGalleryObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface
45
{
46
47
    /**
48
     * The media gallery attribute code.
49
     *
50
     * @var string
51
     */
52
    const ATTRIBUTE_CODE = 'media_gallery';
53
54
    /**
55
     * The ID of the parent product the media is related to.
56
     *
57
     * @var integer
58
     */
59
    protected $parentId;
60
61
    /**
62
     * The ID of the persisted media gallery entity.
63
     *
64
     * @var integer
65
     */
66
    protected $valueId;
67
68
    /**
69
     * The product media processor instance.
70
     *
71
     * @var \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface
72
     */
73
    protected $productMediaProcessor;
74
75
    /**
76
     * The attribute loader instance.
77
     *
78
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
79
     */
80
    protected $attributeLoader;
81
82
    /**
83
     * The entity merger instance.
84
     *
85
     * @var \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface
86
     */
87
    protected $entityMerger;
88
89
    /**
90
     * Initialize the "dymanmic" columns.
91
     *
92
     * @var array
93
     */
94
    protected $columns = array(
95
        EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY_VALUE_TO_ENTITY => array(),
96
        EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY => array(
97
            MemberNames::DISABLED => array(ColumnKeys::IMAGE_DISABLED, BackendTypeKeys::BACKEND_TYPE_INT),
98
            MemberNames::MEDIA_TYPE => array(ColumnKeys::MEDIA_TYPE, BackendTypeKeys::BACKEND_TYPE_VARCHAR)
99
        )
100
    );
101
102
    /**
103
     * Initialize the observer with the passed product media processor instance.
104
     *
105
     * @param \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface $productMediaProcessor The product media processor instance
106
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface|null               $attributeLoader       The attribute loader instance
107
     * @param \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface|null    $entityMerger          The entity merger instance
108
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                 $stateDetector         The state detector instance to use
109
     */
110 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
        ProductMediaProcessorInterface $productMediaProcessor,
112
        AttributeLoaderInterface $attributeLoader = null,
113
        EntityMergerInterface $entityMerger = null,
114
        StateDetectorInterface $stateDetector = null
115
    ) {
116
117
        // initialize the media processor, the dynamic attribute loader and entity merger instance
118
        $this->productMediaProcessor = $productMediaProcessor;
119
        $this->attributeLoader = $attributeLoader;
120
        $this->entityMerger = $entityMerger;
121
122
        // pass the state detector to the parent method
123
        parent::__construct($stateDetector);
124
    }
125
126
    /**
127
     * Return's the product media processor instance.
128
     *
129
     * @return \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface The product media processor instance
130
     */
131
    protected function getProductMediaProcessor()
132
    {
133
        return $this->productMediaProcessor;
134
    }
135
136
    /**
137
     * Process the observer's business logic.
138
     *
139
     * @return array The processed row
140
     */
141
    protected function process()
142
    {
143
144
        // try to load the product SKU and map it the entity ID and
145
        $this->parentId = $this->getValue(ColumnKeys::IMAGE_PARENT_SKU, null, array($this, 'mapParentSku'));
146
147
        // prepare the actual store view code
148
        $this->prepareStoreViewCode($this->getRow());
0 ignored issues
show
Unused Code introduced by
The call to MediaGalleryObserver::prepareStoreViewCode() has too many arguments starting with $this->getRow().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
149
150
        // initialize and persist the product media gallery
151
        $attr = $this->prepareDynamicMediaGalleryAttributes(EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY, $this->prepareProductMediaGalleryAttributes());
152
        if ($this->hasChanges($productMediaGallery = $this->initializeProductMediaGallery($attr))) {
153
            // persist the media gallery data and temporarily persist value ID
154
            $this->setParentValueId($this->valueId = $this->persistProductMediaGallery($productMediaGallery));
0 ignored issues
show
Documentation Bug introduced by
The property $valueId was declared of type integer, but $this->persistProductMed...y($productMediaGallery) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
155
            // persist the product media gallery to entity data
156
            $attr = $this->prepareDynamicMediaGalleryAttributes(EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY_VALUE_TO_ENTITY, $this->prepareProductMediaGalleryValueToEntityAttributes());
157
            if ($productMediaGalleryValueToEntity = $this->initializeProductMediaGalleryValueToEntity($attr)) {
158
                $this->persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity);
159
            }
160
        }
161
162
        // temporarily persist parent ID
163
        $this->setParentId($this->parentId);
164
    }
165
166
    /**
167
     * Merge's and return's the entity with the passed attributes and set's the
168
     * passed status.
169
     *
170
     * @param array       $entity        The entity to merge the attributes into
171
     * @param array       $attr          The attributes to be merged
172
     * @param string|null $changeSetName The change set name to use
173
     *
174
     * @return array The merged entity
175
     * @todo https://github.com/techdivision/import/issues/179
176
     */
177 View Code Duplication
    protected function mergeEntity(array $entity, array $attr, $changeSetName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
    {
179
        return array_merge(
180
            $entity,
181
            $this->entityMerger ? $this->entityMerger->merge($this, $entity, $attr) : $attr,
182
            array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName))
183
        );
184
    }
185
186
    /**
187
     * Appends the dynamic attributes to the static ones and returns them.
188
     *
189
     * @param string $entityTypeCode   The entity type code load to append the dynamic attributes for
190
     * @param array  $staticAttributes The array with the static attributes to append the dynamic to
191
     *
192
     * @return array The array with all available attributes
193
     */
194
    protected function prepareDynamicMediaGalleryAttributes(string $entityTypeCode, array $staticAttributes) : array
195
    {
196
        return array_merge(
197
            $staticAttributes,
198
            $this->attributeLoader ? $this->attributeLoader->load($this, $this->columns[$entityTypeCode]) : array()
199
        );
200
    }
201
202
    /**
203
     * Prepare the product media gallery that has to be persisted.
204
     *
205
     * @return array The prepared product media gallery attributes
206
     */
207
    protected function prepareProductMediaGalleryAttributes()
208
    {
209
210
        // load the attribute ID of the media gallery EAV attribute
211
        $mediaGalleryAttribute = $this->getEavAttributeByAttributeCode(MediaGalleryObserver::ATTRIBUTE_CODE);
212
        $attributeId = $mediaGalleryAttribute[MemberNames::ATTRIBUTE_ID];
213
214
        // initialize the gallery data
215
        $image = $this->getValue(ColumnKeys::IMAGE_PATH_NEW);
216
217
        // initialize and return the entity
218
        return $this->initializeEntity(
219
            $this->loadRawEntity(
220
                EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY,
221
                array(
222
                    MemberNames::ATTRIBUTE_ID => $attributeId,
223
                    MemberNames::VALUE        => $image
224
                )
225
            )
226
        );
227
    }
228
229
    /**
230
     * Prepare the product media gallery value to entity that has to be persisted.
231
     *
232
     * @return array The prepared product media gallery value to entity attributes
233
     */
234
    protected function prepareProductMediaGalleryValueToEntityAttributes()
235
    {
236
237
        // initialize and return the entity
238
        return $this->initializeEntity(
239
            $this->loadRawEntity(
240
                EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY_VALUE_TO_ENTITY,
241
                array(
242
                    MemberNames::VALUE_ID  => $this->valueId,
243
                    MemberNames::ENTITY_ID => $this->parentId
244
                )
245
            )
246
        );
247
    }
248
249
    /**
250
     * Load's and return's a raw entity without primary key but the mandatory members only and nulled values.
251
     *
252
     * @param string $entityTypeCode The entity type code to return the raw entity for
253
     * @param array  $data           An array with data that will be used to initialize the raw entity with
254
     *
255
     * @return array The initialized entity
256
     */
257
    protected function loadRawEntity($entityTypeCode, array $data = array())
258
    {
259
        return $this->getProductMediaProcessor()->loadRawEntity($entityTypeCode, $data);
260
    }
261
262
    /**
263
     * Initialize the product media gallery with the passed attributes and returns an instance.
264
     *
265
     * @param array $attr The product media gallery attributes
266
     *
267
     * @return array The initialized product media gallery
268
     */
269
    protected function initializeProductMediaGallery(array $attr)
270
    {
271
        return $attr;
272
    }
273
274
    /**
275
     * Initialize the product media gallery value to entity with the passed attributes and returns an instance.
276
     *
277
     * @param array $attr The product media gallery value to entity attributes
278
     *
279
     * @return array|null The initialized product media gallery value to entity, or NULL if the product media gallery value to entity already exists
280
     */
281
    protected function initializeProductMediaGalleryValueToEntity(array $attr)
282
    {
283
        return $attr;
284
    }
285
286
    /**
287
     * Map's the passed SKU of the parent product to it's PK.
288
     *
289
     * @param string $parentSku The SKU of the parent product
290
     *
291
     * @return integer The primary key used to create relations
292
     */
293
    protected function mapParentSku($parentSku)
294
    {
295
        return $this->mapSkuToEntityId($parentSku);
296
    }
297
298
    /**
299
     * Set's the value ID of the created media gallery entry.
300
     *
301
     * @param integer $parentValueId The ID of the created media gallery entry
302
     *
303
     * @return void
304
     */
305
    protected function setParentValueId($parentValueId)
306
    {
307
        $this->getSubject()->setParentValueId($parentValueId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setParentValueId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

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...
308
    }
309
310
    /**
311
     * Return the entity ID for the passed SKU.
312
     *
313
     * @param string $sku The SKU to return the entity ID for
314
     *
315
     * @return integer The mapped entity ID
316
     * @throws \Exception Is thrown if the SKU is not mapped yet
317
     */
318
    protected function mapSkuToEntityId($sku)
319
    {
320
        return $this->getSubject()->mapSkuToEntityId($sku);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method mapSkuToEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject.

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...
321
    }
322
323
    /**
324
     * Set's the ID of the parent product to relate the variant with.
325
     *
326
     * @param integer $parentId The ID of the parent product
327
     *
328
     * @return void
329
     */
330
    protected function setParentId($parentId)
331
    {
332
        $this->getSubject()->setParentId($parentId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

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...
333
    }
334
335
    /**
336
     * Return's the ID of the parent product to relate the variant with.
337
     *
338
     * @return integer The ID of the parent product
339
     */
340
    protected function getParentId()
341
    {
342
        return $this->getSubject()->getParentId();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getParentId() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

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...
343
    }
344
345
    /**
346
     * Query whether or not this is the parent ID.
347
     *
348
     * @param integer $parentId The PK of the parent image
349
     *
350
     * @return boolean TRUE if the PK euqals, else FALSE
351
     */
352
    protected function isParentId($parentId)
353
    {
354
        return $this->getParentId() === $parentId;
355
    }
356
357
    /**
358
     * Query whether or not this is the parent store view code.
359
     *
360
     * @param string $storeViewCode The actual store view code
361
     *
362
     * @return boolean TRUE if the store view code equals, else FALSE
363
     */
364
    protected function isParentStoreViewCode($storeViewCode)
365
    {
366
        return $this->getStoreViewCode() === $storeViewCode;
367
    }
368
369
    /**
370
     * Return's the default store view code.
371
     *
372
     * @return array The default store view code
373
     */
374
    protected function getDefaultStoreViewCode()
375
    {
376
        return $this->getSubject()->getDefaultStoreViewCode();
377
    }
378
379
    /**
380
     * Reset the position counter to 1.
381
     *
382
     * @return void
383
     * @deprecated Since 23.0.0
384
     */
385
    protected function resetPositionCounter()
386
    {
387
        $this->getSubject()->resetPositionCounter();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method resetPositionCounter() does only exist in the following implementations of said interface: TechDivision\Import\Prod...a\Subjects\MediaSubject.

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...
388
    }
389
390
    /**
391
     * Return's the EAV attribute with the passed attribute code.
392
     *
393
     * @param string $attributeCode The attribute code
394
     *
395
     * @return array The array with the EAV attribute
396
     * @throws \Exception Is thrown if the attribute with the passed code is not available
397
     */
398
    protected function getEavAttributeByAttributeCode($attributeCode)
399
    {
400
        return $this->getSubject()->getEavAttributeByAttributeCode($attributeCode);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getEavAttributeByAttributeCode() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Prod...a\Subjects\MediaSubject, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Subjects\AbstractEavSubject, TechDivision\Import\Subjects\ValidatorSubject.

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...
401
    }
402
403
    /**
404
     * Persist's the passed product media gallery data and return's the ID.
405
     *
406
     * @param array $productMediaGallery The product media gallery data to persist
407
     *
408
     * @return string The ID of the persisted entity
409
     */
410
    protected function persistProductMediaGallery($productMediaGallery)
411
    {
412
        return $this->getProductMediaProcessor()->persistProductMediaGallery($productMediaGallery);
413
    }
414
415
    /**
416
     * Persist's the passed product media gallery value to entity data.
417
     *
418
     * @param array $productMediaGalleryValueToEntity The product media gallery value to entity data to persist
419
     *
420
     * @return void
421
     */
422
    protected function persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity)
423
    {
424
        $this->getProductMediaProcessor()->persistProductMediaGalleryValueToEntity($productMediaGalleryValueToEntity);
425
    }
426
}
427