1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* TechDivision\Import\Attribute\Observers\CatalogAttributeObserver |
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-attribute |
18
|
|
|
* @link http://www.techdivision.com |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace TechDivision\Import\Attribute\Observers; |
22
|
|
|
|
23
|
|
|
use TechDivision\Import\Utils\EntityStatus; |
24
|
|
|
use TechDivision\Import\Loaders\LoaderInterface; |
25
|
|
|
use TechDivision\Import\Observers\StateDetectorInterface; |
26
|
|
|
use TechDivision\Import\Observers\EntityMergers\EntityMergerInterface; |
27
|
|
|
use TechDivision\Import\Attribute\Utils\ColumnKeys; |
28
|
|
|
use TechDivision\Import\Attribute\Utils\MemberNames; |
29
|
|
|
use TechDivision\Import\Attribute\Utils\EntityTypeCodes; |
30
|
|
|
use TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Observer that create's the EAV catalog attribute itself. |
34
|
|
|
* |
35
|
|
|
* @author Tim Wagner <[email protected]> |
36
|
|
|
* @copyright 2016 TechDivision GmbH <[email protected]> |
37
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
38
|
|
|
* @link https://github.com/techdivision/import-attribute |
39
|
|
|
* @link http://www.techdivision.com |
40
|
|
|
*/ |
41
|
|
|
class CatalogAttributeObserver extends AbstractAttributeImportObserver |
42
|
|
|
{ |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* The key for the additional data containing the swatch type. |
46
|
|
|
* |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
const SWATCH_INPUT_TYPE = 'swatch_input_type'; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* The available swatch types. |
53
|
|
|
* |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
protected $swatchTypes = array('text', 'visual', 'image'); |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* The attribute processor instance. |
60
|
|
|
* |
61
|
|
|
* @var \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface |
62
|
|
|
*/ |
63
|
|
|
protected $attributeBunchProcessor; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* The collection with entity merger instances. |
67
|
|
|
* |
68
|
|
|
* @var \Doctrine\Common\Collections\Collection |
69
|
|
|
*/ |
70
|
|
|
protected $entityMergers; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Array with virtual column name mappings (this is a temporary |
74
|
|
|
* solution till techdivision/import#179 as been implemented). |
75
|
|
|
* |
76
|
|
|
* @var array |
77
|
|
|
* @todo https://github.com/techdivision/import/issues/179 |
78
|
|
|
*/ |
79
|
|
|
protected $reverseHeaderMappings = array(); |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Initializes the observer with the passed subject instance. |
83
|
|
|
* |
84
|
|
|
* @param \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface $attributeBunchProcessor The attribute bunch processor instance |
85
|
|
|
* @param \TechDivision\Import\Observers\EntityMergers\EntityMergerInterface $entityMerger The entity merger instance |
86
|
|
|
* @param \TechDivision\Import\Loaders\LoaderInterface|null $headerMappingLoader The loader for the virtual mappings |
87
|
|
|
* @param \TechDivision\Import\Observers\StateDetectorInterface|null $stateDetector The state detector instance to use |
88
|
|
|
*/ |
89
|
3 |
|
public function __construct( |
90
|
|
|
AttributeBunchProcessorInterface $attributeBunchProcessor, |
91
|
|
|
EntityMergerInterface $entityMerger = null, |
92
|
|
|
LoaderInterface $headerMappingLoader = null, |
93
|
|
|
StateDetectorInterface $stateDetector = null |
94
|
|
|
) { |
95
|
|
|
|
96
|
|
|
// initialize the bunch processor and the entity merger instance |
97
|
3 |
|
$this->attributeBunchProcessor = $attributeBunchProcessor; |
98
|
3 |
|
$this->entityMerger = $entityMerger; |
|
|
|
|
99
|
|
|
|
100
|
|
|
// initialize the reverse header mappings table > CSV column name |
101
|
3 |
|
$this->reverseHeaderMappings = array_merge( |
102
|
3 |
|
$this->reverseHeaderMappings, |
103
|
3 |
|
$headerMappingLoader ? array_flip($headerMappingLoader->load()) : array() |
104
|
|
|
); |
105
|
|
|
|
106
|
|
|
// pass the state detector to the parent method |
107
|
3 |
|
parent::__construct($stateDetector); |
108
|
3 |
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Process the observer's business logic. |
112
|
|
|
* |
113
|
|
|
* @return void |
114
|
|
|
*/ |
115
|
3 |
|
protected function process() |
116
|
|
|
{ |
117
|
|
|
|
118
|
|
|
// query whether or not, we've found a new attribute code => means we've found a new attribute |
119
|
3 |
|
if ($this->hasBeenProcessed($this->getValue(ColumnKeys::ATTRIBUTE_CODE))) { |
120
|
|
|
return; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
// initialize and persist the EAV catalog attribute |
124
|
3 |
|
$this->persistCatalogAttribute($this->initializeAttribute($this->prepareAttributes())); |
125
|
3 |
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Merge's and return's the entity with the passed attributes and set's the |
129
|
|
|
* passed status. |
130
|
|
|
* |
131
|
|
|
* @param array $entity The entity to merge the attributes into |
132
|
|
|
* @param array $attr The attributes to be merged |
133
|
|
|
* @param string|null $changeSetName The change set name to use |
134
|
|
|
* |
135
|
|
|
* @return array The merged entity |
136
|
|
|
* @todo https://github.com/techdivision/import/issues/179 |
137
|
|
|
*/ |
138
|
2 |
View Code Duplication |
protected function mergeEntity(array $entity, array $attr, $changeSetName = null) |
|
|
|
|
139
|
|
|
{ |
140
|
2 |
|
return array_merge( |
141
|
2 |
|
$entity, |
142
|
2 |
|
$this->entityMerger ? $this->entityMerger->merge($this, $entity, $attr) : $attr, |
|
|
|
|
143
|
2 |
|
array(EntityStatus::MEMBER_NAME => $this->detectState($entity, $attr, $changeSetName)) |
144
|
|
|
); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Prepare the attributes of the entity that has to be persisted. |
149
|
|
|
* |
150
|
|
|
* @return array The prepared attributes |
151
|
|
|
* @throws \Exception Is thrown, if the size of the option values doesn't equals the size of swatch values, in case |
152
|
|
|
*/ |
153
|
3 |
|
protected function prepareAttributes() |
154
|
|
|
{ |
155
|
|
|
|
156
|
|
|
// load the recently created EAV attribute ID |
157
|
3 |
|
$attributeId = $this->getLastAttributeId(); |
158
|
|
|
|
159
|
|
|
// initialize the attributes with the values from the DB |
160
|
3 |
|
$attr = $this->loadRawEntity(array(MemberNames::ATTRIBUTE_ID => $attributeId)); |
161
|
|
|
|
162
|
|
|
// intialize the array with the column names we've to load the data from |
163
|
3 |
|
$columnNames = array_keys($attr); |
164
|
|
|
|
165
|
|
|
// iterate over the possible columns and handle the data |
166
|
3 |
|
foreach ($columnNames as $columnName) { |
167
|
|
|
// reverse map the table column name to the CSV column name |
168
|
3 |
|
$columnName = isset($this->reverseHeaderMappings[$columnName]) ? $this->reverseHeaderMappings[$columnName] : $columnName; |
169
|
|
|
|
170
|
|
|
// query whether or not, the column is available in the CSV file |
171
|
3 |
|
if ($this->getSubject()->hasHeader($columnName)) { |
172
|
|
|
// custom handling for the additional_data column |
173
|
1 |
|
if ($columnName === ColumnKeys::ADDITIONAL_DATA) { |
174
|
|
|
// load the raw additional data |
175
|
1 |
|
$explodedAdditionalData = $this->getValue(ColumnKeys::ADDITIONAL_DATA, array(), array($this->getSubject(), 'explode')); |
176
|
|
|
|
177
|
|
|
// query whether or not additional data has been set |
178
|
1 |
|
if (sizeof($explodedAdditionalData) > 0) { |
179
|
|
|
// load and extract the additional data |
180
|
1 |
|
$additionalData = array(); |
181
|
1 |
|
foreach ($explodedAdditionalData as $value) { |
182
|
1 |
|
list ($key, $val) = $this->getSubject()->explode($value, '='); |
183
|
1 |
|
$additionalData[$key] = $val; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
// set the additional data |
187
|
1 |
|
$attr[$columnName] = $additionalData; |
188
|
|
|
|
189
|
|
|
// query whether or not the attribute is a text or a visual swatch |
190
|
1 |
|
if ($this->isSwatchType($additionalData)) { |
191
|
|
|
// load the attribute option values for the custom store views |
192
|
1 |
|
$attributeOptionValues = $this->getValue(ColumnKeys::ATTRIBUTE_OPTION_VALUES, array(), array($this, 'explode')); |
193
|
1 |
|
$attributeOptionSwatch = $this->getSubject()->explode($this->getValue(ColumnKeys::ATTRIBUTE_OPTION_SWATCH), $this->getSubject()->getMultipleValueDelimiter()); |
194
|
|
|
|
195
|
|
|
// query whether or not the size of the option values equals the size of the swatch values |
196
|
1 |
|
if (($sizeOfSwatchValues = sizeof($attributeOptionSwatch)) !== ($sizeOfOptionValues = sizeof($attributeOptionValues))) { |
197
|
|
|
throw new \Exception( |
198
|
|
|
sprintf( |
199
|
|
|
'Size of option values "%d" doesn\'t equals size of swatch values "%d"', |
200
|
|
|
$sizeOfOptionValues, |
201
|
1 |
|
$sizeOfSwatchValues |
202
|
|
|
) |
203
|
|
|
); |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
} |
207
|
|
|
} else { |
208
|
|
|
// query whether or not a column contains a value |
209
|
1 |
|
if ($this->hasValue($columnName)) { |
210
|
3 |
|
$attr[$columnName] = $this->getValue($columnName); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
// return the prepared product |
217
|
3 |
|
return $this->initializeEntity($attr); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Serialize the additional_data attribute of the passed array. |
222
|
|
|
* |
223
|
|
|
* @param array $attr The attribute with the data to serialize |
224
|
|
|
* |
225
|
|
|
* @return array The attribute with the serialized additional_data |
226
|
|
|
*/ |
227
|
3 |
|
protected function serializeAdditionalData(array $attr) |
228
|
|
|
{ |
229
|
|
|
|
230
|
|
|
// serialize the additional data value if available |
231
|
3 |
|
if (isset($attr[MemberNames::ADDITIONAL_DATA]) && $attr[MemberNames::ADDITIONAL_DATA] !== null) { |
232
|
2 |
|
$attr[MemberNames::ADDITIONAL_DATA] = json_encode($attr[MemberNames::ADDITIONAL_DATA]); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
// return the attribute |
236
|
3 |
|
return $attr; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Load's and return's a raw customer entity without primary key but the mandatory members only and nulled values. |
241
|
|
|
* |
242
|
|
|
* @param array $data An array with data that will be used to initialize the raw entity with |
243
|
|
|
* |
244
|
|
|
* @return array The initialized entity |
245
|
|
|
*/ |
246
|
3 |
|
protected function loadRawEntity(array $data = array()) |
247
|
|
|
{ |
248
|
3 |
|
return $this->getAttributeBunchProcessor()->loadRawEntity(EntityTypeCodes::CATALOG_EAV_ATTRIBUTE, $data); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Initialize the attribute with the passed attributes and returns an instance. |
253
|
|
|
* |
254
|
|
|
* @param array $attr The attribute attributes |
255
|
|
|
* |
256
|
|
|
* @return array The initialized attribute |
257
|
|
|
*/ |
258
|
1 |
|
protected function initializeAttribute(array $attr) |
259
|
|
|
{ |
260
|
1 |
|
return $this->serializeAdditionalData($attr); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Return's the attribute bunch processor instance. |
265
|
|
|
* |
266
|
|
|
* @return \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface The attribute bunch processor instance |
267
|
|
|
*/ |
268
|
3 |
|
protected function getAttributeBunchProcessor() |
269
|
|
|
{ |
270
|
3 |
|
return $this->attributeBunchProcessor; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Map's the passed attribute code to the attribute ID that has been created recently. |
275
|
|
|
* |
276
|
|
|
* @param string $attributeCode The attribute code that has to be mapped |
277
|
|
|
* |
278
|
|
|
* @return void |
279
|
|
|
*/ |
280
|
|
|
protected function addAttributeCodeIdMapping($attributeCode) |
281
|
|
|
{ |
282
|
|
|
$this->getSubject()->addAttributeCodeIdMapping($attributeCode); |
|
|
|
|
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Queries whether or not the attribute with the passed code has already been processed. |
287
|
|
|
* |
288
|
|
|
* @param string $attributeCode The attribute code to check |
289
|
|
|
* |
290
|
|
|
* @return boolean TRUE if the path has been processed, else FALSE |
291
|
|
|
*/ |
292
|
3 |
|
protected function hasBeenProcessed($attributeCode) |
293
|
|
|
{ |
294
|
3 |
|
return $this->getSubject()->hasBeenProcessed($attributeCode); |
|
|
|
|
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Return's the ID of the attribute that has been created recently. |
299
|
|
|
* |
300
|
|
|
* @return integer The attribute ID |
301
|
|
|
*/ |
302
|
3 |
|
protected function getLastAttributeId() |
303
|
|
|
{ |
304
|
3 |
|
return $this->getSubject()->getLastAttributeId(); |
|
|
|
|
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Return's TRUE if the additional data contains a swatch type. |
309
|
|
|
* |
310
|
|
|
* @param array $additionalData The additional data to query for a valid swatch type |
311
|
|
|
* |
312
|
|
|
* @return boolean TRUE if the data contains a swatch type, else FALSE |
313
|
|
|
*/ |
314
|
1 |
|
protected function isSwatchType(array $additionalData) |
315
|
|
|
{ |
316
|
1 |
|
return isset($additionalData[CatalogAttributeObserver::SWATCH_INPUT_TYPE]) && in_array($additionalData[CatalogAttributeObserver::SWATCH_INPUT_TYPE], $this->swatchTypes); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Persist the passed EAV catalog attribute. |
321
|
|
|
* |
322
|
|
|
* @param array $catalogAttribute The EAV catalog attribute to persist |
323
|
|
|
* |
324
|
|
|
* @return void |
325
|
|
|
*/ |
326
|
3 |
|
protected function persistCatalogAttribute(array $catalogAttribute) |
327
|
|
|
{ |
328
|
3 |
|
return $this->getAttributeBunchProcessor()->persistCatalogAttribute($catalogAttribute); |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.