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

UrlRewriteObserver::handle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 6

Duplication

Lines 17
Ratio 100 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 17
loc 17
c 0
b 0
f 0
ccs 5
cts 6
cp 0.8333
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
crap 2.0185
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\Product\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
     * Will be invoked by the action on the events the listener has been registered for.
77
     *
78
     * @param array $row The row to handle
79
     *
80
     * @return array The modified row
81
     * @see \TechDivision\Import\Product\Observers\ImportObserverInterface::handle()
82
     */
83 3 View Code Duplication
    public function handle(array $row)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
    {
85
86
        // initialize the row
87 3
        $this->setRow($row);
88
89
        // query whether or not, we've found a new SKU => means we've found a new product
90 3
        if ($this->isLastSku($this->getValue(ColumnKeys::SKU))) {
91
            return $this->getRow();
92
        }
93
94
        // process the functionality and return the row
95 3
        $this->process();
96
97
        // return the processed row
98 3
        return $this->getRow();
99
    }
100
101
    /**
102
     * Set's the prepared URL key.
103
     *
104
     * @param string $urlKey The prepared URL key
105
     *
106
     * @return void
107
     */
108 3
    protected function setUrlKey($urlKey)
109
    {
110 3
        $this->urlKey = $urlKey;
111 3
    }
112
113
    /**
114
     * Return's the prepared URL key.
115
     *
116
     * @return string The prepared URL key
117
     */
118 3
    protected function getUrlKey()
119
    {
120 3
        return $this->urlKey;
121
    }
122
123
    /**
124
     * Set's the actual category ID to process.
125
     *
126
     * @param integer $categoryId The category ID
127
     *
128
     * @return void
129
     */
130 3
    protected function setCategoryId($categoryId)
131
    {
132 3
        $this->categoryId = $categoryId;
133 3
    }
134
135
    /**
136
     * Return's the actual category ID to process.
137
     *
138
     * @return integer The category ID
139
     */
140 3
    protected function getCategoryId()
141
    {
142 3
        return $this->categoryId;
143
    }
144
145
    /**
146
     * Set's the actual entity ID to process.
147
     *
148
     * @param integer $entityId The entity ID
149
     *
150
     * @return void
151
     */
152 3
    protected function setEntityId($entityId)
153
    {
154 3
        $this->entityId = $entityId;
155 3
    }
156
157
    /**
158
     * Return's the actual entity ID to process.
159
     *
160
     * @return integer The entity ID
161
     */
162 3
    protected function getEntityId()
163
    {
164 3
        return $this->entityId;
165
    }
166
167
    /**
168
     * Process the observer's business logic.
169
     *
170
     * @return void
171
     */
172 3
    protected function process()
173
    {
174
175
        // try to prepare the URL key, return immediately if not possible
176 3
        if (!$this->prepareUrlKey()) {
177
            return;
178
        }
179
180
        // initialize the store view code
181 3
        $this->prepareStoreViewCode();
182
183
        // prepare the URL rewrites
184 3
        $this->prepareUrlRewrites();
185
186
        // iterate over the categories and create the URL rewrites
187 3
        foreach ($this->urlRewrites as $urlRewrite) {
188
            // initialize and persist the URL rewrite
189 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
190 3
                $this->persistUrlRewrite($urlRewrite);
191
            }
192
        }
193 3
    }
194
195
    /**
196
     * Initialize the category product with the passed attributes and returns an instance.
197
     *
198
     * @param array $attr The category product attributes
199
     *
200
     * @return array The initialized category product
201
     */
202 2
    protected function initializeUrlRewrite(array $attr)
203
    {
204 2
        return $attr;
205
    }
206
207
    /**
208
     * Prepare's the URL rewrites that has to be created/updated.
209
     *
210
     * @return void
211
     */
212 3
    protected function prepareUrlRewrites()
213
    {
214
215
        // (re-)initialize the array for the URL rewrites
216 3
        $this->urlRewrites = array();
217
218
        // load the root category, because we need that to create the default product URL rewrite
219 3
        $rootCategory = $this->getRootCategory();
220
221
        // add the root category ID to the category => product relations
222 3
        $productCategoryIds = $this->getProductCategoryIds();
223 3
        $productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getLastEntityId();
224
225
        // prepare the URL rewrites
226 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
227
            // set category/entity ID
228 3
            $this->setCategoryId($categoryId);
229 3
            $this->setEntityId($entityId);
230
231
            // prepare the attributes for each URL rewrite
232 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
233
        }
234 3
    }
235
236
    /**
237
     * Prepare's and set's the URL key from the passed row of the CSV file.
238
     *
239
     * @return boolean TRUE, if the URL key has been prepared, else FALSE
240
     */
241 3
    protected function prepareUrlKey()
242
    {
243
244
        // load header and row information
245 3
        $row = $this->getRow();
0 ignored issues
show
Unused Code introduced by
$row is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
246 3
        $headers = $this->getHeaders();
0 ignored issues
show
Unused Code introduced by
$headers is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
247
248
        // initialize the URL key
249 3
        $urlKey = null;
250
251
        // query whether or not we've a URL key available in the CSV file row
252 3
        if ($urlKeyFound = $this->getValue(ColumnKeys::URL_KEY)) {
253 3
            $urlKey = $urlKeyFound;
254
        }
255
256
        // query whether or not an URL key has been specified in the CSV file
257 3
        if (empty($urlKey)) {
258
            // initialize the product name
259
            $productName = null;
260
            // if not, try to use the product name
261
            if ($nameFound = $this->getValue(ColumnKeys::NAME)) {
262
                $productName = $nameFound;
263
            }
264
265
            // if nor URL key AND product name are empty, return immediately
266
            if (empty($productName)) {
267
                return false;
268
            }
269
270
            // initialize the URL key with product name
271
            $urlKey = $this->convertNameToUrlKey($productName);
272
        }
273
274
        // convert and set the URL key
275 3
        $this->setUrlKey($urlKey);
276
277
        // return TRUE if the URL key has been prepared
278 3
        return true;
279
    }
280
281
    /**
282
     * Prepare the attributes of the entity that has to be persisted.
283
     *
284
     * @return array The prepared attributes
285
     */
286 3
    protected function prepareAttributes()
287
    {
288
289
        // load entity/category ID
290 3
        $entityId = $this->getEntityId();
291 3
        $categoryId = $this->getCategoryId();
292
293
        // load the store ID to use
294 3
        $storeId = $this->getRowStoreId();
295
296
        // load the category to create the URL rewrite for
297 3
        $category = $this->getCategory($categoryId);
298
299
        // initialize the values
300 3
        $requestPath = $this->prepareRequestPath($category);
301 3
        $targetPath = $this->prepareTargetPath($category);
302 3
        $metadata = serialize($this->prepareMetadata($category));
303
304
        // return the prepared URL rewrite
305 3
        return $this->initializeEntity(
306
            array(
307 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
308 3
                MemberNames::ENTITY_ID        => $entityId,
309 3
                MemberNames::REQUEST_PATH     => $requestPath,
310 3
                MemberNames::TARGET_PATH      => $targetPath,
311 3
                MemberNames::REDIRECT_TYPE    => 0,
312 3
                MemberNames::STORE_ID         => $storeId,
313
                MemberNames::DESCRIPTION      => null,
314 3
                MemberNames::IS_AUTOGENERATED => 1,
315 3
                MemberNames::METADATA         => $metadata
316
            )
317
        );
318
    }
319
320
    /**
321
     * Prepare's the target path for a URL rewrite.
322
     *
323
     * @param array $category The categroy with the URL path
324
     *
325
     * @return string The target path
326
     */
327 3
    protected function prepareTargetPath(array $category)
328
    {
329
330
        // load the actual entity ID
331 3
        $lastEntityId = $this->getPrimaryKey();
332
333
        // query whether or not, the category is the root category
334 3
        if ($this->isRootCategory($category)) {
335 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
336
        } else {
337 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
338
        }
339
340
        // return the target path
341 3
        return $targetPath;
342
    }
343
344
    /**
345
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
346
     *
347
     * @param array $category The categroy with the URL path
348
     *
349
     * @return string The request path
350
     */
351 3
    protected function prepareRequestPath(array $category)
352
    {
353
354
        // query whether or not, the category is the root category
355 3
        if ($this->isRootCategory($category)) {
356 3
            $requestPath = sprintf('%s.html', $this->getUrlKey());
357
        } else {
358 2
            $requestPath = sprintf('%s/%s.html', $category[MemberNames::URL_PATH], $this->getUrlKey());
359
        }
360
361
        // return the request path
362 3
        return $requestPath;
363
    }
364
365
    /**
366
     * Prepare's the URL rewrite's metadata with the passed category values.
367
     *
368
     * @param array $category The category used for preparation
369
     *
370
     * @return array The metadata
371
     */
372 3
    protected function prepareMetadata(array $category)
373
    {
374
375
        // initialize the metadata
376 3
        $metadata = array();
377
378
        // query whether or not, the passed category IS the root category
379 3
        if ($this->isRootCategory($category)) {
380 3
            return $metadata;
381
        }
382
383
        // if not, set the category ID in the metadata
384 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
385
386
        // return the metadata
387 2
        return $metadata;
388
    }
389
390
    /**
391
     * Initialize's and return's the URL key filter.
392
     *
393
     * @return \TechDivision\Import\Product\Utils\ConvertLiteralUrl The URL key filter
394
     */
395
    protected function getUrlKeyFilter()
396
    {
397
        return new ConvertLiteralUrl();
398
    }
399
400
    /**
401
     * Convert's the passed string into a valid URL key.
402
     *
403
     * @param string $string The string to be converted, e. g. the product name
404
     *
405
     * @return string The converted string as valid URL key
406
     */
407
    protected function convertNameToUrlKey($string)
408
    {
409
        return $this->getUrlKeyFilter()->filter($string);
410
    }
411
412
    /**
413
     * Return's the root category for the actual view store.
414
     *
415
     * @return array The store's root category
416
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
417
     */
418 3
    protected function getRootCategory()
419
    {
420 3
        return $this->getSubject()->getRootCategory();
421
    }
422
423
    /**
424
     * Return's TRUE if the passed category IS the root category, else FALSE.
425
     *
426
     * @param array $category The category to query
427
     *
428
     * @return boolean TRUE if the passed category IS the root category
429
     */
430 3
    protected function isRootCategory(array $category)
431
    {
432
433
        // load the root category
434 3
        $rootCategory = $this->getRootCategory();
435
436
        // compare the entity IDs and return the result
437 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
438
    }
439
440
    /**
441
     * Return's the store ID of the actual row, or of the default store
442
     * if no store view code is set in the CSV file.
443
     *
444
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
445
     *
446
     * @return integer The ID of the actual store
447
     * @throws \Exception Is thrown, if the store with the actual code is not available
448
     */
449 3
    protected function getRowStoreId($default = null)
450
    {
451 3
        return $this->getSubject()->getRowStoreId($default);
452
    }
453
454
    /**
455
     * Return's the list with category IDs the product is related with.
456
     *
457
     * @return array The product's category IDs
458
     */
459 3
    protected function getProductCategoryIds()
460
    {
461 3
        return $this->getSubject()->getProductCategoryIds();
462
    }
463
464
    /**
465
     * Return's the category with the passed ID.
466
     *
467
     * @param integer $categoryId The ID of the category to return
468
     *
469
     * @return array The category data
470
     */
471 3
    protected function getCategory($categoryId)
472
    {
473 3
        return $this->getSubject()->getCategory($categoryId);
474
    }
475
476
    /**
477
     * Persist's the URL write with the passed data.
478
     *
479
     * @param array $row The URL rewrite to persist
480
     *
481
     * @return void
482
     */
483 3
    protected function persistUrlRewrite($row)
484
    {
485 3
        $this->getSubject()->persistUrlRewrite($row);
486 3
    }
487
488
    /**
489
     * Return's the PK to create the product => attribute relation.
490
     *
491
     * @return integer The PK to create the relation with
492
     */
493 3
    protected function getPrimaryKey()
494
    {
495 3
        return $this->getLastEntityId();
496
    }
497
}
498