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

MediaGalleryValueObserver::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\MediaGalleryValueObserver
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\StoreViewCodes;
25
use TechDivision\Import\Utils\BackendTypeKeys;
26
use TechDivision\Import\Observers\StateDetectorInterface;
27
use TechDivision\Import\Observers\AttributeLoaderInterface;
28
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
29
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
30
use TechDivision\Import\Product\Media\Utils\ColumnKeys;
31
use TechDivision\Import\Product\Media\Utils\MemberNames;
32
use TechDivision\Import\Product\Media\Utils\EntityTypeCodes;
33
use TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface;
34
use TechDivision\Import\Observers\EntityMergers\EntityMergerInterface;
35
36
/**
37
 * Observer that creates/updates the product's media gallery value information.
38
 *
39
 * @author    Tim Wagner <[email protected]>
40
 * @copyright 2016 TechDivision GmbH <[email protected]>
41
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
42
 * @link      https://github.com/techdivision/import-product-media
43
 * @link      http://www.techdivision.com
44
 */
45
class MediaGalleryValueObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface
46
{
47
48
    /**
49
     * The product media processor instance.
50
     *
51
     * @var \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface
52
     */
53
    protected $productMediaProcessor;
54
55
    /**
56
     * The attribute loader instance.
57
     *
58
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
59
     */
60
    protected $attributeLoader;
61
62
    /**
63
     * The entity merger instance.
64
     *
65
     * @var \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface
66
     */
67
    protected $entityMerger;
68
69
    /**
70
     * Initialize the "dymanmic" columns.
71
     *
72
     * @var array
73
     */
74
    protected $columns = array(
75
        MemberNames::DISABLED => array(ColumnKeys::HIDE_FROM_PRODUCT_PAGE, BackendTypeKeys::BACKEND_TYPE_INT)
76
    );
77
78
    /**
79
     * Initialize the observer with the passed product media processor instance.
80
     *
81
     * @param \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface $productMediaProcessor The product media processor instance
82
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface|null               $attributeLoader       The attribute loader instance
83
     * @param \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface|null    $entityMerger          The entity merger instance
84
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                 $stateDetector         The state detector instance to use
85
     */
86 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...
87
        ProductMediaProcessorInterface $productMediaProcessor,
88
        AttributeLoaderInterface $attributeLoader = null,
89
        EntityMergerInterface $entityMerger = null,
90
        StateDetectorInterface $stateDetector = null
91
    ) {
92
93
        // initialize the media processor and the dynamic attribute loader instance
94
        $this->productMediaProcessor = $productMediaProcessor;
95
        $this->attributeLoader = $attributeLoader;
96
        $this->entityMerger = $entityMerger;
97
98
        // pass the state detector to the parent method
99
        parent::__construct($stateDetector);
100
    }
101
102
    /**
103
     * Return's the product media processor instance.
104
     *
105
     * @return \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface The product media processor instance
106
     */
107
    protected function getProductMediaProcessor()
108
    {
109
        return $this->productMediaProcessor;
110
    }
111
112
    /**
113
     * Process the observer's business logic.
114
     *
115
     * @return array The processed row
116
     */
117
    protected function process()
118
    {
119
120
        // initialize and persist the product media gallery value
121
        if ($this->hasChanges($productMediaGalleryValue = $this->initializeProductMediaGalleryValue($this->prepareDynamicAttributes()))) {
122
            $this->persistProductMediaGalleryValue($productMediaGalleryValue);
123
        }
124
    }
125
126
    /**
127
     * Merge's and return's the entity with the passed attributes and set's the
128
     * passed status.
129
     *
130
     * @param array       $entity        The entity to merge the attributes into
131
     * @param array       $attr          The attributes to be merged
132
     * @param string|null $changeSetName The change set name to use
133
     *
134
     * @return array The merged entity
135
     * @todo https://github.com/techdivision/import/issues/179
136
     */
137 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...
138
    {
139
        return array_merge(
140
            $entity,
141
            $this->entityMerger ? $this->entityMerger->merge($this, $entity, $attr) : $attr,
142
            array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName))
143
        );
144
    }
145
146
    /**
147
     * Appends the dynamic to the static attributes for the media type
148
     * gallery attributes and returns them.
149
     *
150
     * @return array The array with all available attributes
151
     */
152
    protected function prepareDynamicAttributes()
153
    {
154
        return array_merge($this->prepareAttributes(), $this->attributeLoader ? $this->attributeLoader->load($this, $this->columns) : array());
155
    }
156
157
    /**
158
     * Prepare the product media gallery value that has to be persisted.
159
     *
160
     * @return array The prepared product media gallery value attributes
161
     */
162
    protected function prepareAttributes()
163
    {
164
165
        try {
166
            // try to load the product SKU and map it the entity ID
167
            $parentId = $this->getValue(ColumnKeys::IMAGE_PARENT_SKU, null, array($this, 'mapParentSku'));
168
        } catch (\Exception $e) {
169
            throw $this->wrapException(array(ColumnKeys::IMAGE_PARENT_SKU), $e);
0 ignored issues
show
Documentation introduced by
array(\TechDivision\Impo...Keys::IMAGE_PARENT_SKU) is of type array<integer,?>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
170
        }
171
172
        // load the store ID
173
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
174
175
        // load the value ID
176
        $valueId = $this->getParentValueId();
177
178
        // load the image label
179
        $imageLabel = $this->getValue(ColumnKeys::IMAGE_LABEL);
180
181
        // load the position
182
        $position = (int) $this->getValue(ColumnKeys::IMAGE_POSITION, 0);
183
184
        // prepare the media gallery value
185
        return $this->initializeEntity(
186
            $this->loadRawEntity(
187
                array(
188
                    MemberNames::VALUE_ID    => $valueId,
189
                    MemberNames::STORE_ID    => $storeId,
190
                    MemberNames::ENTITY_ID   => $parentId,
191
                    MemberNames::LABEL       => $imageLabel,
192
                    MemberNames::POSITION    => $position
193
                )
194
            )
195
        );
196
    }
197
198
    /**
199
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
200
     *
201
     * @param array $data An array with data that will be used to initialize the raw entity with
202
     *
203
     * @return array The initialized entity
204
     */
205
    protected function loadRawEntity(array $data = array())
206
    {
207
        return $this->getProductMediaProcessor()->loadRawEntity(EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY_VALUE, $data);
208
    }
209
210
    /**
211
     * Initialize the product media gallery value with the passed attributes and returns an instance.
212
     *
213
     * @param array $attr The product media gallery value attributes
214
     *
215
     * @return array The initialized product media gallery value
216
     */
217
    protected function initializeProductMediaGalleryValue(array $attr)
218
    {
219
        return $attr;
220
    }
221
222
    /**
223
     * Return's the store ID of the actual row, or of the default store
224
     * if no store view code is set in the CSV file.
225
     *
226
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
227
     *
228
     * @return integer The ID of the actual store
229
     * @throws \Exception Is thrown, if the store with the actual code is not available
230
     */
231
    protected function getRowStoreId($default = null)
232
    {
233
        return $this->getSubject()->getRowStoreId($default);
0 ignored issues
show
Bug introduced by
The method getRowStoreId() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getRow()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
234
    }
235
236
    /**
237
     * Map's the passed SKU of the parent product to it's PK.
238
     *
239
     * @param string $parentSku The SKU of the parent product
240
     *
241
     * @return integer The primary key used to create relations
242
     */
243
    protected function mapParentSku($parentSku)
244
    {
245
        return $this->mapSkuToEntityId($parentSku);
246
    }
247
248
    /**
249
     * Return the entity ID for the passed SKU.
250
     *
251
     * @param string $sku The SKU to return the entity ID for
252
     *
253
     * @return integer The mapped entity ID
254
     * @throws \Exception Is thrown if the SKU is not mapped yet
255
     */
256
    protected function mapSkuToEntityId($sku)
257
    {
258
        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...
259
    }
260
261
    /**
262
     * Return's the value ID of the created media gallery entry.
263
     *
264
     * @return integer The ID of the created media gallery entry
265
     */
266
    protected function getParentValueId()
267
    {
268
        return $this->getSubject()->getParentValueId();
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 getParentValueId() 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...
269
    }
270
271
    /**
272
     * Return's the store for the passed store code.
273
     *
274
     * @param string $storeCode The store code to return the store for
275
     *
276
     * @return array The requested store
277
     * @throws \Exception Is thrown, if the requested store is not available
278
     */
279
    protected function getStoreByStoreCode($storeCode)
280
    {
281
        return $this->getSubject()->getStoreByStoreCode($storeCode);
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 getStoreByStoreCode() 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...
282
    }
283
284
    /**
285
     * Returns the acutal value of the position counter and raise's it by one.
286
     *
287
     * @return integer The actual value of the position counter
288
     * @deprecated Since 23.0.0
289
     */
290
    protected function raisePositionCounter()
291
    {
292
        return $this->getSubject()->raisePositionCounter();
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 raisePositionCounter() 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...
293
    }
294
295
    /**
296
     * Persist's the passed product media gallery value data.
297
     *
298
     * @param array $productMediaGalleryValue The product media gallery value data to persist
299
     *
300
     * @return void
301
     */
302
    protected function persistProductMediaGalleryValue($productMediaGalleryValue)
303
    {
304
        $this->getProductMediaProcessor()->persistProductMediaGalleryValue($productMediaGalleryValue);
305
    }
306
}
307