Completed
Push — master ( a29837...055a81 )
by Tim
03:26
created

AbstractProductSubject   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 497
Duplicated Lines 6.04 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 9.48%

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 33
c 7
b 0
f 0
lcom 1
cbo 2
dl 30
loc 497
ccs 11
cts 116
cp 0.0948
rs 9.3999

20 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultFrontendInputCallbackMappings() 0 4 1
A getLinkTypes() 0 4 1
A setLastSku() 0 4 1
A getLastSku() 0 4 1
A setLastEntityId() 0 4 1
A getLastEntityId() 0 4 1
A hasBeenProcessed() 0 4 1
A storeViewHasBeenProcessed() 0 4 3
A addSkuEntityIdMapping() 0 4 1
A addSkuStoreViewCodeMapping() 0 4 1
A setUp() 0 16 1
A tearDown() 0 19 1
A getImageTypes() 0 4 1
B getRowStoreId() 0 24 3
A getTaxClassIdByTaxClassName() 15 15 2
A getStoreWebsiteIdByCode() 15 15 2
A getCategoryByPath() 0 15 2
A getCategory() 0 17 3
A getRootCategory() 0 21 2
B getStoreViewCodesByWebsiteCode() 0 29 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * TechDivision\Import\Product\Subjects\AbstractProductSubject
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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Subjects;
22
23
use TechDivision\Import\Utils\RegistryKeys;
24
use TechDivision\Import\Utils\FrontendInputTypes;
25
use TechDivision\Import\Product\Utils\MemberNames;
26
use TechDivision\Import\Subjects\AbstractEavSubject;
27
use TechDivision\Import\Subjects\EntitySubjectInterface;
28
29
/**
30
 * The abstract product subject implementation that provides basic product
31
 * handling business logic.
32
 *
33
 * @author    Tim Wagner <[email protected]>
34
 * @copyright 2016 TechDivision GmbH <[email protected]>
35
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
36
 * @link      https://github.com/techdivision/import-product
37
 * @link      http://www.techdivision.com
38
 */
39
abstract class AbstractProductSubject extends AbstractEavSubject implements EntitySubjectInterface
40
{
41
42
    /**
43
     * The available stores.
44
     *
45
     * @var array
46
     */
47
    protected $stores = array();
48
49
    /**
50
     * The available store websites.
51
     *
52
     * @var array
53
     */
54
    protected $storeWebsites = array();
55
56
    /**
57
     * The available EAV attributes, grouped by their attribute set and the attribute set name as keys.
58
     *
59
     * @var array
60
     */
61
    protected $attributes = array();
62
63
    /**
64
     * The available tax classes.
65
     *
66
     * @var array
67
     */
68
    protected $taxClasses = array();
69
70
    /**
71
     * The available categories.
72
     *
73
     * @var array
74
     */
75
    protected $categories = array();
76
77
    /**
78
     * The available link types.
79
     *
80
     * @var array
81
     */
82
    protected $linkTypes = array();
83
84
    /**
85
     * The ID of the product that has been created recently.
86
     *
87
     * @var string
88
     */
89
    protected $lastEntityId;
90
91
    /**
92
     * The SKU of the product that has been created recently.
93
     *
94
     * @var string
95
     */
96
    protected $lastSku;
97
98
    /**
99
     * The Magento 2 configuration.
100
     *
101
     * @var array
102
     */
103
    protected $coreConfigData;
104
105
    /**
106
     * The mapping for the SKUs to the created entity IDs.
107
     *
108
     * @var array
109
     */
110
    protected $skuEntityIdMapping = array();
111
112
    /**
113
     * The mapping for the SKUs to the store view codes.
114
     *
115
     * @var array
116
     */
117
    protected $skuStoreViewCodeMapping = array();
118
119
    /**
120
     * The array with the available image types and their label columns.
121
     *
122
     * @var array
123
     */
124
    protected $imageTypes = array();
125
126
    /**
127
     * Mappings for CSV column header => attribute code.
128
     *
129
     * @var array
130
     */
131
    protected $headerMappings = array(
132
        'product_online'       => 'status',
133
        'tax_class_name'       => 'tax_class_id',
134
        'bundle_price_type'    => 'price_type',
135
        'bundle_sku_type'      => 'sku_type',
136
        'bundle_price_view'    => 'price_view',
137
        'bundle_weight_type'   => 'weight_type',
138
        'bundle_shipment_type' => 'shipment_type',
139
        'related_skus'         => 'relation_skus',
140
        'related_position'     => 'relation_position',
141
        'crosssell_skus'       => 'cross_sell_skus',
142
        'crosssell_position'   => 'cross_sell_position',
143
        'upsell_skus'          => 'up_sell_skus',
144
        'upsell_position'      => 'up_sell_position',
145
        'msrp_price'           => 'msrp',
146
        'base_image'           => 'image',
147
        'base_image_label'     => 'image_label',
148
        'thumbnail_image'      => 'thumbnail',
149
        'thumbnail_image_label'=> 'thumbnail_label'
150
    );
151
152
    /**
153
     * The default mappings for the user defined attributes, based on the attributes frontend input type.
154
     *
155
     * @var array
156
     */
157
    protected $defaultFrontendInputCallbackMappings = array(
158
        FrontendInputTypes::SELECT      => 'import_product.callback.select',
159
        FrontendInputTypes::MULTISELECT => 'import_product.callback.multiselect',
160
        FrontendInputTypes::BOOLEAN     => 'import_product.callback.boolean'
161
    );
162
163
    /**
164
     * Return's the default callback frontend input mappings for the user defined attributes.
165
     *
166
     * @return array The default frontend input callback mappings
167
     */
168
    public function getDefaultFrontendInputCallbackMappings()
169
    {
170
        return $this->defaultFrontendInputCallbackMappings;
171
    }
172
173 18
    /**
174
     * Return's the available link types.
175 18
     *
176
     * @return array The link types
177
     */
178
    public function getLinkTypes()
179
    {
180
        return $this->linkTypes;
181
    }
182
183
    /**
184
     * Set's the SKU of the last imported product.
185
     *
186
     * @param string $lastSku The SKU
187
     *
188
     * @return void
189
     */
190
    public function setLastSku($lastSku)
191
    {
192
        $this->lastSku = $lastSku;
193
    }
194
195
    /**
196
     * Return's the SKU of the last imported product.
197
     *
198
     * @return string|null The SKU
199
     */
200
    public function getLastSku()
201
    {
202
        return $this->lastSku;
203
    }
204
205
    /**
206
     * Set's the ID of the product that has been created recently.
207
     *
208
     * @param string $lastEntityId The entity ID
209
     *
210
     * @return void
211
     */
212
    public function setLastEntityId($lastEntityId)
213
    {
214
        $this->lastEntityId = $lastEntityId;
215
    }
216
217
    /**
218
     * Return's the ID of the product that has been created recently.
219
     *
220
     * @return string The entity Id
221
     */
222
    public function getLastEntityId()
223
    {
224
        return $this->lastEntityId;
225
    }
226
227
    /**
228
     * Queries whether or not the SKU has already been processed.
229
     *
230
     * @param string $sku The SKU to check been processed
231
     *
232
     * @return boolean TRUE if the SKU has been processed, else FALSE
233
     */
234
    public function hasBeenProcessed($sku)
235
    {
236
        return isset($this->skuEntityIdMapping[$sku]);
237
    }
238
239
    /**
240
     * Queries whether or not the passed PK and store view code has already been processed.
241
     *
242
     * @param string $pk            The PK to check been processed
243
     * @param string $storeViewCode The store view code to check been processed
244
     *
245
     * @return boolean TRUE if the PK and store view code has been processed, else FALSE
246
     */
247
    public function storeViewHasBeenProcessed($pk, $storeViewCode)
248
    {
249
        return isset($this->skuEntityIdMapping[$pk]) && isset($this->skuStoreViewCodeMapping[$pk]) && in_array($storeViewCode, $this->skuStoreViewCodeMapping[$pk]);
250
    }
251
252
    /**
253
     * Add the passed SKU => entity ID mapping.
254
     *
255
     * @param string $sku The SKU
256
     *
257
     * @return void
258
     */
259
    public function addSkuEntityIdMapping($sku)
260
    {
261
        $this->skuEntityIdMapping[$sku] = $this->getLastEntityId();
262
    }
263
264
    /**
265
     * Add the passed SKU => store view code mapping.
266
     *
267
     * @param string $sku           The SKU
268
     * @param string $storeViewCode The store view code
269
     *
270
     * @return void
271
     */
272
    public function addSkuStoreViewCodeMapping($sku, $storeViewCode)
273
    {
274
        $this->skuStoreViewCodeMapping[$sku][] = $storeViewCode;
275
    }
276
277
    /**
278
     * Intializes the previously loaded global data for exactly one bunch.
279
     *
280
     * @param string $serial The serial of the actual import
281
     *
282
     * @return void
283
     */
284
    public function setUp($serial)
285
    {
286
287
        // load the status of the actual import
288
        $status = $this->getRegistryProcessor()->getAttribute($serial);
289 18
290
        // load the global data we've prepared initially
291
        $this->linkTypes = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::LINK_TYPES];
292
        $this->categories = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::CATEGORIES];
293 18
        $this->taxClasses = $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::TAX_CLASSES];
294
        $this->imageTypes =  $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::IMAGE_TYPES];
295
        $this->storeWebsites =  $status[RegistryKeys::GLOBAL_DATA][RegistryKeys::STORE_WEBSITES];
296 18
297 18
        // invoke the parent method
298 18
        parent::setUp($serial);
299 18
    }
300
301
302 18
    /**
303
     * Clean up the global data after importing the bunch.
304
     *
305 18
     * @param string $serial The serial of the actual import
306 18
     *
307
     * @return void
308
     */
309
    public function tearDown($serial)
310
    {
311
312
        // invoke the parent method
313
        parent::tearDown($serial);
314
315
        // load the registry processor
316
        $registryProcessor = $this->getRegistryProcessor();
317
318
        // update the status
319
        $registryProcessor->mergeAttributesRecursive(
320
            $serial,
321
            array(
322
                RegistryKeys::FILES => array($this->getFilename() => array(RegistryKeys::STATUS => 1)),
323
                RegistryKeys::SKU_ENTITY_ID_MAPPING => $this->skuEntityIdMapping,
324
                RegistryKeys::SKU_STORE_VIEW_CODE_MAPPING => $this->skuStoreViewCodeMapping
325
            )
326
        );
327
    }
328
329
    /**
330
     * Return's the available image types.
331
     *
332
     * @return array The array with the available image types
333
     */
334
    public function getImageTypes()
335
    {
336
        return $this->imageTypes;
337
    }
338
339
    /**
340
     * Return's the store ID of the actual row, or of the default store
341
     * if no store view code is set in the CSV file.
342
     *
343
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
344
     *
345
     * @return integer The ID of the actual store
346
     * @throws \Exception Is thrown, if the store with the actual code is not available
347
     */
348
    public function getRowStoreId($default = null)
349
    {
350
351
        // initialize the default store view code, if not passed
352
        if ($default == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $default of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
353
            $defaultStore = $this->getDefaultStore();
354
            $default = $defaultStore[MemberNames::CODE];
355
        }
356
357
        // load the store view code the create the product/attributes for
358
        $storeViewCode = $this->getStoreViewCode($default);
359
360
        // query whether or not, the requested store is available
361
        if (isset($this->stores[$storeViewCode])) {
362
            return (integer) $this->stores[$storeViewCode][MemberNames::STORE_ID];
363
        }
364
365
        // throw an exception, if not
366
        throw new \Exception(
367
            $this->appendExceptionSuffix(
368
                sprintf('Found invalid store view code %s', $storeViewCode)
369
            )
370
        );
371
    }
372
373
    /**
374
     * Return's the tax class ID for the passed tax class name.
375
     *
376
     * @param string $taxClassName The tax class name to return the ID for
377
     *
378
     * @return integer The tax class ID
379
     * @throws \Exception Is thrown, if the tax class with the requested name is not available
380
     */
381 View Code Duplication
    public function getTaxClassIdByTaxClassName($taxClassName)
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...
382
    {
383
384
        // query whether or not, the requested tax class is available
385
        if (isset($this->taxClasses[$taxClassName])) {
386
            return (integer) $this->taxClasses[$taxClassName][MemberNames::CLASS_ID];
387
        }
388
389
        // throw an exception, if not
390
        throw new \Exception(
391
            $this->appendExceptionSuffix(
392
                sprintf('Found invalid tax class name %s', $taxClassName)
393
            )
394
        );
395
    }
396
397
    /**
398
     * Return's the store website for the passed code.
399
     *
400
     * @param string $code The code of the store website to return the ID for
401
     *
402
     * @return integer The store website ID
403
     * @throws \Exception Is thrown, if the store website with the requested code is not available
404
     */
405 View Code Duplication
    public function getStoreWebsiteIdByCode($code)
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...
406
    {
407
408
        // query whether or not, the requested store website is available
409
        if (isset($this->storeWebsites[$code])) {
410
            return (integer) $this->storeWebsites[$code][MemberNames::WEBSITE_ID];
411
        }
412
413
        // throw an exception, if not
414
        throw new \Exception(
415
            $this->appendExceptionSuffix(
416
                sprintf('Found invalid website code %s', $code)
417
            )
418
        );
419
    }
420
421
    /**
422
     * Return's the category with the passed path.
423
     *
424
     * @param string $path The path of the category to return
425
     *
426
     * @return array The category
427
     * @throws \Exception Is thrown, if the requested category is not available
428
     */
429
    public function getCategoryByPath($path)
430
    {
431
432
        // query whether or not the category with the passed path exists
433
        if (isset($this->categories[$path])) {
434
            return $this->categories[$path];
435
        }
436
437
        // throw an exception, if not
438
        throw new \Exception(
439
            $this->appendExceptionSuffix(
440
                sprintf('Can\'t find category with path %s', $path)
441
            )
442
        );
443
    }
444
445
    /**
446
     * Return's the category with the passed ID.
447
     *
448
     * @param integer $categoryId The ID of the category to return
449
     *
450
     * @return array The category data
451
     * @throws \Exception Is thrown, if the category is not available
452
     */
453
    public function getCategory($categoryId)
454
    {
455
456
        // try to load the category with the passed ID
457
        foreach ($this->categories as $category) {
458
            if ($category[MemberNames::ENTITY_ID] == $categoryId) {
459
                return $category;
460
            }
461
        }
462
463
        // throw an exception if the category is NOT available
464
        throw new \Exception(
465
            $this->appendExceptionSuffix(
466
                sprintf('Can\'t load category with ID %d', $categoryId)
467
            )
468
        );
469
    }
470
471
    /**
472
     * Return's the root category for the actual view store.
473
     *
474
     * @return array The store's root category
475
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
476
     */
477
    public function getRootCategory()
478
    {
479
480
        // load the default store
481
        $defaultStore = $this->getDefaultStore();
482
483
        // load the actual store view code
484
        $storeViewCode = $this->getStoreViewCode($defaultStore[MemberNames::CODE]);
485
486
        // query weather or not we've a root category or not
487
        if (isset($this->rootCategories[$storeViewCode])) {
488
            return $this->rootCategories[$storeViewCode];
489
        }
490
491
        // throw an exception if the root category is NOT available
492
        throw new \Exception(
493
            $this->appendExceptionSuffix(
494
                sprintf('Root category for %s is not available', $storeViewCode)
495
            )
496
        );
497
    }
498
499
    /**
500
     * Returns an array with the codes of the store views related with the passed website code.
501
     *
502
     * @param string $websiteCode The code of the website to return the store view codes for
503
     *
504
     * @return array The array with the matching store view codes
505
     */
506
    public function getStoreViewCodesByWebsiteCode($websiteCode)
507
    {
508
509
        // query whether or not the website with the passed code exists
510
        if (!isset($this->storeWebsites[$websiteCode])) {
511
            // throw an exception if the website is NOT available
512
            throw new \Exception(
513
                $this->appendExceptionSuffix(
514
                    sprintf('Website with code "%s" is not available', $websiteCode)
515
                )
516
            );
517
        }
518
519
        // initialize the array for the store view codes
520
        $storeViewCodes = array();
521
522
        // load the website ID
523
        $websiteId = (integer) $this->storeWebsites[$websiteCode][MemberNames::WEBSITE_ID];
524
525
        // iterate over the available stores to find the one of the website
526
        foreach ($this->stores as $storeCode => $store) {
527
            if ((integer) $store[MemberNames::WEBSITE_ID] === $websiteId) {
528
                $storeViewCodes[] = $storeCode;
529
            }
530
        }
531
532
        // return the array with the matching store view codes
533
        return $storeViewCodes;
534
    }
535
}
536