Completed
Push — master ( 6a5528...b7892c )
by
unknown
13s
created

UrlRewriteObserver::process()   D

Complexity

Conditions 14
Paths 49

Size

Total Lines 148
Code Lines 75

Duplication

Lines 29
Ratio 19.59 %

Code Coverage

Tests 24
CRAP Score 68.3747

Importance

Changes 0
Metric Value
dl 29
loc 148
ccs 24
cts 69
cp 0.3478
rs 4.9516
c 0
b 0
f 0
cc 14
eloc 75
nc 49
nop 0
crap 68.3747

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * TechDivision\Import\Product\UrlRewrite\Observers\UrlRewriteObserver
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-url-rewrite
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\UrlRewrite\Observers;
22
23
use TechDivision\Import\Utils\StoreViewCodes;
24
use TechDivision\Import\Product\Utils\VisibilityKeys;
25
use TechDivision\Import\Product\Utils\CoreConfigDataKeys;
26
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
27
use TechDivision\Import\Product\UrlRewrite\Utils\ColumnKeys;
28
use TechDivision\Import\Product\UrlRewrite\Utils\MemberNames;
29
use TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface;
30
31
/**
32
 * Observer that creates/updates the product's URL rewrites.
33
 *
34
 * @author    Tim Wagner <[email protected]>
35
 * @copyright 2016 TechDivision GmbH <[email protected]>
36
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
37
 * @link      https://github.com/techdivision/import-product-url-rewrite
38
 * @link      http://www.techdivision.com
39
 */
40
class UrlRewriteObserver extends AbstractProductImportObserver
41
{
42
43
    /**
44
     * The entity type to load the URL rewrites for.
45
     *
46
     * @var string
47
     */
48
    const ENTITY_TYPE = 'product';
49
50
    /**
51
     * The key for the category in the metadata.
52
     *
53
     * @var string
54
     */
55
    const CATEGORY_ID = 'category_id';
56
57
    /**
58
     * The URL key from the CSV file column that has to be processed by the observer.
59
     *
60
     * @var string
61
     */
62
    protected $urlKey;
63
64
    /**
65
     * The actual category ID to process.
66
     *
67
     * @var integer
68
     */
69
    protected $categoryId;
70
71
    /**
72
     * The actual entity ID to process.
73
     *
74
     * @var integer
75
     */
76
    protected $entityId;
77
78
    /**
79
     * The ID of the recently created URL rewrite.
80
     *
81
     * @var integer
82
     */
83
    protected $urlRewriteId;
84
85
    /**
86
     * The array with the URL rewrites that has to be created.
87
     *
88
     * @var array
89
     */
90
    protected $urlRewrites = array();
91
92
    /**
93
     * The array with the category IDs related with the product.
94
     *
95
     * @var array
96
     */
97
    protected $productCategoryIds = array();
98
99
    /**
100
     * The product bunch processor instance.
101
     *
102
     * @var \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface
103
     */
104
    protected $productUrlRewriteProcessor;
105
106
    /**
107
     * Extending classes may need to know, whether currently processed store view is active.
108
     *
109
     * @var bool
110
     */
111
    protected $isStoreViewActive;
112
113
    /**
114
     * Initialize the observer with the passed product URL rewrite processor instance.
115
     *
116
     * @param \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor The product URL rewrite processor instance
117
     */
118 3
    public function __construct(ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor)
119
    {
120 3
        $this->productUrlRewriteProcessor = $productUrlRewriteProcessor;
121 3
    }
122
123
    /**
124
     * Return's the product bunch processor instance.
125
     *
126
     * @return \TechDivision\Import\Product\Services\ProductBunchProcessorInterface The product bunch processor instance
127
     */
128 3
    protected function getProductUrlRewriteProcessor()
129
    {
130 3
        return $this->productUrlRewriteProcessor;
131
    }
132
133
    /**
134
     * Process the observer's business logic.
135
     *
136
     * @return void
137
     * @throws \Exception Is thrown, if the product is not available or no URL key has been specified
138
     */
139 3
    protected function process()
140
    {
141
142
        // try to load the entity ID for the product with the passed SKU
143 3
        if ($product = $this->loadProduct($sku = $this->getValue(ColumnKeys::SKU))) {
144 3
            $this->setLastEntityId($this->entityId = $product[MemberNames::ENTITY_ID]);
145 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
146
            // prepare a log message
147
            $message = sprintf('Product with SKU "%s" can\'t be loaded to create URL rewrites', $sku);
148
            // query whether or not we're in debug mode
149
            if ($this->getSubject()->isDebugMode()) {
150
                $this->getSubject()->getSystemLogger()->warning($message);
151
                return;
152
            } else {
153
                throw new \Exception($message);
154
            }
155
        }
156
157
        // try to load the URL key
158 3
        if ($this->hasValue(ColumnKeys::URL_KEY)) {
159 3
            $this->urlKey = $this->getValue(ColumnKeys::URL_KEY);
160 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
161
            // prepare a log message
162
            $message = sprintf('Can\'t find a value in column "url_key" for product with SKU "%s"', $sku);
163
            // query whether or not we're in debug mode
164
            if ($this->getSubject()->isDebugMode()) {
165
                $this->getSubject()->getSystemLogger()->warning($message);
166
            } else {
167
                return;
168
                throw new \Exception($message);
0 ignored issues
show
Unused Code introduced by
throw new \Exception($message); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
169
            }
170
        }
171
172
        // initialize the store view code
173 3
        $this->getSubject()->prepareStoreViewCode();
174
175
        // load the store view - if no store view has been set we use the admin store view as default
176 3
        $storeViewCode = $this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN);
177
178
        // query whether or not the row has already been processed
179 3
        if ($this->storeViewHasBeenProcessed($sku, $storeViewCode)) {
180
            // log a message
181
            $this->getSubject()
182
                ->getSystemLogger()
183
                ->debug(
184
                    sprintf(
185
                        'URL rewrites for SKU "%s" + store view code "%s" has already been processed',
186
                        $sku,
187
                        $storeViewCode
188
                    )
189
                );
190
191
            // return without creating any rewrites
192
            return;
193
        };
194
195
        // stop processing as we don't want to create URL rewrites for the admin store view
196 3
        if ($storeViewCode === StoreViewCodes::ADMIN) {
197
            // log a message and return
198
            $this->getSubject()
199
                ->getSystemLogger()
200
                ->debug(
201
                    sprintf(
202
                        'Store with code "%s" is not active, no URL rewrites will be created for product with SKU "%s"',
203
                        $storeViewCode,
204
                        $sku
205
                    )
206
                );
207
208
            // return without creating any rewrites
209
            return;
210
        }
211
212
        // stop processing if the store is NOT active
213 3
        $this->isStoreViewActive = $this->getSubject()->storeIsActive($storeViewCode);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method storeIsActive() does only exist in the following implementations of said interface: TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
214 3
        if (!$this->isStoreViewActive) {
215
            // log a message and return
216
            $this->getSubject()
217
                ->getSystemLogger()
218
                ->debug(
219
                    sprintf(
220
                        'Store with code "%s" is not active, no URL rewrites will be created for product with SKU "%s"',
221
                        $storeViewCode,
222
                        $sku
223
                    )
224
                );
225
226
            // return without creating any rewrites
227
            return;
228
        }
229
230
        // only map the visibility for the product row related to the default store view
231 3
        if (!$this->hasBeenProcessed($sku)) {
232 3
            $this->addEntityIdVisibilityIdMapping($this->getValue(ColumnKeys::VISIBILITY));
233
        }
234
235
        // do NOT create new URL rewrites, if the product is NOT visible (any more), BUT
236
        // handle existing URL rewrites, e. g. to remove and clean up the URL rewrites
237 3
        if (!$this->isVisible()) {
238
            // log a message
239
            $this->getSubject()
240
                ->getSystemLogger()
241
                ->debug(
242
                    sprintf(
243
                        'Product with SKU "%s" is not visible, so no URL rewrites will be created',
244
                        $sku
245
                    )
246
                );
247
248
            // return without creating any rewrites
249
            return;
250
        }
251
252
        // prepare the URL rewrites
253 3
        $this->prepareUrlRewrites();
254
255
        // iterate over the categories and create the URL rewrites
256 3
        foreach ($this->urlRewrites as $categoryId => $urlRewrite) {
257
            // initialize and persist the URL rewrite
258 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
259
                // initialize URL rewrite and catagory ID
260 3
                $this->categoryId = $categoryId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $categoryId can also be of type string. However, the property $categoryId is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
261
262
                try {
263
                    // persist the URL rewrite
264 3
                    $this->urlRewriteId = $this->persistUrlRewrite($urlRewrite);
0 ignored issues
show
Documentation Bug introduced by
The property $urlRewriteId was declared of type integer, but $this->persistUrlRewrite($urlRewrite) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
265
266
                    // initialize and persist the URL rewrite product => category relation
267 3
                    $urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory(
268 3
                        $this->prepareUrlRewriteProductCategoryAttributes()
269
                    );
270
271
                    // persist the URL rewrite product category relation
272 3
                    $this->persistUrlRewriteProductCategory($urlRewriteProductCategory);
273
274
                } catch (\Exception $e) {
275
                    // query whether or not debug mode has been enabled
276 View Code Duplication
                    if ($this->getSubject()->isDebugMode()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
277
                        $this->getSubject()
278
                            ->getSystemLogger()
279
                            ->warning($this->getSubject()->appendExceptionSuffix($e->getMessage()));
280
                    } else {
281 3
                        throw $e;
282
                    }
283
                }
284
            }
285
        }
286 3
    }
287
288
    /**
289
     * Initialize the category product with the passed attributes and returns an instance.
290
     *
291
     * @param array $attr The category product attributes
292
     *
293
     * @return array The initialized category product
294
     */
295 2
    protected function initializeUrlRewrite(array $attr)
296
    {
297 2
        return $attr;
298
    }
299
300
    /**
301
     * Initialize the URL rewrite product => category relation with the passed attributes
302
     * and returns an instance.
303
     *
304
     * @param array $attr The URL rewrite product => category relation attributes
305
     *
306
     * @return array The initialized URL rewrite product => category relation
307
     */
308 2
    protected function initializeUrlRewriteProductCategory($attr)
309
    {
310 2
        return $attr;
311
    }
312
313
    /**
314
     * Prepare's the URL rewrites that has to be created/updated.
315
     *
316
     * @return void
317
     */
318 3
    protected function prepareUrlRewrites()
319
    {
320
321
        // (re-)initialize the array for the URL rewrites and the product category IDs
322 3
        $this->urlRewrites = array();
323 3
        $this->productCategoryIds = array();
324
325
        // load the root category, because we need that to create the default product URL rewrite
326 3
        $rootCategory = $this->getRootCategory();
327
328
        // at least, add the root category ID to the category => product relations
329 3
        $this->productCategoryIds[] = $rootCategory[MemberNames::ENTITY_ID];
330
331
        // query whether or not categories has to be used as product URL suffix
332 3
        if ($this->getSubject()->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_USE_CATEGORIES, false)) {
333
            // append the category => product relations found
334 3
            foreach ($this->getValue(ColumnKeys::CATEGORIES, array(), array($this, 'explode')) as $path) {
335
                try {
336
                    // try to load the category for the given path
337 2
                    $category = $this->getCategoryByPath(trim($path));
338
                    // resolve the product's categories recursively
339 2
                    $this->resolveCategoryIds($category[MemberNames::ENTITY_ID], true);
340
341
                } catch (\Exception $e) {
342
                    // query whether or not debug mode has been enabled
343 View Code Duplication
                    if ($this->getSubject()->isDebugMode()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
344
                        $this->getSubject()
345
                            ->getSystemLogger()
346
                            ->warning($this->getSubject()->appendExceptionSuffix($e->getMessage()));
347
                    } else {
348 2
                        throw $e;
349
                    }
350
                }
351
            }
352
        }
353
354
        // prepare the URL rewrites
355 3
        foreach ($this->productCategoryIds as $categoryId) {
356
            // set the category ID
357 3
            $this->categoryId = $categoryId;
358
359
            // prepare the attributes for each URL rewrite
360 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
361
        }
362 3
    }
363
364
    /**
365
     * Resolve's the parent categories of the category with the passed ID and relate's
366
     * it with the product with the passed ID, if the category is top level OR has the
367
     * anchor flag set.
368
     *
369
     * @param integer $categoryId The ID of the category to resolve the parents
370
     * @param boolean $topLevel   TRUE if the passed category has top level, else FALSE
371
     *
372
     * @return void
373
     */
374 2
    protected function resolveCategoryIds($categoryId, $topLevel = false)
375
    {
376
377
        // return immediately if this is the absolute root node
378 2
        if ((integer) $categoryId === 1) {
379 2
            return;
380
        }
381
382
        // load the data of the category with the passed ID
383 2
        $category = $this->getCategory($categoryId);
384
385
        // query whether or not the product has already been related
386 2
        if (!in_array($categoryId, $this->productCategoryIds)) {
387 2
            if ($topLevel) {
388
                // relate it, if the category is top level
389 2
                $this->productCategoryIds[] = $categoryId;
390 2
            } elseif ((integer) $category[MemberNames::IS_ANCHOR] === 1) {
391
                // also relate it, if the category is not top level, but has the anchor flag set
392
                $this->productCategoryIds[] = $categoryId;
393
            } else {
394 2
                $this->getSubject()
395 2
                    ->getSystemLogger()
396 2
                    ->debug(sprintf('Don\'t create URL rewrite for category "%s" because of missing anchor flag', $category[MemberNames::PATH]));
397
            }
398
        }
399
400
        // load the root category
401 2
        $rootCategory = $this->getRootCategory();
402
403
        // try to resolve the parent category IDs
404 2
        if ($rootCategory[MemberNames::ENTITY_ID] !== ($parentId = $category[MemberNames::PARENT_ID])) {
405 2
            $this->resolveCategoryIds($parentId, false);
406
        }
407 2
    }
408
409
    /**
410
     * Prepare the attributes of the entity that has to be persisted.
411
     *
412
     * @return array The prepared attributes
413
     */
414 3
    protected function prepareAttributes()
415
    {
416
417
        // load the store ID to use
418 3
        $storeId = $this->getSubject()->getRowStoreId();
0 ignored issues
show
Bug introduced by
The method getRowStoreId() does not exist on TechDivision\Import\Subjects\SubjectInterface. Did you maybe mean getRow()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
419
420
        // load the category to create the URL rewrite for
421 3
        $category = $this->getCategory($this->categoryId);
422
423
        // initialize the values
424 3
        $requestPath = $this->prepareRequestPath($category);
425 3
        $targetPath = $this->prepareTargetPath($category);
426 3
        $metadata = serialize($this->prepareMetadata($category));
427
428
        // return the prepared URL rewrite
429 3
        return $this->initializeEntity(
430
            array(
431 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
432 3
                MemberNames::ENTITY_ID        => $this->entityId,
433 3
                MemberNames::REQUEST_PATH     => $requestPath,
434 3
                MemberNames::TARGET_PATH      => $targetPath,
435 3
                MemberNames::REDIRECT_TYPE    => 0,
436 3
                MemberNames::STORE_ID         => $storeId,
437 3
                MemberNames::DESCRIPTION      => null,
438 3
                MemberNames::IS_AUTOGENERATED => 1,
439 3
                MemberNames::METADATA         => $metadata
440
            )
441
        );
442
    }
443
444
    /**
445
     * Prepare's the URL rewrite product => category relation attributes.
446
     *
447
     * @return array The prepared attributes
448
     */
449 3
    protected function prepareUrlRewriteProductCategoryAttributes()
450
    {
451
452
        // return the prepared product
453 3
        return $this->initializeEntity(
454
            array(
455 3
                MemberNames::PRODUCT_ID => $this->entityId,
456 3
                MemberNames::CATEGORY_ID => $this->categoryId,
457 3
                MemberNames::URL_REWRITE_ID => $this->urlRewriteId
458
            )
459
        );
460
    }
461
462
    /**
463
     * Prepare's the target path for a URL rewrite.
464
     *
465
     * @param array $category The categroy with the URL path
466
     *
467
     * @return string The target path
468
     */
469 3
    protected function prepareTargetPath(array $category)
470
    {
471
472
        // query whether or not, the category is the root category
473 3
        if ($this->isRootCategory($category)) {
474 3
            $targetPath = sprintf('catalog/product/view/id/%d', $this->entityId);
475
        } else {
476 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $this->entityId, $category[MemberNames::ENTITY_ID]);
477
        }
478
479
        // return the target path
480 3
        return $targetPath;
481
    }
482
483
    /**
484
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
485
     *
486
     * @param array $category The categroy with the URL path
487
     *
488
     * @return string The request path
489
     * @throws \RuntimeException Is thrown, if the passed category has no or an empty value for attribute "url_path"
490
     */
491 3
    protected function prepareRequestPath(array $category)
492
    {
493
494
        // load the product URL suffix to use
495 3
        $urlSuffix = $this->getSubject()->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_URL_SUFFIX, '.html');
496
497
        // query whether or not, the category is the root category
498 3
        if ($this->isRootCategory($category)) {
499 3
            return sprintf('%s%s', $this->urlKey, $urlSuffix);
500
        } else {
501
            // query whether or not the category's "url_path" attribute, necessary to create a valid "request_path", is available
502 2
            if (isset($category[MemberNames::URL_PATH]) && $category[MemberNames::URL_PATH]) {
503 2
                return sprintf('%s/%s%s', $category[MemberNames::URL_PATH], $this->urlKey, $urlSuffix);
504
            }
505
        }
506
507
        // throw an exception if the category's "url_path" attribute is NOT available
508
        throw new \RuntimeException(
509
            sprintf(
510
                'Can\'t find mandatory attribute "%s" for category ID "%d", necessary to build a valid "request_path"',
511
                MemberNames::URL_PATH,
512
                $category[MemberNames::ENTITY_ID]
513
            )
514
        );
515
    }
516
517
    /**
518
     * Prepare's the URL rewrite's metadata with the passed category values.
519
     *
520
     * @param array $category The category used for preparation
521
     *
522
     * @return array The metadata
523
     */
524 3
    protected function prepareMetadata(array $category)
525
    {
526
527
        // initialize the metadata
528 3
        $metadata = array();
529
530
        // query whether or not, the passed category IS the root category
531 3
        if ($this->isRootCategory($category)) {
532 3
            return $metadata;
533
        }
534
535
        // if not, set the category ID in the metadata
536 2
        $metadata[UrlRewriteObserver::CATEGORY_ID] = (integer) $category[MemberNames::ENTITY_ID];
537
538
        // return the metadata
539 2
        return $metadata;
540
    }
541
542
    /**
543
     * Query whether or not the actual entity is visible.
544
     *
545
     * @return boolean TRUE if the entity is visible, else FALSE
546
     */
547 3
    protected function isVisible()
548
    {
549 3
        return $this->getEntityIdVisibilityIdMapping() !== VisibilityKeys::VISIBILITY_NOT_VISIBLE;
550
    }
551
552
    /**
553
     * Return's the visibility for the passed entity ID, if it already has been mapped. The mapping will be created
554
     * by calling <code>\TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue</code> which will
555
     * be done by the <code>\TechDivision\Import\Product\Callbacks\VisibilityCallback</code>.
556
     *
557
     * @return integer The visibility ID
558
     * @throws \Exception Is thrown, if the entity ID has not been mapped
559
     * @see \TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue()
560
     */
561 3
    protected function getEntityIdVisibilityIdMapping()
562
    {
563 3
        return $this->getSubject()->getEntityIdVisibilityIdMapping();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getEntityIdVisibilityIdMapping() does only exist in the following implementations of said interface: TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
564
    }
565
566
    /**
567
     * Return's the root category for the actual view store.
568
     *
569
     * @return array The store's root category
570
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
571
     */
572 3
    protected function getRootCategory()
573
    {
574 3
        return $this->getSubject()->getRootCategory();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getRootCategory() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject, TechDivision\Import\Subjects\AbstractEavSubject, TechDivision\Import\Subjects\AbstractSubject, TechDivision\Import\Subjects\MoveFilesSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
575
    }
576
577
    /**
578
     * Return's TRUE if the passed category IS the root category, else FALSE.
579
     *
580
     * @param array $category The category to query
581
     *
582
     * @return boolean TRUE if the passed category IS the root category
583
     */
584 3
    protected function isRootCategory(array $category)
585
    {
586
587
        // load the root category
588 3
        $rootCategory = $this->getRootCategory();
589
590
        // compare the entity IDs and return the result
591 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
592
    }
593
594
    /**
595
     * Return's the category with the passed path.
596
     *
597
     * @param string $path The path of the category to return
598
     *
599
     * @return array The category
600
     */
601 2
    protected function getCategoryByPath($path)
602
    {
603 2
        return $this->getSubject()->getCategoryByPath($path);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getCategoryByPath() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
604
    }
605
606
    /**
607
     * Return's the category with the passed ID.
608
     *
609
     * @param integer $categoryId The ID of the category to return
610
     *
611
     * @return array The category data
612
     */
613 2
    protected function getCategory($categoryId)
614
    {
615 2
        return $this->getSubject()->getCategory($categoryId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method getCategory() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
616
    }
617
618
    /**
619
     * Persist's the URL rewrite with the passed data.
620
     *
621
     * @param array $row The URL rewrite to persist
622
     *
623
     * @return string The ID of the persisted entity
624
     */
625 3
    protected function persistUrlRewrite($row)
626
    {
627 3
        return $this->getProductUrlRewriteProcessor()->persistUrlRewrite($row);
628
    }
629
630
    /**
631
     * Persist's the URL rewrite product => category relation with the passed data.
632
     *
633
     * @param array $row The URL rewrite product => category relation to persist
634
     *
635
     * @return void
636
     */
637 3
    protected function persistUrlRewriteProductCategory($row)
638
    {
639 3
        return $this->getProductUrlRewriteProcessor()->persistUrlRewriteProductCategory($row);
640
    }
641
642
    /**
643
     * Queries whether or not the passed SKU and store view code has already been processed.
644
     *
645
     * @param string $sku           The SKU to check been processed
646
     * @param string $storeViewCode The store view code to check been processed
647
     *
648
     * @return boolean TRUE if the SKU and store view code has been processed, else FALSE
649
     */
650 3
    protected function storeViewHasBeenProcessed($sku, $storeViewCode)
651
    {
652 3
        return $this->getSubject()->storeViewHasBeenProcessed($sku, $storeViewCode);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method storeViewHasBeenProcessed() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
653
    }
654
655
    /**
656
     * Add the entity ID => visibility mapping for the actual entity ID.
657
     *
658
     * @param string $visibility The visibility of the actual entity to map
659
     *
660
     * @return void
661
     */
662 3
    protected function addEntityIdVisibilityIdMapping($visibility)
663
    {
664 3
        $this->getSubject()->addEntityIdVisibilityIdMapping($visibility);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method addEntityIdVisibilityIdMapping() does only exist in the following implementations of said interface: TechDivision\Import\Prod...jects\UrlRewriteSubject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
665 3
    }
666
667
    /**
668
     * Set's the ID of the product that has been created recently.
669
     *
670
     * @param string $lastEntityId The entity ID
671
     *
672
     * @return void
673
     */
674 3
    protected function setLastEntityId($lastEntityId)
675
    {
676 3
        $this->getSubject()->setLastEntityId($lastEntityId);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Subjects\SubjectInterface as the method setLastEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, TechDivision\Import\Prod...jects\UrlRewriteSubject, TechDivision\Import\Subjects\ExportableTraitImpl.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
677 3
    }
678
679
    /**
680
     * Load's and return's the product with the passed SKU.
681
     *
682
     * @param string $sku The SKU of the product to load
683
     *
684
     * @return array The product
685
     */
686 3
    protected function loadProduct($sku)
687
    {
688 3
        return $this->getProductUrlRewriteProcessor()->loadProduct($sku);
689
    }
690
}
691