Completed
Push — 23.x ( b390a3 )
by Tim
04:13
created

MediaGalleryValueObserver::hasValue()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 1
nop 1
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\StoreViewCodes;
24
use TechDivision\Import\Utils\BackendTypeKeys;
25
use TechDivision\Import\Observers\StateDetectorInterface;
26
use TechDivision\Import\Observers\AttributeLoaderInterface;
27
use TechDivision\Import\Observers\DynamicAttributeObserverInterface;
28
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
29
use TechDivision\Import\Product\Media\Utils\ColumnKeys;
30
use TechDivision\Import\Product\Media\Utils\MemberNames;
31
use TechDivision\Import\Product\Media\Utils\EntityTypeCodes;
32
use TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface;
33
34
/**
35
 * Observer that creates/updates the product's media gallery value information.
36
 *
37
 * @author    Tim Wagner <[email protected]>
38
 * @copyright 2016 TechDivision GmbH <[email protected]>
39
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
40
 * @link      https://github.com/techdivision/import-product-media
41
 * @link      http://www.techdivision.com
42
 */
43
class MediaGalleryValueObserver extends AbstractProductImportObserver implements DynamicAttributeObserverInterface
44
{
45
46
    /**
47
     * The product media processor instance.
48
     *
49
     * @var \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface
50
     */
51
    protected $productMediaProcessor;
52
53
    /**
54
     * The attribute loader instance.
55
     *
56
     * @var \TechDivision\Import\Observers\AttributeLoaderInterface
57
     */
58
    protected $attributeLoader;
59
60
    /**
61
     * Initialize the "dymanmic" columns.
62
     *
63
     * @var array
64
     */
65
    protected $columns = array(
66
        MemberNames::DISABLED => array(ColumnKeys::HIDE_FROM_PRODUCT_PAGE, BackendTypeKeys::BACKEND_TYPE_INT)
67
    );
68
69
    /**
70
     * Array with virtual column name mappings (this is a temporary
71
     * solution till techdivision/import#179 as been implemented).
72
     *
73
     * @var array
74
     * @todo https://github.com/techdivision/import/issues/179
75
     */
76
    protected $virtualMapping = array(
77
        MemberNames::LABEL    => ColumnKeys::IMAGE_LABEL,
78
        MemberNames::POSITION => ColumnKeys::IMAGE_POSITION,
79
        MemberNames::DISABLED => ColumnKeys::HIDE_FROM_PRODUCT_PAGE
80
    );
81
82
    /**
83
     * Initialize the observer with the passed product media processor instance.
84
     *
85
     * @param \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface $productMediaProcessor The product media processor instance
86
     * @param \TechDivision\Import\Observers\AttributeLoaderInterface                    $attributeLoader       The attribute loader instance
87
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null                 $stateDetector         The state detector instance to use
88
     */
89 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...
90
        ProductMediaProcessorInterface $productMediaProcessor,
91
        AttributeLoaderInterface $attributeLoader = null,
92
        StateDetectorInterface $stateDetector = null
93
    ) {
94
95
        // initialize the media processor and the dynamic attribute loader instance
96
        $this->productMediaProcessor = $productMediaProcessor;
97
        $this->attributeLoader = $attributeLoader;
98
99
        // pass the state detector to the parent method
100
        parent::__construct($stateDetector);
101
    }
102
103
    /**
104
     * Return's the product media processor instance.
105
     *
106
     * @return \TechDivision\Import\Product\Media\Services\ProductMediaProcessorInterface The product media processor instance
107
     */
108
    protected function getProductMediaProcessor()
109
    {
110
        return $this->productMediaProcessor;
111
    }
112
113
    /**
114
     * Query whether or not a value for the column with the passed name exists.
115
     *
116
     * @param string $name The column name to query for a valid value
117
     *
118
     * @return boolean TRUE if the value is set, else FALSE
119
     * @todo https://github.com/techdivision/import/issues/179
120
     */
121
    public function hasValue($name)
122
    {
123
        return parent::hasValue(isset($this->virtualMapping[$name]) ? $this->virtualMapping[$name] : $name);
124
    }
125
126
    /**
127
     * Process the observer's business logic.
128
     *
129
     * @return array The processed row
130
     */
131
    protected function process()
132
    {
133
134
        // initialize and persist the product media gallery value
135
        if ($this->hasChanges($productMediaGalleryValue = $this->initializeProductMediaGalleryValue($this->prepareDynamicAttributes()))) {
136
            $this->persistProductMediaGalleryValue($productMediaGalleryValue);
137
        }
138
    }
139
140
    /**
141
     * Appends the dynamic to the static attributes for the media type
142
     * gallery attributes and returns them.
143
     *
144
     * @return array The array with all available attributes
145
     */
146
    protected function prepareDynamicAttributes()
147
    {
148
        return array_merge($this->prepareAttributes(), $this->attributeLoader ? $this->attributeLoader->load($this, $this->columns) : array());
149
    }
150
151
    /**
152
     * Prepare the product media gallery value that has to be persisted.
153
     *
154
     * @return array The prepared product media gallery value attributes
155
     */
156
    protected function prepareAttributes()
157
    {
158
159
        try {
160
            // try to load the product SKU and map it the entity ID
161
            $parentId = $this->getValue(ColumnKeys::IMAGE_PARENT_SKU, null, array($this, 'mapParentSku'));
162
        } catch (\Exception $e) {
163
            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...
164
        }
165
166
        // load the store ID
167
        $storeId = $this->getRowStoreId(StoreViewCodes::ADMIN);
168
169
        // load the value ID
170
        $valueId = $this->getParentValueId();
171
172
        // load the image label
173
        $imageLabel = $this->getValue(ColumnKeys::IMAGE_LABEL);
174
175
        // load the position
176
        $position = (int) $this->getValue(ColumnKeys::IMAGE_POSITION, 0);
177
178
        // prepare the media gallery value
179
        return $this->initializeEntity(
180
            $this->loadRawEntity(
181
                array(
182
                    MemberNames::VALUE_ID    => $valueId,
183
                    MemberNames::STORE_ID    => $storeId,
184
                    MemberNames::ENTITY_ID   => $parentId,
185
                    MemberNames::LABEL       => $imageLabel,
186
                    MemberNames::POSITION    => $position
187
                )
188
            )
189
        );
190
    }
191
192
    /**
193
     * Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values.
194
     *
195
     * @param array $data An array with data that will be used to initialize the raw entity with
196
     *
197
     * @return array The initialized entity
198
     */
199
    protected function loadRawEntity(array $data = array())
200
    {
201
        return $this->getProductMediaProcessor()->loadRawEntity(EntityTypeCodes::CATALOG_PRODUCT_MEDIA_GALLERY_VALUE, $data);
202
    }
203
204
    /**
205
     * Initialize the product media gallery value with the passed attributes and returns an instance.
206
     *
207
     * @param array $attr The product media gallery value attributes
208
     *
209
     * @return array The initialized product media gallery value
210
     */
211
    protected function initializeProductMediaGalleryValue(array $attr)
212
    {
213
        return $attr;
214
    }
215
216
    /**
217
     * Return's the store ID of the actual row, or of the default store
218
     * if no store view code is set in the CSV file.
219
     *
220
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
221
     *
222
     * @return integer The ID of the actual store
223
     * @throws \Exception Is thrown, if the store with the actual code is not available
224
     */
225
    protected function getRowStoreId($default = null)
226
    {
227
        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...
228
    }
229
230
    /**
231
     * Map's the passed SKU of the parent product to it's PK.
232
     *
233
     * @param string $parentSku The SKU of the parent product
234
     *
235
     * @return integer The primary key used to create relations
236
     */
237
    protected function mapParentSku($parentSku)
238
    {
239
        return $this->mapSkuToEntityId($parentSku);
240
    }
241
242
    /**
243
     * Return the entity ID for the passed SKU.
244
     *
245
     * @param string $sku The SKU to return the entity ID for
246
     *
247
     * @return integer The mapped entity ID
248
     * @throws \Exception Is thrown if the SKU is not mapped yet
249
     */
250
    protected function mapSkuToEntityId($sku)
251
    {
252
        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...
253
    }
254
255
    /**
256
     * Return's the value ID of the created media gallery entry.
257
     *
258
     * @return integer The ID of the created media gallery entry
259
     */
260
    protected function getParentValueId()
261
    {
262
        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...
263
    }
264
265
    /**
266
     * Return's the store for the passed store code.
267
     *
268
     * @param string $storeCode The store code to return the store for
269
     *
270
     * @return array The requested store
271
     * @throws \Exception Is thrown, if the requested store is not available
272
     */
273
    protected function getStoreByStoreCode($storeCode)
274
    {
275
        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...
276
    }
277
278
    /**
279
     * Returns the acutal value of the position counter and raise's it by one.
280
     *
281
     * @return integer The actual value of the position counter
282
     * @deprecated Since 23.0.0
283
     */
284
    protected function raisePositionCounter()
285
    {
286
        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...
287
    }
288
289
    /**
290
     * Persist's the passed product media gallery value data.
291
     *
292
     * @param array $productMediaGalleryValue The product media gallery value data to persist
293
     *
294
     * @return void
295
     */
296
    protected function persistProductMediaGalleryValue($productMediaGalleryValue)
297
    {
298
        $this->getProductMediaProcessor()->persistProductMediaGalleryValue($productMediaGalleryValue);
299
    }
300
}
301