Completed
Push — master ( 15a8ae...22c46b )
by Tim
11s queued 10s
created

getEmptyAttributeValueConstant()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 1
c 1
b 0
f 1
dl 0
loc 3
ccs 0
cts 1
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * TechDivision\Import\Converter\Product\Attribute\Observers\ProductToAttributeOptionValueConverterObserver
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 2019 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-converter-product-attribute
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Converter\Product\Attribute\Observers;
22
23
use TechDivision\Import\Utils\StoreViewCodes;
24
use TechDivision\Import\Utils\FrontendInputTypes;
25
use TechDivision\Import\Attribute\Utils\ColumnKeys;
26
use TechDivision\Import\Attribute\Utils\MemberNames;
27
use TechDivision\Import\Product\Utils\ConfigurationKeys;
28
use TechDivision\Import\Observers\StateDetectorInterface;
29
use TechDivision\Import\Services\ImportProcessorInterface;
30
use TechDivision\Import\Converter\Observers\AbstractConverterObserver;
31
use TechDivision\Import\Attribute\Callbacks\SwatchTypeLoaderInterface;
32
use TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface;
33
34
/**
35
 * Observer that extracts the missing attribute option values from a product CSV.
36
 *
37
 * @author    Tim Wagner <[email protected]>
38
 * @copyright 2019 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-converter-product-attribute
41
 * @link      http://www.techdivision.com
42
 */
43
class ProductToAttributeOptionValueConverterObserver extends AbstractConverterObserver
44
{
45
46
    /**
47
     * The artefact type.
48
     *
49
     * @var string
50
     */
51
    const ARTEFACT_TYPE = 'option-import';
52
53
    /**
54
     * The import processor instance.
55
     *
56
     * @var \TechDivision\Import\Services\ImportProcessorInterface
57
     */
58
    protected $importProcessor;
59
60
    /**
61
     * The attribute bunch processor instance.
62
     *
63
     * @var \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface
64
     */
65
    protected $attributeBunchProcessor;
66
67
    /**
68
     * The swatch type loader instance.
69
     *
70
     * @var \TechDivision\Import\Attribute\Callbacks\SwatchTypeLoaderInterface
71
     */
72
    protected $swatchTypeLoader;
73
74
    /**
75
     * The array with the column keys that has to be cleaned up when their values are empty.
76
     *
77
     * @var array
78
     */
79
    protected $cleanUpEmptyColumnKeys;
80
81
    /**
82
     * Initialize the observer with the passed product bunch processor instance.
83
     *
84
     * @param \TechDivision\Import\Services\ImportProcessorInterface                   $importProcessor         The product bunch processor instance
85
     * @param \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface $attributeBunchProcessor The attribute bunch processor instance
86
     * @param \TechDivision\Import\Attribute\Callbacks\SwatchTypeLoaderInterface       $swatchTypeLoader        The swatch type loader instance
87
     * @param \TechDivision\Import\Observers\StateDetectorInterface|null               $stateDetector           The state detector instance to use
88
     */
89
    public function __construct(
90
        ImportProcessorInterface $importProcessor,
91
        AttributeBunchProcessorInterface $attributeBunchProcessor,
92
        SwatchTypeLoaderInterface $swatchTypeLoader,
93
        StateDetectorInterface $stateDetector = null
94
    ) {
95
96
        // initialize the swatch type loader and the processor instances
97
        $this->importProcessor = $importProcessor;
98
        $this->swatchTypeLoader = $swatchTypeLoader;
99
        $this->attributeBunchProcessor = $attributeBunchProcessor;
100
101
        // pass the state detector to the parent method
102
        parent::__construct($stateDetector);
103
    }
104
105
    /**
106
     * @return string
107
     */
108
    public function getEmptyAttributeValueConstant()
109
    {
110
        return $this->getSubject()->getConfiguration()->getConfiguration()->getEmptyAttributeValueConstant();
111
    }
112
113
    /**
114
     * Remove all the empty values from the row and return the cleared row.
115
     *
116
     * @return array The cleared row
117
     */
118
    protected function clearRow()
119
    {
120
121
        // query whether or not the column keys has been initialized
122
        if ($this->cleanUpEmptyColumnKeys === null) {
123
            // initialize the array with the column keys that has to be cleaned-up
124
            $this->cleanUpEmptyColumnKeys = array();
125
126
            // query whether or not column names that has to be cleaned up have been configured
127
            if ($this->getSubject()->getConfiguration()->hasParam(ConfigurationKeys::CLEAN_UP_EMPTY_COLUMNS)) {
128
                // if yes, load the column names
129
                $cleanUpEmptyColumns = $this->getSubject()->getCleanUpColumns();
130
131
                // translate the column names into column keys
132
                foreach ($cleanUpEmptyColumns as $cleanUpEmptyColumn) {
133
                    if ($this->hasHeader($cleanUpEmptyColumn)) {
134
                        $this->cleanUpEmptyColumnKeys[] = $this->getHeader($cleanUpEmptyColumn);
135
                    }
136
                }
137
            }
138
        }
139
140
        $emptyValueDefinition = $this->getEmptyAttributeValueConstant();
141
        // load the header keys
142
        $headers = in_array($emptyValueDefinition, $this->row, true) ? array_flip($this->getHeaders()) : [];
143
        // remove all the empty values from the row, expected the columns has to be cleaned-up
144
        foreach ($this->row as $key => $value) {
145
            // query whether or not to cleanup complete attribute
146
            if ($value === $emptyValueDefinition) {
147
                $this->cleanUpEmptyColumnKeys[$headers[$key]] = $key;
148
                $this->row[$key] = '';
149
            }
150
            // query whether or not the value is empty AND the column has NOT to be cleaned-up
151
            if (($value === null || $value === '') && in_array($key, $this->cleanUpEmptyColumnKeys) === false) {
152
                unset($this->row[$key]);
153
            }
154
        }
155
156
        // finally return the clean row
157
        return $this->row;
158
    }
159
160
    /**
161
     * Process the observer's business logic.
162
     *
163
     * @return void
164
     */
165
    protected function process()
166
    {
167
168
        // initialize the store view code
169
        $this->prepareStoreViewCode();
170
171
        // load the store ID, use the admin store if NO store view code has been set
172
        $storeId = $this->getStoreId(StoreViewCodes::ADMIN);
173
174
        // load the user defined EAV attributes by the found attribute set and the backend types
175
        $attributes = $this->getEavUserDefinedAttributes();
176
177
        // load the header keys
178
        $headers = array_flip($this->getHeaders());
179
180
        // remove all the empty values from the row
181
        $row = $this->clearRow();
182
183
        // initialize the array for the artefacts
184
        $artefacts = array();
185
186
        // load the entity type ID
187
        $entityType = $this->loadEavEntityTypeByEntityTypeCode($this->getSubject()->getEntityTypeCode());
188
        $entityTypeId = $entityType[MemberNames::ENTITY_TYPE_ID];
189
190
        $emptyValueDefinition = $this->getEmptyAttributeValueConstant();
191
192
        // iterate over the attributes and append them to the row
193
        foreach ($row as $key => $attributeValue) {
194
            // query whether or not attribute with the found code exists
195
            if (!isset($attributes[$attributeCode = $headers[$key]])) {
196
                // log a message in debug mode
197
                if ($this->isDebugMode()) {
198
                    $this->getSystemLogger()->debug(
199
                        $this->appendExceptionSuffix(
200
                            sprintf(
201
                                'Can\'t find attribute with attribute code %s',
202
                                $attributeCode
203
                            )
204
                        )
205
                    );
206
                }
207
208
                // stop processing
209
                continue;
210
            } else {
211
                // log a message in debug mode
212
                if ($this->isDebugMode()) {
213
                    // log a message in debug mode
214
                    $this->getSystemLogger()->debug(
215
                        $this->appendExceptionSuffix(
216
                            sprintf(
217
                                'Found attribute with attribute code %s',
218
                                $attributeCode
219
                            )
220
                        )
221
                    );
222
                }
223
            }
224
225
            // if yes, load the attribute by its code
226
            $attribute = $attributes[$attributeCode];
227
228
            // we only support user defined EAV attributes of type select and multiselect
229
            if (in_array($attribute[MemberNames::FRONTEND_INPUT], array(FrontendInputTypes::SELECT, FrontendInputTypes::MULTISELECT))) {
230
                // explode the values if we've a multiselect
231
                $valuesExploded = $this->explode($attributeValue, $this->getMultipleValueDelimiter());
232
                // check if valueExploded an array to fix crash on next step "foreach"
233
                $values = is_array($valuesExploded) ? $valuesExploded : [];
234
                // iterate over the values
235
                foreach ($values as $value) {
236
                    // query whether the value corresponds to the Empty Value definition to skip
237
                    if ($value === $emptyValueDefinition) {
238
                        continue;
239
                    }
240
                    // query whether or not the attribute value already exists
241
                    if ($this->loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)) {
242
                        continue;
243
                    }
244
245
                    // try to load the swatch type, if available
246
                    $swatchType = $this->getSwatchTypeLoader()->loadSwatchType($entityTypeId, $attributeCode);
247
248
                    // add the artefact to the array
249
                    $artefacts[] = $this->newArtefact(
250
                        array(
251
                            ColumnKeys::DEFAULT_VALUE  => $attribute[MemberNames::DEFAULT_VALUE],
252
                            ColumnKeys::ATTRIBUTE_CODE => $attribute[MemberNames::ATTRIBUTE_CODE],
253
                            ColumnKeys::SORT_ORDER     => 0,
254
                            ColumnKeys::VALUE          => is_null($swatchType) ? $value : null,
255
                            ColumnKeys::SWATCH_TYPE    => $swatchType,
256
                            ColumnKeys::SWATCH_VALUE   => $swatchType ? $value : null
257
                        ),
258
                        array()
259
                    );
260
                }
261
            }
262
        }
263
264
        // export the array with artefacts
265
        $this->addArtefacts($artefacts);
266
    }
267
268
    /**
269
     * Returns the value(s) of the primary key column(s). As the primary key column can
270
     * also consist of two columns, the return value can be an array also.
271
     *
272
     * @return mixed The primary key value(s)
273
     */
274
    protected function getPrimaryKeyValue()
275
    {
276
        return $this->getValue(\TechDivision\Import\Product\Utils\ColumnKeys::SKU);
277
    }
278
279
    /**
280
     * Return's the import processor instance.
281
     *
282
     * @return \TechDivision\Import\Services\ImportProcessorInterface The import processor instance
283
     */
284
    protected function getImportProcessor()
285
    {
286
        return $this->importProcessor;
287
    }
288
289
    /**
290
     * Return's the attribute bunch processor instance.
291
     *
292
     * @return \TechDivision\Import\Attribute\Services\AttributeBunchProcessorInterface The attribute bunch processor instance
293
     */
294
    protected function getAttributeBunchProcessor()
295
    {
296
        return $this->attributeBunchProcessor;
297
    }
298
299
    /**
300
     * Return's the swatch type loader instance.
301
     *
302
     * @return \TechDivision\Import\Attribute\Callbacks\SwatchTypeLoaderInterface The swatch type loader instance
303
     */
304
    protected function getSwatchTypeLoader()
305
    {
306
        return $this->swatchTypeLoader;
307
    }
308
309
    /**
310
     * Return's an array with the available user defined EAV attributes for the actual entity type.
311
     *
312
     * @return array The array with the user defined EAV attributes
313
     */
314
    protected function getEavUserDefinedAttributes()
315
    {
316
        return $this->getSubject()->getEavUserDefinedAttributes();
317
    }
318
319
    /**
320
     * Return's an EAV entity type with the passed entity type code.
321
     *
322
     * @param string $entityTypeCode The code of the entity type to return
323
     *
324
     * @return array The entity type with the passed entity type code
325
     */
326
    protected function loadEavEntityTypeByEntityTypeCode($entityTypeCode)
327
    {
328
        return $this->getImportProcessor()->getEavEntityTypeByEntityTypeCode($entityTypeCode);
329
    }
330
331
    /**
332
     * Load's and return's the EAV attribute option value with the passed entity type ID, code, store ID and value.
333
     *
334
     * @param string  $entityTypeId  The entity type ID of the EAV attribute to load the option value for
335
     * @param string  $attributeCode The code of the EAV attribute option to load
336
     * @param integer $storeId       The store ID of the attribute option to load
337
     * @param string  $value         The value of the attribute option to load
338
     *
339
     * @return array The EAV attribute option value
340
     */
341
    protected function loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value)
342
    {
343
        return $this->getAttributeBunchProcessor()->loadAttributeOptionValueByEntityTypeIdAndAttributeCodeAndStoreIdAndValue($entityTypeId, $attributeCode, $storeId, $value);
344
    }
345
346
    /**
347
     * Create's and return's a new empty artefact entity.
348
     *
349
     * @param array $columns             The array with the column data
350
     * @param array $originalColumnNames The array with a mapping from the old to the new column names
351
     *
352
     * @return array The new artefact entity
353
     */
354
    protected function newArtefact(array $columns, array $originalColumnNames)
355
    {
356
        return $this->getSubject()->newArtefact($columns, $originalColumnNames);
357
    }
358
359
    /**
360
     * Add the passed product type artefacts to the product with the
361
     * last entity ID.
362
     *
363
     * @param array $artefacts The product type artefacts
364
     *
365
     * @return void
366
     * @uses \TechDivision\Import\Product\Media\Subjects\MediaSubject::getLastEntityId()
367
     */
368
    protected function addArtefacts(array $artefacts)
369
    {
370
        $this->getSubject()->addArtefacts(ProductToAttributeOptionValueConverterObserver::ARTEFACT_TYPE, $artefacts);
371
    }
372
}
373