Completed
Pull Request — master (#87)
by Tim
03:03
created

UrlRewriteObserver::process()   C

Complexity

Conditions 10
Paths 26

Size

Total Lines 87
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 15.8818

Importance

Changes 0
Metric Value
dl 0
loc 87
ccs 22
cts 36
cp 0.6111
rs 5.2688
c 0
b 0
f 0
cc 10
eloc 38
nc 26
nop 0
crap 15.8818

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\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
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Product\Observers;
22
23
use TechDivision\Import\Utils\StoreViewCodes;
24
use TechDivision\Import\Product\Utils\ColumnKeys;
25
use TechDivision\Import\Product\Utils\MemberNames;
26
use TechDivision\Import\Product\Utils\VisibilityKeys;
27
use TechDivision\Import\Product\Utils\CoreConfigDataKeys;
28
use TechDivision\Import\Product\Services\ProductBunchProcessorInterface;
29
30
/**
31
 * Observer that creates/updates the product's URL rewrites.
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
class UrlRewriteObserver extends AbstractProductImportObserver
40
{
41
42
    /**
43
     * The entity type to load the URL rewrites for.
44
     *
45
     * @var string
46
     */
47
    const ENTITY_TYPE = 'product';
48
49
    /**
50
     * The key for the category in the metadata.
51
     *
52
     * @var string
53
     */
54
    const CATEGORY_ID = 'category_id';
55
56
    /**
57
     * The URL key from the CSV file column that has to be processed by the observer.
58
     *
59
     * @var string
60
     */
61
    protected $urlKey;
62
63
    /**
64
     * The actual category ID to process.
65
     *
66
     * @var integer
67
     */
68
    protected $categoryId;
69
70
    /**
71
     * The actual entity ID to process.
72
     *
73
     * @var integer
74
     */
75
    protected $entityId;
76
77
    /**
78
     * The ID of the recently created URL rewrite.
79
     *
80
     * @var integer
81
     */
82
    protected $urlRewriteId;
83
84
    /**
85
     * The array with the URL rewrites that has to be created.
86
     *
87
     * @var array
88
     */
89
    protected $urlRewrites = array();
90
91
    /**
92
     * The array with the category IDs related with the product.
93
     *
94
     * @var array
95
     */
96
    protected $productCategoryIds = array();
97
98
    /**
99
     * The product bunch processor instance.
100
     *
101
     * @var \TechDivision\Import\Product\Services\ProductBunchProcessorInterface
102
     */
103
    protected $productBunchProcessor;
104
105
    /**
106
     * Initialize the observer with the passed product bunch processor instance.
107
     *
108
     * @param \TechDivision\Import\Product\Services\ProductBunchProcessorInterface $productBunchProcessor The product bunch processor instance
109
     */
110 3
    public function __construct(ProductBunchProcessorInterface $productBunchProcessor)
111
    {
112 3
        $this->productBunchProcessor = $productBunchProcessor;
113 3
    }
114
115
    /**
116
     * Return's the product bunch processor instance.
117
     *
118
     * @return \TechDivision\Import\Product\Services\ProductBunchProcessorInterface The product bunch processor instance
119
     */
120 3
    protected function getProductBunchProcessor()
121
    {
122 3
        return $this->productBunchProcessor;
123
    }
124
125
    /**
126
     * Process the observer's business logic.
127
     *
128
     * @return void
129
     */
130 3
    protected function process()
131
    {
132
133
        // initialize the store view code
134 3
        $this->getSubject()->prepareStoreViewCode();
135
136
        // load the SKU
137 3
        $sku = $this->getValue($this->getPrimaryKeyColumnName());
138
139
        // load the store view - if no store view has been set we use the admin store view as default
140 3
        $storeViewCode = $this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN);
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 getStoreViewCode() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, 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...
141
142
        // only map the visibility of the product with the default store view
143 3
        if (!$this->hasBeenProcessed($sku)) {
144 3
            $this->addEntityIdVisibilityIdMapping($this->getValue(ColumnKeys::VISIBILITY));
145
        }
146
147
        // query whether or not we're in the admin store view
148 3
        if ($storeViewCode === StoreViewCodes::ADMIN) {
149
            // stop processing as we don't want to create URL rewrites for the admin store view
150
            return;
151
        }
152
153
        // query whether or not the row has already been processed
154 3
        if ($this->storeViewHasBeenProcessed($sku, $storeViewCode)) {
155
            // log a message
156
            $this->getSubject()
157
                 ->getSystemLogger()
158
                 ->warning(
159
                     sprintf(
160
                         'URL rewrites for SKU "%s" + store view code "%s" has already been processed',
161
                         $sku,
162
                         $storeViewCode
163
                     )
164
                 );
165
166
            // return immediately
167
            return;
168
        };
169
170
        // try to load the URL key, return immediately if not possible
171 3
        if ($this->hasValue(ColumnKeys::URL_KEY)) {
172 3
            $this->urlKey = $this->getValue(ColumnKeys::URL_KEY);
173
        } else {
174
            return;
175
        }
176
177
        // prepare the URL rewrites
178 3
        $this->prepareUrlRewrites();
179
180
        // do NOT create new URL rewrites, if the product is NOT visible (any more), BUT
181
        // handle existing URL rewrites, e. g. to remove and clean up the URL rewrites
182 3
        if ($this->isNotVisible()) {
183
            return;
184
        }
185
186
        // iterate over the categories and create the URL rewrites
187 3
        foreach ($this->urlRewrites as $categoryId => $urlRewrite) {
188
            // initialize and persist the URL rewrite
189 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
190
                // initialize URL rewrite and entity ID
191 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...
192 3
                $this->entityId = $urlRewrite[MemberNames::ENTITY_ID];
193
194
                try {
195
                    // persist the URL rewrite
196 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...
197
198
                    // initialize and persist the URL rewrite product => category relation
199 3
                    $urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory(
200 3
                        $this->prepareUrlRewriteProductCategoryAttributes()
201
                    );
202
203
                    // persist the URL rewrite product category relation
204 3
                    $this->persistUrlRewriteProductCategory($urlRewriteProductCategory);
205
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
206
                } catch (\Exception $e) {
207
                    // query whether or not debug mode has been enabled
208
                    if ($this->getSubject()->isDebugMode()) {
209
                        $this->getSubject()->getSystemLogger()->warning($this->getSubject()->appendExceptionSuffix($e->getMessage()));
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 appendExceptionSuffix() does only exist in the following implementations of said interface: TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, 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...
210
                    } else {
211 3
                        throw $e;
212
                    }
213
                }
214
            }
215
        }
216 3
    }
217
218
    /**
219
     * Initialize the category product with the passed attributes and returns an instance.
220
     *
221
     * @param array $attr The category product attributes
222
     *
223
     * @return array The initialized category product
224
     */
225 2
    protected function initializeUrlRewrite(array $attr)
226
    {
227 2
        return $attr;
228
    }
229
230
    /**
231
     * Initialize the URL rewrite product => category relation with the passed attributes
232
     * and returns an instance.
233
     *
234
     * @param array $attr The URL rewrite product => category relation attributes
235
     *
236
     * @return array The initialized URL rewrite product => category relation
237
     */
238 2
    protected function initializeUrlRewriteProductCategory($attr)
239
    {
240 2
        return $attr;
241
    }
242
243
    /**
244
     * Prepare's the URL rewrites that has to be created/updated.
245
     *
246
     * @return void
247
     */
248 3
    protected function prepareUrlRewrites()
249
    {
250
251
        // (re-)initialize the array for the URL rewrites and the product category IDs
252 3
        $this->urlRewrites = array();
253 3
        $this->productCategoryIds = array();
254
255
        // load the root category, because we need that to create the default product URL rewrite
256 3
        $rootCategory = $this->getRootCategory();
257
258
        // query whether or not categories has to be used as product URL suffix
259 3
        if ($this->getSubject()->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_USE_CATEGORIES, false)) {
260
            // if yes, add the category IDs of the products
261 3
            foreach ($this->getProductCategoryIds() as $categoryId => $entityId) {
262 3
                $this->resolveCategoryIds($categoryId, $entityId, true);
263
            }
264
        }
265
266
        // at least, add the root category ID to the category => product relations
267 3
        $this->productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getSubject()->getLastEntityId();
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 getLastEntityId() does only exist in the following implementations of said interface: TechDivision\Import\Observers\EntitySubjectImpl, TechDivision\Import\Plugins\ExportableSubjectImpl, TechDivision\Import\Prod...\AbstractProductSubject, TechDivision\Import\Product\Subjects\BunchSubject, 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...
268
269
        // prepare the URL rewrites
270 3
        foreach ($this->productCategoryIds as $categoryId => $entityId) {
271
            // set category/entity ID
272 3
            $this->entityId = $entityId;
273 3
            $this->categoryId = $categoryId;
274
275
            // prepare the attributes for each URL rewrite
276 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
277
        }
278 3
    }
279
280
    /**
281
     * Resolve's the parent categories of the category with the passed ID and relate's
282
     * it with the product with the passed ID, if the category is top level OR has the
283
     * anchor flag set.
284
     *
285
     * @param integer $categoryId The ID of the category to resolve the parents
286
     * @param integer $entityId   The ID of the product entity to relate the category with
287
     * @param boolean $topLevel   TRUE if the passed category has top level, else FALSE
288
     *
289
     * @return void
290
     */
291 3
    protected function resolveCategoryIds($categoryId, $entityId, $topLevel = false)
292
    {
293
294
        // return immediately if this is the absolute root node
295 3
        if ((integer) $categoryId === 1) {
296 3
            return;
297
        }
298
299
        // load the data of the category with the passed ID
300 3
        $category = $this->getCategory($categoryId);
301
302
        // query whether or not the product has already been related
303 3
        if (!isset($this->productCategoryIds[$categoryId])) {
304 3
            if ($topLevel) {
305
                // relate it, if the category is top level
306 3
                $this->productCategoryIds[$categoryId] = $entityId;
307
            } elseif ((integer) $category[MemberNames::IS_ANCHOR] === 1) {
308
                // also relate it, if the category is not top level, but has the anchor flag set
309
                $this->productCategoryIds[$categoryId] = $entityId;
310
            } else {
311
                $this->getSubject()
312
                     ->getSystemLogger()
313
                     ->debug(sprintf('Don\'t create URL rewrite for category "%s" because of missing anchor flag', $category[MemberNames::PATH]));
314
            }
315
        }
316
317
        // load the root category
318 3
        $rootCategory = $this->getRootCategory();
319
320
        // try to resolve the parent category IDs
321 3
        if ($rootCategory[MemberNames::ENTITY_ID] !== ($parentId = $category[MemberNames::PARENT_ID])) {
322 3
            $this->resolveCategoryIds($parentId, $entityId, false);
323
        }
324 3
    }
325
326
    /**
327
     * Prepare the attributes of the entity that has to be persisted.
328
     *
329
     * @return array The prepared attributes
330
     */
331 3
    protected function prepareAttributes()
332
    {
333
334
        // load the store ID to use
335 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...
336
337
        // load the category to create the URL rewrite for
338 3
        $category = $this->getCategory($this->categoryId);
339
340
        // initialize the values
341 3
        $requestPath = $this->prepareRequestPath($category);
342 3
        $targetPath = $this->prepareTargetPath($category);
343 3
        $metadata = serialize($this->prepareMetadata($category));
344
345
        // return the prepared URL rewrite
346 3
        return $this->initializeEntity(
347
            array(
348 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
349 3
                MemberNames::ENTITY_ID        => $this->entityId,
350 3
                MemberNames::REQUEST_PATH     => $requestPath,
351 3
                MemberNames::TARGET_PATH      => $targetPath,
352 3
                MemberNames::REDIRECT_TYPE    => 0,
353 3
                MemberNames::STORE_ID         => $storeId,
354 3
                MemberNames::DESCRIPTION      => null,
355 3
                MemberNames::IS_AUTOGENERATED => 1,
356 3
                MemberNames::METADATA         => $metadata
357
            )
358
        );
359
    }
360
361
    /**
362
     * Prepare's the URL rewrite product => category relation attributes.
363
     *
364
     * @return array The prepared attributes
365
     */
366 3
    protected function prepareUrlRewriteProductCategoryAttributes()
367
    {
368
369
        // return the prepared product
370 3
        return $this->initializeEntity(
371
            array(
372 3
                MemberNames::PRODUCT_ID => $this->entityId,
373 3
                MemberNames::CATEGORY_ID => $this->categoryId,
374 3
                MemberNames::URL_REWRITE_ID => $this->urlRewriteId
375
            )
376
        );
377
    }
378
379
    /**
380
     * Prepare's the target path for a URL rewrite.
381
     *
382
     * @param array $category The categroy with the URL path
383
     *
384
     * @return string The target path
385
     */
386 3
    protected function prepareTargetPath(array $category)
387
    {
388
389
        // load the actual entity ID
390 3
        $lastEntityId = $this->getLastEntityId();
0 ignored issues
show
Deprecated Code introduced by
The method TechDivision\Import\Obse...rver::getLastEntityId() has been deprecated with message: Will be removed with version 1.0.0, use subject method instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
391
392
        // query whether or not, the category is the root category
393 3
        if ($this->isRootCategory($category)) {
394 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
395
        } else {
396 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
397
        }
398
399
        // return the target path
400 3
        return $targetPath;
401
    }
402
403
    /**
404
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
405
     *
406
     * @param array $category The categroy with the URL path
407
     *
408
     * @return string The request path
409
     * @throws \RuntimeException Is thrown, if the passed category has no or an empty value for attribute "url_path"
410
     */
411 3
    protected function prepareRequestPath(array $category)
412
    {
413
414
        // load the product URL suffix to use
415 3
        $urlSuffix = $this->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_URL_SUFFIX, '.html');
0 ignored issues
show
Deprecated Code introduced by
The method TechDivision\Import\Obse...er::getCoreConfigData() has been deprecated with message: Will be removed with version 1.0.0, use subject method instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
416
417
        // query whether or not, the category is the root category
418 3
        if ($this->isRootCategory($category)) {
419 3
            return sprintf('%s%s', $this->urlKey, $urlSuffix);
420
        } else {
421
            // query whether or not the category's "url_path" attribute, necessary to create a valid "request_path", is available
422 2
            if (isset($category[MemberNames::URL_PATH]) && $category[MemberNames::URL_PATH]) {
423 2
                return sprintf('%s/%s%s', $category[MemberNames::URL_PATH], $this->urlKey, $urlSuffix);
424
            }
425
        }
426
427
        // throw an exception if the category's "url_path" attribute is NOT available
428
        throw new \RuntimeException(
429
            sprintf(
430
                'Can\'t find mandatory attribute "%s" for category ID "%d", necessary to build a valid "request_path"',
431
                MemberNames::URL_PATH,
432
                $category[MemberNames::ENTITY_ID]
433
            )
434
        );
435
    }
436
437
    /**
438
     * Prepare's the URL rewrite's metadata with the passed category values.
439
     *
440
     * @param array $category The category used for preparation
441
     *
442
     * @return array The metadata
443
     */
444 3
    protected function prepareMetadata(array $category)
445
    {
446
447
        // initialize the metadata
448 3
        $metadata = array();
449
450
        // query whether or not, the passed category IS the root category
451 3
        if ($this->isRootCategory($category)) {
452 3
            return $metadata;
453
        }
454
455
        // if not, set the category ID in the metadata
456 2
        $metadata[UrlRewriteObserver::CATEGORY_ID] = $category[MemberNames::ENTITY_ID];
457
458
        // return the metadata
459 2
        return $metadata;
460
    }
461
462
    /**
463
     * Query whether or not the actual entity is visible or not.
464
     *
465
     * @return boolean TRUE if the entity is NOT visible, else FALSE
466
     */
467 3
    protected function isNotVisible()
468
    {
469 3
        return $this->getEntityIdVisibilityIdMapping() === VisibilityKeys::VISIBILITY_NOT_VISIBLE;
470
    }
471
472
    /**
473
     * Return's the visibility for the passed entity ID, if it already has been mapped. The mapping will be created
474
     * by calling <code>\TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue</code> which will
475
     * be done by the <code>\TechDivision\Import\Product\Callbacks\VisibilityCallback</code>.
476
     *
477
     * @return integer The visibility ID
478
     * @throws \Exception Is thrown, if the entity ID has not been mapped
479
     * @see \TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue()
480
     */
481 3
    protected function getEntityIdVisibilityIdMapping()
482
    {
483 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\Product\Subjects\BunchSubject.

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...
484
    }
485
486
    /**
487
     * Return's the root category for the actual view store.
488
     *
489
     * @return array The store's root category
490
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
491
     */
492 3
    protected function getRootCategory()
493
    {
494 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\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...
495
    }
496
497
    /**
498
     * Return's TRUE if the passed category IS the root category, else FALSE.
499
     *
500
     * @param array $category The category to query
501
     *
502
     * @return boolean TRUE if the passed category IS the root category
503
     */
504 3
    protected function isRootCategory(array $category)
505
    {
506
507
        // load the root category
508 3
        $rootCategory = $this->getRootCategory();
509
510
        // compare the entity IDs and return the result
511 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
512
    }
513
514
    /**
515
     * Return's the list with category IDs the product is related with.
516
     *
517
     * @return array The product's category IDs
518
     */
519 3
    protected function getProductCategoryIds()
520
    {
521 3
        return $this->getSubject()->getProductCategoryIds();
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 getProductCategoryIds() does only exist in the following implementations of said interface: TechDivision\Import\Product\Subjects\BunchSubject.

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...
522
    }
523
524
    /**
525
     * Return's the category with the passed ID.
526
     *
527
     * @param integer $categoryId The ID of the category to return
528
     *
529
     * @return array The category data
530
     */
531 2
    protected function getCategory($categoryId)
532
    {
533 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.

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...
534
    }
535
536
    /**
537
     * Persist's the URL rewrite with the passed data.
538
     *
539
     * @param array $row The URL rewrite to persist
540
     *
541
     * @return string The ID of the persisted entity
542
     */
543 3
    protected function persistUrlRewrite($row)
544
    {
545 3
        return $this->getProductBunchProcessor()->persistUrlRewrite($row);
546
    }
547
548
    /**
549
     * Persist's the URL rewrite product => category relation with the passed data.
550
     *
551
     * @param array $row The URL rewrite product => category relation to persist
552
     *
553
     * @return void
554
     */
555 3
    protected function persistUrlRewriteProductCategory($row)
556
    {
557 3
        return $this->getProductBunchProcessor()->persistUrlRewriteProductCategory($row);
558
    }
559
560
    /**
561
     * Return's the column name that contains the primary key.
562
     *
563
     * @return string the column name that contains the primary key
564
     */
565 3
    protected function getPrimaryKeyColumnName()
566
    {
567 3
        return ColumnKeys::SKU;
568
    }
569
570
    /**
571
     * Queries whether or not the passed SKU and store view code has already been processed.
572
     *
573
     * @param string $sku           The SKU to check been processed
574
     * @param string $storeViewCode The store view code to check been processed
575
     *
576
     * @return boolean TRUE if the SKU and store view code has been processed, else FALSE
577
     */
578 3
    protected function storeViewHasBeenProcessed($sku, $storeViewCode)
579
    {
580 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.

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...
581
    }
582
583
    /**
584
     * Add the entity ID => visibility mapping for the actual entity ID.
585
     *
586
     * @param string $visibility The visibility of the actual entity to map
587
     *
588
     * @return void
589
     */
590 3
    protected function addEntityIdVisibilityIdMapping($visibility)
591
    {
592 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\Product\Subjects\BunchSubject.

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...
593 3
    }
594
}
595