1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * TechDivision\Import\Serializer\Csv\AdditionalAttributeCsvSerializer |
||||
5 | * |
||||
6 | * PHP version 7 |
||||
7 | * |
||||
8 | * @author Tim Wagner <[email protected]> |
||||
9 | * @copyright 2021 TechDivision GmbH <[email protected]> |
||||
10 | * @license https://opensource.org/licenses/MIT |
||||
11 | * @link https://github.com/techdivision/import-serializer-csv |
||||
12 | * @link http://www.techdivision.com |
||||
13 | */ |
||||
14 | |||||
15 | namespace TechDivision\Import\Serializer\Csv; |
||||
16 | |||||
17 | use TechDivision\Import\Serializer\SerializerInterface; |
||||
18 | use TechDivision\Import\Serializer\SerializerFactoryInterface; |
||||
19 | use TechDivision\Import\Serializer\Configuration\ConfigurationInterface; |
||||
20 | use TechDivision\Import\Serializer\AdditionalAttributeSerializerInterface; |
||||
21 | use TechDivision\Import\Serializer\Configuration\SerializerConfigurationInterface; |
||||
22 | use TechDivision\Import\Serializer\Csv\Utils\MemberNames; |
||||
23 | use TechDivision\Import\Serializer\Csv\Utils\FrontendInputTypes; |
||||
24 | use TechDivision\Import\Serializer\Csv\Services\EavAttributeAwareProcessorInterface; |
||||
25 | |||||
26 | /** |
||||
27 | * Serializer implementation that un-/serializes the additional product attribues found in the CSV file |
||||
28 | * in the row 'additional_attributes'. |
||||
29 | * |
||||
30 | * @author Tim Wagner <[email protected]> |
||||
31 | * @copyright 2021 TechDivision GmbH <[email protected]> |
||||
32 | * @license https://opensource.org/licenses/MIT |
||||
33 | * @link https://github.com/techdivision/import-serializer-csv |
||||
34 | * @link http://www.techdivision.com |
||||
35 | */ |
||||
36 | class AdditionalAttributeCsvSerializer extends AbstractCsvSerializer implements AdditionalAttributeSerializerInterface |
||||
37 | { |
||||
38 | |||||
39 | /** |
||||
40 | * The factory instance for the CSV value serializer. |
||||
41 | * |
||||
42 | * @var \TechDivision\Import\Serializer\SerializerFactoryInterface |
||||
43 | */ |
||||
44 | private $valueCsvSerializerFactory; |
||||
45 | |||||
46 | /** |
||||
47 | * The CSV value serializer instance. |
||||
48 | * |
||||
49 | * @var \TechDivision\Import\Serializer\SerializerInterface |
||||
50 | */ |
||||
51 | private $valueCsvSerializer; |
||||
52 | |||||
53 | /** |
||||
54 | * The entity type from the configuration. |
||||
55 | * |
||||
56 | * @var array |
||||
57 | */ |
||||
58 | private $entityType = array(); |
||||
59 | |||||
60 | /** |
||||
61 | * The configuration instance. |
||||
62 | * |
||||
63 | * @var \TechDivision\Import\Serializer\Configuration\ConfigurationInterface |
||||
64 | */ |
||||
65 | private $configuration; |
||||
66 | |||||
67 | /** |
||||
68 | * The attribute loader instance. |
||||
69 | * |
||||
70 | * @var \TechDivision\Import\Serializer\Csv\Services\EavAttributeAwareProcessorInterface |
||||
71 | */ |
||||
72 | private $eavAttributeAwareProcessor; |
||||
73 | |||||
74 | /** |
||||
75 | * Initialize the serializer with the passed CSV value serializer factory. |
||||
76 | * |
||||
77 | * @param \TechDivision\Import\Serializer\Configuration\ConfigurationInterface $configuration The configuration instance |
||||
78 | * @param \TechDivision\Import\Serializer\Csv\Services\EavAttributeAwareProcessorInterface $attributeLoader The attribute loader instance |
||||
79 | * @param \TechDivision\Import\Serializer\SerializerFactoryInterface $valueCsvSerializerFactory The CSV value serializer factory |
||||
80 | */ |
||||
81 | public function __construct( |
||||
82 | ConfigurationInterface $configuration, |
||||
83 | EavAttributeAwareProcessorInterface $attributeLoader, |
||||
84 | SerializerFactoryInterface $valueCsvSerializerFactory |
||||
85 | ) { |
||||
86 | |||||
87 | // set the passed instances |
||||
88 | $this->configuration = $configuration; |
||||
89 | $this->eavAttributeAwareProcessor = $attributeLoader; |
||||
90 | $this->valueCsvSerializerFactory = $valueCsvSerializerFactory; |
||||
91 | |||||
92 | // load the entity type for the entity type defined in the configuration |
||||
93 | $this->entityType = $attributeLoader->getEavEntityTypeByEntityTypeCode($configuration->getEntityTypeCode()); |
||||
94 | |||||
95 | // pre-initialize the serialize with the values |
||||
96 | // found in the main configuration |
||||
97 | $this->init($configuration); |
||||
98 | } |
||||
99 | |||||
100 | /** |
||||
101 | * Returns the configuration instance. |
||||
102 | * |
||||
103 | * @return \TechDivision\Import\Serializer\Configuration\SerializerConfigurationInterface The configuration instance |
||||
104 | */ |
||||
105 | protected function getConfiguration() |
||||
106 | { |
||||
107 | return $this->configuration; |
||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||
108 | } |
||||
109 | |||||
110 | /** |
||||
111 | * Returns the factory instance for the CSV value serializer. |
||||
112 | * |
||||
113 | * @return \TechDivision\Import\Serializer\SerializerFactoryInterface The CSV value serializer factory instance |
||||
114 | */ |
||||
115 | protected function getValueCsvSerializerFactory() |
||||
116 | { |
||||
117 | return $this->valueCsvSerializerFactory; |
||||
118 | } |
||||
119 | |||||
120 | /** |
||||
121 | * Returns the CSV value serializer instance. |
||||
122 | * |
||||
123 | * @param \TechDivision\Import\Serializer\SerializerInterface $valueCsvSerializer The CSV value serializer instance |
||||
124 | * |
||||
125 | * @return void |
||||
126 | */ |
||||
127 | protected function setValueCsvSerializer(SerializerInterface $valueCsvSerializer) |
||||
128 | { |
||||
129 | $this->valueCsvSerializer = $valueCsvSerializer; |
||||
130 | } |
||||
131 | |||||
132 | /** |
||||
133 | * Returns the CSV value serializer instance. |
||||
134 | * |
||||
135 | * @return \TechDivision\Import\Serializer\SerializerInterface The CSV value serializer instance |
||||
136 | */ |
||||
137 | protected function getValueCsvSerializer() |
||||
138 | { |
||||
139 | return $this->valueCsvSerializer; |
||||
140 | } |
||||
141 | |||||
142 | /** |
||||
143 | * Returns the EAV attribute aware processor instance. |
||||
144 | * |
||||
145 | * @return \TechDivision\Import\Serializer\Csv\Services\EavAttributeAwareProcessorInterface The EAV attribute aware processor instance |
||||
146 | */ |
||||
147 | protected function getEavAttributeAwareProcessor() : EavAttributeAwareProcessorInterface |
||||
148 | { |
||||
149 | return $this->eavAttributeAwareProcessor; |
||||
150 | } |
||||
151 | |||||
152 | /** |
||||
153 | * Returns entity type ID mapped from the configuration. |
||||
154 | * |
||||
155 | * @return integer The mapped entity type ID |
||||
156 | */ |
||||
157 | protected function getEntityTypeId() |
||||
158 | { |
||||
159 | return $this->entityType[MemberNames::ENTITY_TYPE_ID]; |
||||
160 | } |
||||
161 | |||||
162 | /** |
||||
163 | * Returns the multiple value delimiter from the configuration. |
||||
164 | * |
||||
165 | * @return string The multiple value delimiter |
||||
166 | */ |
||||
167 | protected function getMultipleValueDelimiter() |
||||
168 | { |
||||
169 | return $this->getConfiguration()->getMultipleValueDelimiter(); |
||||
0 ignored issues
–
show
The method
getMultipleValueDelimiter() does not exist on TechDivision\Import\Seri...rConfigurationInterface . It seems like you code against a sub-type of TechDivision\Import\Seri...rConfigurationInterface such as TechDivision\Import\Seri...\ConfigurationInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
170 | } |
||||
171 | |||||
172 | /** |
||||
173 | * Returns the multiple field delimiter from the configuration. |
||||
174 | * |
||||
175 | * @return string The multiple field delimiter |
||||
176 | */ |
||||
177 | protected function getMultipleFieldDelimiter() |
||||
178 | { |
||||
179 | return $this->getConfiguration()->getMultipleFieldDelimiter(); |
||||
0 ignored issues
–
show
The method
getMultipleFieldDelimiter() does not exist on TechDivision\Import\Seri...rConfigurationInterface . It seems like you code against a sub-type of TechDivision\Import\Seri...rConfigurationInterface such as TechDivision\Import\Seri...\ConfigurationInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
180 | } |
||||
181 | |||||
182 | /** |
||||
183 | * Loads and returns the attribute with the passed code from the database. |
||||
184 | * |
||||
185 | * @param string $attributeCode The code of the attribute to return |
||||
186 | * |
||||
187 | * @return array The attribute |
||||
188 | */ |
||||
189 | protected function loadAttributeByAttributeCode($attributeCode) |
||||
190 | { |
||||
191 | return $this->getEavAttributeAwareProcessor()->getEavAttributeByEntityTypeIdAndAttributeCode($this->getEntityTypeId(), $attributeCode); |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * Packs the passed value according to the frontend input type of the attribute with the passed code. |
||||
196 | * |
||||
197 | * @param string $attributeCode The code of the attribute to pack the passed value for |
||||
198 | * @param mixed $value The value to pack |
||||
199 | * |
||||
200 | * @return string The packed value |
||||
201 | */ |
||||
202 | protected function pack($attributeCode, $value) |
||||
203 | { |
||||
204 | |||||
205 | // load the attibute with the passed code |
||||
206 | $attribute = $this->loadAttributeByAttributeCode($attributeCode); |
||||
207 | |||||
208 | // pack the value according to the attribute's frontend input type |
||||
209 | switch ($attribute[MemberNames::FRONTEND_INPUT]) { |
||||
210 | case FrontendInputTypes::MULTISELECT: |
||||
211 | return $this->implode($value, $this->getMultipleValueDelimiter()); |
||||
212 | break; |
||||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other ![]() |
|||||
213 | |||||
214 | case FrontendInputTypes::BOOLEAN: |
||||
215 | return $value === true ? 'true' : 'false'; |
||||
216 | break; |
||||
217 | |||||
218 | default: |
||||
219 | return $value; |
||||
220 | } |
||||
221 | } |
||||
222 | |||||
223 | /** |
||||
224 | * Unpacks the passed value according to the frontend input type of the attribute with the passed code. |
||||
225 | * |
||||
226 | * @param string $attributeCode The code of the attribute to pack the passed value for |
||||
227 | * @param string $value The value to unpack |
||||
228 | * |
||||
229 | * @return mixed The unpacked value |
||||
230 | */ |
||||
231 | protected function unpack($attributeCode, $value) |
||||
232 | { |
||||
233 | |||||
234 | // load the attibute with the passed code |
||||
235 | $attribute = $this->loadAttributeByAttributeCode($attributeCode); |
||||
236 | |||||
237 | // unpack the value according to the attribute's frontend input type |
||||
238 | switch ($attribute[MemberNames::FRONTEND_INPUT]) { |
||||
239 | case FrontendInputTypes::MULTISELECT: |
||||
240 | return $this->explode($value, $this->getMultipleValueDelimiter()); |
||||
241 | break; |
||||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other ![]() |
|||||
242 | |||||
243 | case FrontendInputTypes::BOOLEAN: |
||||
244 | return filter_var($value, FILTER_VALIDATE_BOOLEAN); |
||||
245 | break; |
||||
246 | |||||
247 | default: |
||||
248 | return $value; |
||||
249 | } |
||||
250 | } |
||||
251 | |||||
252 | /** |
||||
253 | * Passes the configuration and initializes the serializer. |
||||
254 | * |
||||
255 | * @param \TechDivision\Import\Serializer\Configuration\SerializerConfigurationInterface $configuration The CSV configuration |
||||
256 | * |
||||
257 | * @return void |
||||
258 | */ |
||||
259 | public function init(SerializerConfigurationInterface $configuration) |
||||
260 | { |
||||
261 | |||||
262 | // pass the configuration to the parent instance |
||||
263 | parent::init($configuration); |
||||
264 | |||||
265 | // create the CSV value serializer instance |
||||
266 | $this->setValueCsvSerializer($this->getValueCsvSerializerFactory()->createSerializer($configuration)); |
||||
267 | } |
||||
268 | |||||
269 | /** |
||||
270 | * Unserializes the elements of the passed string. |
||||
271 | * |
||||
272 | * @param string|null $serialized The value to unserialize |
||||
273 | * @param string|null $delimiter The delimiter used to unserialize the elements |
||||
274 | * |
||||
275 | * @return array The unserialized values |
||||
276 | * @see \TechDivision\Import\Serializer\SerializerInterface::unserialize() |
||||
277 | */ |
||||
278 | public function unserialize($serialized = null, $delimiter = null) |
||||
279 | { |
||||
280 | return $this->getValueCsvSerializer()->explode($serialized, $delimiter ? $delimiter : $this->getMultipleFieldDelimiter()); |
||||
281 | } |
||||
282 | |||||
283 | /** |
||||
284 | * Serializes the elements of the passed array. |
||||
285 | * |
||||
286 | * @param array|null $unserialized The serialized data |
||||
287 | * @param string|null $delimiter The delimiter used to serialize the values |
||||
288 | * |
||||
289 | * @return string The serialized array |
||||
290 | * @see \TechDivision\Import\Serializer\SerializerInterface::serialize() |
||||
291 | */ |
||||
292 | public function serialize(array $unserialized = null, $delimiter = null) |
||||
293 | { |
||||
294 | return $this->getValueCsvSerializer()->implode($unserialized, $delimiter ? $delimiter : $this->getMultipleFieldDelimiter()); |
||||
295 | } |
||||
296 | |||||
297 | /** |
||||
298 | * Extracts the elements of the passed value by exploding them |
||||
299 | * with the also passed delimiter. |
||||
300 | * |
||||
301 | * @param string|null $value The value to extract |
||||
302 | * @param string|null $delimiter The delimiter used to extrace the elements |
||||
303 | * |
||||
304 | * @return array|null The exploded values |
||||
305 | * @see \TechDivision\Import\Serializer\SerializerInterface::unserialize() |
||||
306 | */ |
||||
307 | public function explode($value = null, $delimiter = null) |
||||
308 | { |
||||
309 | return $this->unserialize($value, $delimiter); |
||||
310 | } |
||||
311 | |||||
312 | /** |
||||
313 | * Compacts the elements of the passed value by imploding them |
||||
314 | * with the also passed delimiter. |
||||
315 | * |
||||
316 | * @param array|null $value The values to compact |
||||
317 | * @param string|null $delimiter The delimiter use to implode the values |
||||
318 | * |
||||
319 | * @return string|null The compatected value |
||||
320 | * @see \TechDivision\Import\Serializer\SerializerInterface::serialize() |
||||
321 | */ |
||||
322 | public function implode(array $value = null, $delimiter = null) |
||||
323 | { |
||||
324 | return $this->serialize($value, $delimiter); |
||||
325 | } |
||||
326 | |||||
327 | /** |
||||
328 | * Create a CSV compatible string from the passed category path. |
||||
329 | * |
||||
330 | * @param string|null $value The normalized category path (usually from the DB) |
||||
331 | * @param bool $unpack TRUE if the option values has to be unpacked, in case of a multiselect attribute |
||||
332 | * |
||||
333 | * @return array The array with the denormalized attribute values |
||||
334 | */ |
||||
335 | public function denormalize(string $value = null, bool $unpack = true) : array |
||||
336 | { |
||||
337 | |||||
338 | // initialize the array for the attributes |
||||
339 | $attrs = array(); |
||||
340 | |||||
341 | // explode the additional attributes and iterate |
||||
342 | // over the attributes and append them to the row |
||||
343 | if (is_array($additionalAttributes = $this->unserialize($value))) { |
||||
0 ignored issues
–
show
|
|||||
344 | foreach ($additionalAttributes as $additionalAttribute) { |
||||
345 | // initialize the option value |
||||
346 | $optionValue = ''; |
||||
347 | // explode attribute code/option value from the attribute |
||||
348 | $exploded = $this->explode($additionalAttribute, '='); |
||||
349 | // initialize attribute code and option value, depending on what we've exploded |
||||
350 | if (!is_array($exploded) || sizeof($exploded) < 1) { |
||||
351 | continue; |
||||
352 | } elseif (sizeof($exploded) === 1) { |
||||
353 | list ($attributeCode) = $exploded; |
||||
354 | } else { |
||||
355 | list ($attributeCode, $optionValue) = $exploded; |
||||
356 | } |
||||
357 | // query whether or not we've to pack the option |
||||
358 | // values in case we've a multiselect input field |
||||
359 | $optionValue = $unpack ? $this->unpack($attributeCode, $optionValue) : $optionValue; |
||||
360 | // unpack the attribute values and add them to the array |
||||
361 | $attrs[$attributeCode] = $optionValue; |
||||
362 | } |
||||
363 | } |
||||
364 | |||||
365 | // return the extracted array with the additional attributes |
||||
366 | return $attrs; |
||||
367 | } |
||||
368 | |||||
369 | /** |
||||
370 | * Normalizes the category path in a standard representation. |
||||
371 | * |
||||
372 | * For example this means, that a category path `Default Category/Gear` |
||||
373 | * will be normalized into `"Default Category"/Gear`. |
||||
374 | * |
||||
375 | * @param array $values The category path that has to be normalized |
||||
376 | * @param bool $pack TRUE if the option values has to be packed, in case of a multiselect attribute |
||||
377 | * |
||||
378 | * @return string The normalized category path |
||||
379 | */ |
||||
380 | public function normalize(array $values, bool $pack = true) : string |
||||
381 | { |
||||
382 | |||||
383 | // initialize the array for the attributes |
||||
384 | $attrs = array(); |
||||
385 | |||||
386 | // iterate over the attributes and append them to the row |
||||
387 | if (is_array($values)) { |
||||
0 ignored issues
–
show
|
|||||
388 | foreach ($values as $attributeCode => $optionValue) { |
||||
389 | // query whether or not we've to unpack the option |
||||
390 | // values in case we've a multiselect input field |
||||
391 | $optionValue = $pack ? $this->pack($attributeCode, $optionValue) : $optionValue; |
||||
392 | // append th eption value to the array |
||||
393 | $attrs[] = sprintf('%s=%s', $attributeCode, $optionValue); |
||||
394 | } |
||||
395 | } |
||||
396 | |||||
397 | // implode the array with the packed additional attributes and return it |
||||
398 | return $this->serialize($attrs); |
||||
399 | } |
||||
400 | } |
||||
401 |