Completed
Pull Request — master (#42)
by Tim
05:26
created

UrlRewriteObserver::getRowStoreId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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\Product\Utils\ColumnKeys;
24
use TechDivision\Import\Product\Utils\MemberNames;
25
use TechDivision\Import\Utils\Filter\ConvertLiteralUrl;
26
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
27
28
/**
29
 * Observer that creates/updates the product's URL rewrites.
30
 *
31
 * @author    Tim Wagner <[email protected]>
32
 * @copyright 2016 TechDivision GmbH <[email protected]>
33
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
34
 * @link      https://github.com/techdivision/import-product
35
 * @link      http://www.techdivision.com
36
 */
37
class UrlRewriteObserver extends AbstractProductImportObserver
38
{
39
40
    /**
41
     * The entity type to load the URL rewrites for.
42
     *
43
     * @var string
44
     */
45
    const ENTITY_TYPE = 'product';
46
47
    /**
48
     * The URL key from the CSV file column that has to be processed by the observer.
49
     *
50
     * @var string
51
     */
52
    protected $urlKey;
53
54
    /**
55
     * The actual category ID to process.
56
     *
57
     * @var integer
58
     */
59
    protected $categoryId;
60
61
    /**
62
     * The actual entity ID to process.
63
     *
64
     * @var integer
65
     */
66
    protected $entityId;
67
68
    /**
69
     * The array with the URL rewrites that has to be created.
70
     *
71
     * @var array
72
     */
73
    protected $urlRewrites = array();
74
75
    /**
76
     * Process the observer's business logic.
77
     *
78
     * @return void
79
     */
80 3
    protected function process()
81
    {
82
83
        // query whether or not, we've found a new SKU => means we've found a new product
84 3
        if ($this->hasBeenProcessed($this->getValue(ColumnKeys::SKU))) {
0 ignored issues
show
Bug introduced by
The method getValue() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
85
            return;
86
        }
87
88
        // try to prepare the URL key, return immediately if not possible
89 3
        if (!$this->prepareUrlKey()) {
90
            return;
91
        }
92
93
        // initialize the store view code
94 3
        $this->prepareStoreViewCode();
0 ignored issues
show
Bug introduced by
The method prepareStoreViewCode() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
95
96
        // prepare the URL rewrites
97 3
        $this->prepareUrlRewrites();
98
99
        // iterate over the categories and create the URL rewrites
100 3
        foreach ($this->urlRewrites as $categoryId => $urlRewrite) {
101
            // initialize and persist the URL rewrite
102 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
103
                // initialize URL rewrite and catagory ID
104 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...
105 3
                $this->entityId = $urlRewrite[MemberNames::ENTITY_ID];
106 3
                $this->urlRewriteId = $this->persistUrlRewrite($urlRewrite);
0 ignored issues
show
Bug introduced by
The property urlRewriteId does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
107
108
                // initialize and persist the URL rewrite product => category relation
109 3
                $urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory(
110 3
                    $this->prepareUrlRewriteProductCategoryAttributes()
111
                );
112
113
                // create a URL rewrite product category relation, if not available yet
114 3
                if ($urlRewriteProductCategory === null) {
115
                    continue;
116
                }
117
118
                // persist the URL rewrite product category relation
119 3
                $this->persistUrlRewriteProductCategory($urlRewriteProductCategory);
120
            }
121
        }
122 3
    }
123
124
    /**
125
     * Initialize the category product with the passed attributes and returns an instance.
126
     *
127
     * @param array $attr The category product attributes
128
     *
129
     * @return array The initialized category product
130
     */
131 2
    protected function initializeUrlRewrite(array $attr)
132
    {
133 2
        return $attr;
134
    }
135
136
    /**
137
     * Initialize the URL rewrite product => category relation with the passed attributes
138
     * and returns an instance.
139
     *
140
     * @param array $attr The URL rewrite product => category relation attributes
141
     *
142
     * @return array The initialized URL rewrite product => category relation
143
     */
144 2
    protected function initializeUrlRewriteProductCategory($attr)
145
    {
146 2
        return $attr;
147
    }
148
149
    /**
150
     * Prepare's the URL rewrites that has to be created/updated.
151
     *
152
     * @return void
153
     */
154 3
    protected function prepareUrlRewrites()
155
    {
156
157
        // (re-)initialize the array for the URL rewrites
158 3
        $this->urlRewrites = array();
159
160
        // load the root category, because we need that to create the default product URL rewrite
161 3
        $rootCategory = $this->getRootCategory();
162
163
        // add the root category ID to the category => product relations
164 3
        $productCategoryIds = $this->getProductCategoryIds();
165 3
        $productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getLastEntityId();
0 ignored issues
show
Bug introduced by
The method getLastEntityId() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
166
167
        // prepare the URL rewrites
168 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
169
            // set category/entity ID
170 3
            $this->categoryId = $categoryId;
171 3
            $this->entityId = $entityId;
172
173
            // prepare the attributes for each URL rewrite
174 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
175
        }
176 3
    }
177
178
    /**
179
     * Prepare's and set's the URL key from the passed row of the CSV file.
180
     *
181
     * @return boolean TRUE, if the URL key has been prepared, else FALSE
182
     */
183 3
    protected function prepareUrlKey()
184
    {
185
186
        // initialize the URL key
187 3
        $urlKey = null;
188
189
        // query whether or not we've a URL key available in the CSV file row
190 3
        if ($urlKeyFound = $this->getValue(ColumnKeys::URL_KEY)) {
0 ignored issues
show
Bug introduced by
The method getValue() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
191 3
            $urlKey = $urlKeyFound;
192
        }
193
194
        // query whether or not an URL key has been specified in the CSV file
195 3
        if (empty($urlKey)) {
196
            // initialize the product name
197
            $productName = null;
198
            // if not, try to use the product name
199
            if ($nameFound = $this->getValue(ColumnKeys::NAME)) {
0 ignored issues
show
Bug introduced by
The method getValue() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
200
                $productName = $nameFound;
201
            }
202
203
            // if nor URL key AND product name are empty, return immediately
204
            if (empty($productName)) {
205
                return false;
206
            }
207
208
            // initialize the URL key with product name
209
            $urlKey = $this->convertNameToUrlKey($productName);
210
        }
211
212
        // convert and set the URL key
213 3
        $this->urlKey = $urlKey;
214
215
        // return TRUE if the URL key has been prepared
216 3
        return true;
217
    }
218
219
    /**
220
     * Prepare the attributes of the entity that has to be persisted.
221
     *
222
     * @return array The prepared attributes
223
     */
224 3
    protected function prepareAttributes()
225
    {
226
227
        // load the store ID to use
228 3
        $storeId = $this->getRowStoreId();
229
230
        // load the category to create the URL rewrite for
231 3
        $category = $this->getCategory($this->categoryId);
232
233
        // initialize the values
234 3
        $requestPath = $this->prepareRequestPath($category);
235 3
        $targetPath = $this->prepareTargetPath($category);
236 3
        $metadata = serialize($this->prepareMetadata($category));
237
238
        // return the prepared URL rewrite
239 3
        return $this->initializeEntity(
0 ignored issues
show
Bug introduced by
The method initializeEntity() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
240
            array(
241 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
242 3
                MemberNames::ENTITY_ID        => $this->entityId,
243 3
                MemberNames::REQUEST_PATH     => $requestPath,
244 3
                MemberNames::TARGET_PATH      => $targetPath,
245 3
                MemberNames::REDIRECT_TYPE    => 0,
246 3
                MemberNames::STORE_ID         => $storeId,
247
                MemberNames::DESCRIPTION      => null,
248 3
                MemberNames::IS_AUTOGENERATED => 1,
249 3
                MemberNames::METADATA         => $metadata
250
            )
251
        );
252
    }
253
254
    /**
255
     * Prepare's the URL rewrite product => category relation attributes.
256
     *
257
     * @return arry The prepared attributes
258
     */
259 3
    protected function prepareUrlRewriteProductCategoryAttributes()
260
    {
261
262
        // return the prepared product
263 3
        return $this->initializeEntity(
0 ignored issues
show
Bug introduced by
The method initializeEntity() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
264
            array(
265 3
                MemberNames::PRODUCT_ID => $this->entityId,
266 3
                MemberNames::CATEGORY_ID => $this->categoryId,
267 3
                MemberNames::URL_REWRITE_ID => $this->urlRewriteId
268
            )
269
        );
270
    }
271
272
    /**
273
     * Prepare's the target path for a URL rewrite.
274
     *
275
     * @param array $category The categroy with the URL path
276
     *
277
     * @return string The target path
278
     */
279 3
    protected function prepareTargetPath(array $category)
280
    {
281
282
        // load the actual entity ID
283 3
        $lastEntityId = $this->getPrimaryKey();
284
285
        // query whether or not, the category is the root category
286 3
        if ($this->isRootCategory($category)) {
287 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
288
        } else {
289 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
290
        }
291
292
        // return the target path
293 3
        return $targetPath;
294
    }
295
296
    /**
297
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
298
     *
299
     * @param array $category The categroy with the URL path
300
     *
301
     * @return string The request path
302
     */
303 3
    protected function prepareRequestPath(array $category)
304
    {
305
306
        // query whether or not, the category is the root category
307 3
        if ($this->isRootCategory($category)) {
308 3
            $requestPath = sprintf('%s.html', $this->urlKey);
309
        } else {
310 2
            $requestPath = sprintf('%s/%s.html', $category[MemberNames::URL_PATH], $this->urlKey);
311
        }
312
313
        // return the request path
314 3
        return $requestPath;
315
    }
316
317
    /**
318
     * Prepare's the URL rewrite's metadata with the passed category values.
319
     *
320
     * @param array $category The category used for preparation
321
     *
322
     * @return array The metadata
323
     */
324 3
    protected function prepareMetadata(array $category)
325
    {
326
327
        // initialize the metadata
328 3
        $metadata = array();
329
330
        // query whether or not, the passed category IS the root category
331 3
        if ($this->isRootCategory($category)) {
332 3
            return $metadata;
333
        }
334
335
        // if not, set the category ID in the metadata
336 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
337
338
        // return the metadata
339 2
        return $metadata;
340
    }
341
342
    /**
343
     * Initialize's and return's the URL key filter.
344
     *
345
     * @return \TechDivision\Import\Utils\ConvertLiteralUrl The URL key filter
346
     */
347
    protected function getUrlKeyFilter()
348
    {
349
        return new ConvertLiteralUrl();
350
    }
351
352
    /**
353
     * Convert's the passed string into a valid URL key.
354
     *
355
     * @param string $string The string to be converted, e. g. the product name
356
     *
357
     * @return string The converted string as valid URL key
358
     */
359
    protected function convertNameToUrlKey($string)
360
    {
361
        return $this->getUrlKeyFilter()->filter($string);
362
    }
363
364
    /**
365
     * Return's the root category for the actual view store.
366
     *
367
     * @return array The store's root category
368
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
369
     */
370 3
    protected function getRootCategory()
371
    {
372 3
        return $this->getSubject()->getRootCategory();
373
    }
374
375
    /**
376
     * Return's TRUE if the passed category IS the root category, else FALSE.
377
     *
378
     * @param array $category The category to query
379
     *
380
     * @return boolean TRUE if the passed category IS the root category
381
     */
382 3
    protected function isRootCategory(array $category)
383
    {
384
385
        // load the root category
386 3
        $rootCategory = $this->getRootCategory();
387
388
        // compare the entity IDs and return the result
389 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
390
    }
391
392
    /**
393
     * Return's the store ID of the actual row, or of the default store
394
     * if no store view code is set in the CSV file.
395
     *
396
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
397
     *
398
     * @return integer The ID of the actual store
399
     * @throws \Exception Is thrown, if the store with the actual code is not available
400
     */
401 3
    protected function getRowStoreId($default = null)
402
    {
403 3
        return $this->getSubject()->getRowStoreId($default);
404
    }
405
406
    /**
407
     * Return's the list with category IDs the product is related with.
408
     *
409
     * @return array The product's category IDs
410
     */
411 3
    protected function getProductCategoryIds()
412
    {
413 3
        return $this->getSubject()->getProductCategoryIds();
414
    }
415
416
    /**
417
     * Return's the category with the passed ID.
418
     *
419
     * @param integer $categoryId The ID of the category to return
420
     *
421
     * @return array The category data
422
     */
423 3
    protected function getCategory($categoryId)
424
    {
425 3
        return $this->getSubject()->getCategory($categoryId);
426
    }
427
428
    /**
429
     * Persist's the URL rewrite with the passed data.
430
     *
431
     * @param array $row The URL rewrite to persist
432
     *
433
     * @return string The ID of the persisted entity
434
     */
435 3
    protected function persistUrlRewrite($row)
436
    {
437 3
        return $this->getSubject()->persistUrlRewrite($row);
438
    }
439
440
    /**
441
     * Persist's the URL rewrite product => category relation with the passed data.
442
     *
443
     * @param array $row The URL rewrite product => category relation to persist
444
     *
445
     * @return void
446
     */
447 3
    protected function persistUrlRewriteProductCategory($row)
448
    {
449 3
        return $this->getSubject()->persistUrlRewriteProductCategory($row);
450
    }
451
452
    /**
453
     * Return's the PK to create the product => attribute relation.
454
     *
455
     * @return integer The PK to create the relation with
456
     */
457 3
    protected function getPrimaryKey()
458
    {
459 3
        return $this->getLastEntityId();
0 ignored issues
show
Bug introduced by
The method getLastEntityId() does not seem to exist on object<TechDivision\Impo...ers\UrlRewriteObserver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
460
    }
461
}
462