1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* TechDivision\Import\Product\Media\Observers\ProductMediaObserver |
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\Product\Media\Utils\ColumnKeys; |
24
|
|
|
use TechDivision\Import\Product\Observers\AbstractProductImportObserver; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Observer that extracts theproduct's media data from a CSV file to be added to media specifi CSV file. |
28
|
|
|
* |
29
|
|
|
* @author Tim Wagner <[email protected]> |
30
|
|
|
* @copyright 2016 TechDivision GmbH <[email protected]> |
31
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
32
|
|
|
* @link https://github.com/techdivision/import-product-media |
33
|
|
|
* @link http://www.techdivision.com |
34
|
|
|
*/ |
35
|
|
|
class ProductMediaObserver extends AbstractProductImportObserver |
36
|
|
|
{ |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* The artefact type. |
40
|
|
|
* |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
const ARTEFACT_TYPE = 'media'; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* The the default image label. |
47
|
|
|
* |
48
|
|
|
* @var string |
49
|
|
|
*/ |
50
|
|
|
const DEFAULT_IMAGE_LABEL = 'Image'; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* The default image position. |
54
|
|
|
* |
55
|
|
|
* @var int |
56
|
|
|
*/ |
57
|
|
|
const DEFAULT_IMAGE_POSITION = 0; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* The array with the image information of on row before they'll be converted into artefacts. |
61
|
|
|
* |
62
|
|
|
* @var array |
63
|
|
|
*/ |
64
|
|
|
protected $images = array(); |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* The image artefacts that has to be exported. |
68
|
|
|
* |
69
|
|
|
* @var array |
70
|
|
|
*/ |
71
|
|
|
protected $artefacts = array(); |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* The array with names of the images that should be hidden on the product detail page. |
75
|
|
|
* |
76
|
|
|
* @var array |
77
|
|
|
*/ |
78
|
|
|
protected $imagesToHide = array(); |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* The array with names of the images that has to be disabled. |
82
|
|
|
* |
83
|
|
|
* @var array |
84
|
|
|
*/ |
85
|
|
|
protected $disabledImages = array(); |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Holds the image values of the main row. |
89
|
|
|
* |
90
|
|
|
* @var array |
91
|
|
|
*/ |
92
|
|
|
protected $mainRow = array(); |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Process the observer's business logic. |
96
|
|
|
* |
97
|
|
|
* @return array The processed row |
98
|
|
|
*/ |
99
|
|
|
protected function process() |
100
|
|
|
{ |
101
|
|
|
|
102
|
|
|
// reset the values of the parent row, if the SKU changes |
103
|
|
|
if ($this->isLastSku($this->getValue(ColumnKeys::SKU)) === false) { |
104
|
|
|
$this->mainRow = array(); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
// initialize the arrays for the images, the artefacts, |
108
|
|
|
// the hidden as well as the disabled images |
109
|
|
|
$this->images = array(); |
110
|
|
|
$this->artefacts = array(); |
111
|
|
|
$this->imagesToHide = array(); |
112
|
|
|
$this->disabledImages = array(); |
113
|
|
|
|
114
|
|
|
// load the images that has to be hidden on product |
115
|
|
|
// detail page or completely disabled |
116
|
|
|
$this->loadImagesToHide(); |
117
|
|
|
$this->loadImagesToDisable(); |
118
|
|
|
|
119
|
|
|
// process the images/additional images |
120
|
|
|
$this->processImages(); |
121
|
|
|
$this->processAdditionalImages(); |
122
|
|
|
|
123
|
|
|
// append the artefacts that has to be exported to the subject |
124
|
|
|
$this->addArtefacts($this->artefacts); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Resolve's the value with the passed colum name from the actual row. If a callback will |
129
|
|
|
* be passed, the callback will be invoked with the found value as parameter. If |
130
|
|
|
* the value is NULL or empty, the default value will be returned. |
131
|
|
|
* |
132
|
|
|
* @param string $name The name of the column to return the value for |
133
|
|
|
* @param mixed|null $default The default value, that has to be returned, if the row's value is empty |
134
|
|
|
* @param callable|null $callback The callback that has to be invoked on the value, e. g. to format it |
135
|
|
|
* |
136
|
|
|
* @return mixed|null The, almost formatted, value |
137
|
|
|
* @see \TechDivision\Import\Observers\AbstractObserver::getValue() |
138
|
|
|
*/ |
139
|
|
|
protected function getImageValue($name, $default = null, callable $callback = null) |
140
|
|
|
{ |
141
|
|
|
|
142
|
|
|
// query whether or not the a image value is available, return it if yes |
143
|
|
|
if ($this->hasValue($name) && $this->isLastSku($this->getValue(ColumnKeys::SKU)) === false) { |
144
|
|
|
return $this->mainRow[$name] = $this->getValue($name, $default, $callback); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// try to load it from the parent rows |
148
|
|
|
if (isset($this->mainRow[$name])) { |
149
|
|
|
return $this->mainRow[$name]; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Parses the column and exports the image data to a separate file. |
155
|
|
|
* |
156
|
|
|
* @return void |
157
|
|
|
*/ |
158
|
|
|
protected function processImages() |
159
|
|
|
{ |
160
|
|
|
|
161
|
|
|
// load the store view code |
162
|
|
|
$storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE); |
163
|
|
|
$attributeSetCode = $this->getValue(ColumnKeys::ATTRIBUTE_SET_CODE); |
164
|
|
|
|
165
|
|
|
// load the parent SKU from the row |
166
|
|
|
$parentSku = $this->getValue(ColumnKeys::SKU); |
167
|
|
|
|
168
|
|
|
// load the media roles |
169
|
|
|
$mediaRoles = $this->getMediaRoles(); |
170
|
|
|
|
171
|
|
|
// iterate over the available image fields |
172
|
|
|
foreach ($mediaRoles as $mediaAttrColumnNames) { |
173
|
|
|
// query whether or not the column contains an image name |
174
|
|
|
if ($image = $this->getImageValue($imageColumnName = $mediaAttrColumnNames[ColumnKeys::IMAGE_PATH])) { |
175
|
|
|
// load the original image path and query whether or not an image with the name already exists |
176
|
|
|
if (isset($this->artefacts[$imagePath = $this->getInversedImageMapping($image)])) { |
177
|
|
|
continue; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
// initialize the label text |
181
|
|
|
$labelText = null; |
182
|
|
|
// query whether or not a custom label text has been passed |
183
|
|
|
if ($this->hasValue($labelColumnName = $mediaAttrColumnNames[ColumnKeys::IMAGE_LABEL])) { |
184
|
|
|
$labelText = $this->getValue($mediaAttrColumnNames[ColumnKeys::IMAGE_LABEL]); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
// initialize the default image position |
188
|
|
|
$position = null; |
189
|
|
|
// query and retrieve optional image position |
190
|
|
|
if ($this->hasValue($mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION])) { |
191
|
|
|
$position = $this->getValue($mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION]); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
// prepare the new base image |
195
|
|
|
$artefact = $this->newArtefact( |
196
|
|
|
array( |
197
|
|
|
ColumnKeys::STORE_VIEW_CODE => $storeViewCode, |
198
|
|
|
ColumnKeys::ATTRIBUTE_SET_CODE => $attributeSetCode, |
199
|
|
|
ColumnKeys::IMAGE_PARENT_SKU => $parentSku, |
200
|
|
|
ColumnKeys::IMAGE_PATH => $imagePath, |
201
|
|
|
ColumnKeys::IMAGE_PATH_NEW => $image, |
202
|
|
|
ColumnKeys::HIDE_FROM_PRODUCT_PAGE => in_array($image, $this->imagesToHide) ? 1 : 0, |
203
|
|
|
ColumnKeys::MEDIA_TYPE => 'image', |
204
|
|
|
ColumnKeys::IMAGE_LABEL => $labelText, |
205
|
|
|
ColumnKeys::IMAGE_POSITION => $position, |
206
|
|
|
ColumnKeys::IMAGE_DISABLED => in_array($image, $this->disabledImages) ? 1 : 0, |
207
|
|
|
), |
208
|
|
|
array( |
209
|
|
|
ColumnKeys::STORE_VIEW_CODE => ColumnKeys::STORE_VIEW_CODE, |
210
|
|
|
ColumnKeys::ATTRIBUTE_SET_CODE => ColumnKeys::ATTRIBUTE_SET_CODE, |
211
|
|
|
ColumnKeys::IMAGE_PARENT_SKU => ColumnKeys::SKU, |
212
|
|
|
ColumnKeys::IMAGE_PATH => $imageColumnName, |
213
|
|
|
ColumnKeys::IMAGE_PATH_NEW => $imageColumnName, |
214
|
|
|
ColumnKeys::HIDE_FROM_PRODUCT_PAGE => ColumnKeys::HIDE_FROM_PRODUCT_PAGE, |
215
|
|
|
ColumnKeys::MEDIA_TYPE => null, |
216
|
|
|
ColumnKeys::IMAGE_LABEL => $labelColumnName, |
217
|
|
|
ColumnKeys::IMAGE_POSITION => $mediaAttrColumnNames[ColumnKeys::IMAGE_POSITION], |
218
|
|
|
ColumnKeys::IMAGE_DISABLED => ColumnKeys::DISABLED_IMAGES |
219
|
|
|
) |
220
|
|
|
); |
221
|
|
|
|
222
|
|
|
// append the base image to the artefacts |
223
|
|
|
$this->artefacts[$imagePath] = $artefact; |
224
|
|
|
} |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Parses the column and exports the additional image data to a separate file. |
230
|
|
|
* |
231
|
|
|
* @return void |
232
|
|
|
*/ |
233
|
|
|
protected function processAdditionalImages() |
234
|
|
|
{ |
235
|
|
|
|
236
|
|
|
// load the store view code |
237
|
|
|
$storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE); |
238
|
|
|
$attributeSetCode = $this->getValue(ColumnKeys::ATTRIBUTE_SET_CODE); |
239
|
|
|
|
240
|
|
|
// load the parent SKU from the row |
241
|
|
|
$parentSku = $this->getValue(ColumnKeys::SKU); |
242
|
|
|
|
243
|
|
|
// query whether or not, we've additional images |
244
|
|
|
if ($additionalImages = $this->getImageValue(ColumnKeys::ADDITIONAL_IMAGES, null, array($this, 'explode'))) { |
245
|
|
|
// expand the additional image labels, if available |
246
|
|
|
$additionalImageLabels = $this->getValue(ColumnKeys::ADDITIONAL_IMAGE_LABELS, array(), array($this, 'explode')); |
247
|
|
|
// retrieve the additional image positions |
248
|
|
|
$additionalImagePositions = $this->getValue(ColumnKeys::ADDITIONAL_IMAGE_POSITIONS, array(), array($this, 'explode')); |
249
|
|
|
|
250
|
|
|
// initialize the images with the found values |
251
|
|
|
foreach ($additionalImages as $key => $additionalImage) { |
252
|
|
|
// load the original image path and query whether or not an image with the name already exists |
253
|
|
|
if (isset($this->artefacts[$imagePath = $this->getInversedImageMapping($additionalImage)])) { |
254
|
|
|
continue; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
// prepare the additional image |
258
|
|
|
$artefact = $this->newArtefact( |
259
|
|
|
array( |
260
|
|
|
ColumnKeys::STORE_VIEW_CODE => $storeViewCode, |
261
|
|
|
ColumnKeys::ATTRIBUTE_SET_CODE => $attributeSetCode, |
262
|
|
|
ColumnKeys::IMAGE_PARENT_SKU => $parentSku, |
263
|
|
|
ColumnKeys::IMAGE_PATH => $imagePath, |
264
|
|
|
ColumnKeys::IMAGE_PATH_NEW => $additionalImage, |
265
|
|
|
ColumnKeys::HIDE_FROM_PRODUCT_PAGE => in_array($additionalImage, $this->imagesToHide) ? 1 : 0, |
266
|
|
|
ColumnKeys::MEDIA_TYPE => 'image', |
267
|
|
|
ColumnKeys::IMAGE_LABEL => isset($additionalImageLabels[$key]) ? $additionalImageLabels[$key] : null, |
268
|
|
|
ColumnKeys::IMAGE_POSITION => isset($additionalImagePositions[$key]) ? $additionalImagePositions[$key] : null, |
269
|
|
|
ColumnKeys::IMAGE_DISABLED => in_array($additionalImage, $this->disabledImages) ? 1 : 0 |
270
|
|
|
), |
271
|
|
|
array( |
272
|
|
|
ColumnKeys::STORE_VIEW_CODE => ColumnKeys::STORE_VIEW_CODE, |
273
|
|
|
ColumnKeys::ATTRIBUTE_SET_CODE => ColumnKeys::ATTRIBUTE_SET_CODE, |
274
|
|
|
ColumnKeys::IMAGE_PARENT_SKU => ColumnKeys::SKU, |
275
|
|
|
ColumnKeys::IMAGE_PATH => ColumnKeys::ADDITIONAL_IMAGES, |
276
|
|
|
ColumnKeys::IMAGE_PATH_NEW => ColumnKeys::ADDITIONAL_IMAGES, |
277
|
|
|
ColumnKeys::HIDE_FROM_PRODUCT_PAGE => ColumnKeys::HIDE_FROM_PRODUCT_PAGE, |
278
|
|
|
ColumnKeys::MEDIA_TYPE => null, |
279
|
|
|
ColumnKeys::IMAGE_LABEL => ColumnKeys::ADDITIONAL_IMAGE_LABELS, |
280
|
|
|
ColumnKeys::IMAGE_POSITION => ColumnKeys::ADDITIONAL_IMAGE_POSITIONS, |
281
|
|
|
ColumnKeys::IMAGE_DISABLED => ColumnKeys::DISABLED_IMAGES |
282
|
|
|
) |
283
|
|
|
); |
284
|
|
|
|
285
|
|
|
// append the additional image to the artefacts |
286
|
|
|
$this->artefacts[$imagePath] = $artefact; |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Load the images that has to be hidden on the product detail page. |
293
|
|
|
* |
294
|
|
|
* @return void |
295
|
|
|
*/ |
296
|
|
View Code Duplication |
protected function loadImagesToHide() |
|
|
|
|
297
|
|
|
{ |
298
|
|
|
|
299
|
|
|
// load the array with the images that has to be hidden |
300
|
|
|
$hideFromProductPage = $this->getValue(ColumnKeys::HIDE_FROM_PRODUCT_PAGE, array(), array($this, 'explode')); |
301
|
|
|
|
302
|
|
|
// map the image names, because probably they have been renamed by the upload functionality |
303
|
|
|
foreach ($hideFromProductPage as $filename) { |
304
|
|
|
$this->imagesToHide[] = $this->getImageMapping($filename); |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Load the images that has to be disabled |
310
|
|
|
* |
311
|
|
|
* @return void |
312
|
|
|
*/ |
313
|
|
View Code Duplication |
protected function loadImagesToDisable() |
|
|
|
|
314
|
|
|
{ |
315
|
|
|
|
316
|
|
|
// load the array with the images that has to be disabled |
317
|
|
|
$disabledImages = $this->getValue(ColumnKeys::DISABLED_IMAGES, array(), array($this, 'explode')); |
318
|
|
|
|
319
|
|
|
// map the image names, because probably they have been renamed by the upload functionality |
320
|
|
|
foreach ($disabledImages as $filename) { |
321
|
|
|
$this->disabledImages[] = $this->getImageMapping($filename); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Return's the array with the available image types and their label columns. |
327
|
|
|
* |
328
|
|
|
* @return array The array with the available image types |
329
|
|
|
*/ |
330
|
|
|
protected function getImageTypes() |
331
|
|
|
{ |
332
|
|
|
return $this->getSubject()->getImageTypes(); |
|
|
|
|
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Return's the default image label. |
337
|
|
|
* |
338
|
|
|
* @return string|null The default image label |
339
|
|
|
* @deprecated Since 23.0.0 |
340
|
|
|
*/ |
341
|
|
|
protected function getDefaultImageLabel() |
342
|
|
|
{ |
343
|
|
|
return ProductMediaObserver::DEFAULT_IMAGE_LABEL; |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Returns the default image position. |
348
|
|
|
* |
349
|
|
|
* @return int The default image position |
350
|
|
|
* @deprecated Since 23.0.0 |
351
|
|
|
*/ |
352
|
|
|
protected function getDefaultImagePosition() |
353
|
|
|
{ |
354
|
|
|
return ProductMediaObserver::DEFAULT_IMAGE_POSITION; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* Returns the mapped filename (which is the new filename). |
359
|
|
|
* |
360
|
|
|
* @param string $filename The filename to map |
361
|
|
|
* |
362
|
|
|
* @return string The mapped filename |
363
|
|
|
*/ |
364
|
|
|
protected function getImageMapping($filename) |
365
|
|
|
{ |
366
|
|
|
return $this->getSubject()->getImageMapping($filename); |
|
|
|
|
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Returns the original filename for passed one (which is the new filename). |
371
|
|
|
* |
372
|
|
|
* @param string $newFilename The new filename to return the original one for |
373
|
|
|
* |
374
|
|
|
* @return string The original filename |
375
|
|
|
*/ |
376
|
|
|
protected function getInversedImageMapping($newFilename) |
377
|
|
|
{ |
378
|
|
|
return $this->getSubject()->getInversedImageMapping($newFilename); |
|
|
|
|
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
/** |
382
|
|
|
* Returns the media roles of the subject. |
383
|
|
|
* |
384
|
|
|
* @return mixed |
385
|
|
|
*/ |
386
|
|
|
protected function getMediaRoles() |
387
|
|
|
{ |
388
|
|
|
return $this->getSubject()->getMediaRoles(); |
|
|
|
|
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Create's and return's a new empty artefact entity. |
393
|
|
|
* |
394
|
|
|
* @param array $columns The array with the column data |
395
|
|
|
* @param array $originalColumnNames The array with a mapping from the old to the new column names |
396
|
|
|
* |
397
|
|
|
* @return array The new artefact entity |
398
|
|
|
*/ |
399
|
|
|
protected function newArtefact(array $columns, array $originalColumnNames) |
400
|
|
|
{ |
401
|
|
|
return $this->getSubject()->newArtefact($columns, $originalColumnNames); |
|
|
|
|
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Add the passed product type artefacts to the product with the |
406
|
|
|
* last entity ID. |
407
|
|
|
* |
408
|
|
|
* @param array $artefacts The product type artefacts |
409
|
|
|
* |
410
|
|
|
* @return void |
411
|
|
|
* @uses \TechDivision\Import\Product\Media\Subjects\MediaSubject::getLastEntityId() |
412
|
|
|
*/ |
413
|
|
|
protected function addArtefacts(array $artefacts) |
414
|
|
|
{ |
415
|
|
|
$this->getSubject()->addArtefacts(ProductMediaObserver::ARTEFACT_TYPE, $artefacts, false); |
|
|
|
|
416
|
|
|
} |
417
|
|
|
} |
418
|
|
|
|
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.