Completed
Push — master ( 404f67...9bc8e3 )
by Tim
10s
created

UrlRewriteObserver   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 436
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 92.08%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 2
dl 0
loc 436
ccs 93
cts 101
cp 0.9208
rs 9
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A setUrlKey() 0 4 1
A getUrlKey() 0 4 1
A setCategoryId() 0 4 1
A getCategoryId() 0 4 1
A setEntityId() 0 4 1
A getEntityId() 0 4 1
B process() 0 27 5
A initializeUrlRewrite() 0 4 1
A prepareUrlRewrites() 0 23 2
B prepareUrlKey() 0 35 5
B prepareAttributes() 0 33 1
A prepareTargetPath() 0 16 2
A prepareRequestPath() 0 13 2
A prepareMetadata() 0 17 2
A getUrlKeyFilter() 0 4 1
A convertNameToUrlKey() 0 4 1
A getRootCategory() 0 4 1
A isRootCategory() 0 9 1
A getRowStoreId() 0 4 1
A getProductCategoryIds() 0 4 1
A getCategory() 0 4 1
A persistUrlRewrite() 0 4 1
A getPrimaryKey() 0 4 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\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
     * Set's the prepared URL key.
77
     *
78
     * @param string $urlKey The prepared URL key
79
     *
80
     * @return void
81
     */
82 3
    protected function setUrlKey($urlKey)
83
    {
84 3
        $this->urlKey = $urlKey;
85 3
    }
86
87
    /**
88
     * Return's the prepared URL key.
89
     *
90
     * @return string The prepared URL key
91
     */
92 3
    protected function getUrlKey()
93
    {
94 3
        return $this->urlKey;
95
    }
96
97
    /**
98
     * Set's the actual category ID to process.
99
     *
100
     * @param integer $categoryId The category ID
101
     *
102
     * @return void
103
     */
104 3
    protected function setCategoryId($categoryId)
105
    {
106 3
        $this->categoryId = $categoryId;
107 3
    }
108
109
    /**
110
     * Return's the actual category ID to process.
111
     *
112
     * @return integer The category ID
113
     */
114 3
    protected function getCategoryId()
115
    {
116 3
        return $this->categoryId;
117
    }
118
119
    /**
120
     * Set's the actual entity ID to process.
121
     *
122
     * @param integer $entityId The entity ID
123
     *
124
     * @return void
125
     */
126 3
    protected function setEntityId($entityId)
127
    {
128 3
        $this->entityId = $entityId;
129 3
    }
130
131
    /**
132
     * Return's the actual entity ID to process.
133
     *
134
     * @return integer The entity ID
135
     */
136 3
    protected function getEntityId()
137
    {
138 3
        return $this->entityId;
139
    }
140
141
    /**
142
     * Process the observer's business logic.
143
     *
144
     * @return void
145
     */
146 3
    protected function process()
147
    {
148
149
        // query whether or not, we've found a new SKU => means we've found a new product
150 3
        if ($this->isLastSku($this->getValue(ColumnKeys::SKU))) {
151
            return;
152
        }
153
154
        // try to prepare the URL key, return immediately if not possible
155 3
        if (!$this->prepareUrlKey()) {
156
            return;
157
        }
158
159
        // initialize the store view code
160 3
        $this->prepareStoreViewCode();
161
162
        // prepare the URL rewrites
163 3
        $this->prepareUrlRewrites();
164
165
        // iterate over the categories and create the URL rewrites
166 3
        foreach ($this->urlRewrites as $urlRewrite) {
167
            // initialize and persist the URL rewrite
168 3
            if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) {
169 3
                $this->persistUrlRewrite($urlRewrite);
170
            }
171
        }
172 3
    }
173
174
    /**
175
     * Initialize the category product with the passed attributes and returns an instance.
176
     *
177
     * @param array $attr The category product attributes
178
     *
179
     * @return array The initialized category product
180
     */
181 2
    protected function initializeUrlRewrite(array $attr)
182
    {
183 2
        return $attr;
184
    }
185
186
    /**
187
     * Prepare's the URL rewrites that has to be created/updated.
188
     *
189
     * @return void
190
     */
191 3
    protected function prepareUrlRewrites()
192
    {
193
194
        // (re-)initialize the array for the URL rewrites
195 3
        $this->urlRewrites = array();
196
197
        // load the root category, because we need that to create the default product URL rewrite
198 3
        $rootCategory = $this->getRootCategory();
199
200
        // add the root category ID to the category => product relations
201 3
        $productCategoryIds = $this->getProductCategoryIds();
202 3
        $productCategoryIds[$rootCategory[MemberNames::ENTITY_ID]] = $this->getLastEntityId();
203
204
        // prepare the URL rewrites
205 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
206
            // set category/entity ID
207 3
            $this->setCategoryId($categoryId);
208 3
            $this->setEntityId($entityId);
209
210
            // prepare the attributes for each URL rewrite
211 3
            $this->urlRewrites[$categoryId] = $this->prepareAttributes();
212
        }
213 3
    }
214
215
    /**
216
     * Prepare's and set's the URL key from the passed row of the CSV file.
217
     *
218
     * @return boolean TRUE, if the URL key has been prepared, else FALSE
219
     */
220 3
    protected function prepareUrlKey()
221
    {
222
223
        // initialize the URL key
224 3
        $urlKey = null;
225
226
        // query whether or not we've a URL key available in the CSV file row
227 3
        if ($urlKeyFound = $this->getValue(ColumnKeys::URL_KEY)) {
228 3
            $urlKey = $urlKeyFound;
229
        }
230
231
        // query whether or not an URL key has been specified in the CSV file
232 3
        if (empty($urlKey)) {
233
            // initialize the product name
234
            $productName = null;
235
            // if not, try to use the product name
236
            if ($nameFound = $this->getValue(ColumnKeys::NAME)) {
237
                $productName = $nameFound;
238
            }
239
240
            // if nor URL key AND product name are empty, return immediately
241
            if (empty($productName)) {
242
                return false;
243
            }
244
245
            // initialize the URL key with product name
246
            $urlKey = $this->convertNameToUrlKey($productName);
247
        }
248
249
        // convert and set the URL key
250 3
        $this->setUrlKey($urlKey);
251
252
        // return TRUE if the URL key has been prepared
253 3
        return true;
254
    }
255
256
    /**
257
     * Prepare the attributes of the entity that has to be persisted.
258
     *
259
     * @return array The prepared attributes
260
     */
261 3
    protected function prepareAttributes()
262
    {
263
264
        // load entity/category ID
265 3
        $entityId = $this->getEntityId();
266 3
        $categoryId = $this->getCategoryId();
267
268
        // load the store ID to use
269 3
        $storeId = $this->getRowStoreId();
270
271
        // load the category to create the URL rewrite for
272 3
        $category = $this->getCategory($categoryId);
273
274
        // initialize the values
275 3
        $requestPath = $this->prepareRequestPath($category);
276 3
        $targetPath = $this->prepareTargetPath($category);
277 3
        $metadata = serialize($this->prepareMetadata($category));
278
279
        // return the prepared URL rewrite
280 3
        return $this->initializeEntity(
281
            array(
282 3
                MemberNames::ENTITY_TYPE      => UrlRewriteObserver::ENTITY_TYPE,
283 3
                MemberNames::ENTITY_ID        => $entityId,
284 3
                MemberNames::REQUEST_PATH     => $requestPath,
285 3
                MemberNames::TARGET_PATH      => $targetPath,
286 3
                MemberNames::REDIRECT_TYPE    => 0,
287 3
                MemberNames::STORE_ID         => $storeId,
288
                MemberNames::DESCRIPTION      => null,
289 3
                MemberNames::IS_AUTOGENERATED => 1,
290 3
                MemberNames::METADATA         => $metadata
291
            )
292
        );
293
    }
294
295
    /**
296
     * Prepare's the target path for a URL rewrite.
297
     *
298
     * @param array $category The categroy with the URL path
299
     *
300
     * @return string The target path
301
     */
302 3
    protected function prepareTargetPath(array $category)
303
    {
304
305
        // load the actual entity ID
306 3
        $lastEntityId = $this->getPrimaryKey();
307
308
        // query whether or not, the category is the root category
309 3
        if ($this->isRootCategory($category)) {
310 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
311
        } else {
312 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
313
        }
314
315
        // return the target path
316 3
        return $targetPath;
317
    }
318
319
    /**
320
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
321
     *
322
     * @param array $category The categroy with the URL path
323
     *
324
     * @return string The request path
325
     */
326 3
    protected function prepareRequestPath(array $category)
327
    {
328
329
        // query whether or not, the category is the root category
330 3
        if ($this->isRootCategory($category)) {
331 3
            $requestPath = sprintf('%s.html', $this->getUrlKey());
332
        } else {
333 2
            $requestPath = sprintf('%s/%s.html', $category[MemberNames::URL_PATH], $this->getUrlKey());
334
        }
335
336
        // return the request path
337 3
        return $requestPath;
338
    }
339
340
    /**
341
     * Prepare's the URL rewrite's metadata with the passed category values.
342
     *
343
     * @param array $category The category used for preparation
344
     *
345
     * @return array The metadata
346
     */
347 3
    protected function prepareMetadata(array $category)
348
    {
349
350
        // initialize the metadata
351 3
        $metadata = array();
352
353
        // query whether or not, the passed category IS the root category
354 3
        if ($this->isRootCategory($category)) {
355 3
            return $metadata;
356
        }
357
358
        // if not, set the category ID in the metadata
359 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
360
361
        // return the metadata
362 2
        return $metadata;
363
    }
364
365
    /**
366
     * Initialize's and return's the URL key filter.
367
     *
368
     * @return \TechDivision\Import\Product\Utils\ConvertLiteralUrl The URL key filter
369
     */
370
    protected function getUrlKeyFilter()
371
    {
372
        return new ConvertLiteralUrl();
373
    }
374
375
    /**
376
     * Convert's the passed string into a valid URL key.
377
     *
378
     * @param string $string The string to be converted, e. g. the product name
379
     *
380
     * @return string The converted string as valid URL key
381
     */
382
    protected function convertNameToUrlKey($string)
383
    {
384
        return $this->getUrlKeyFilter()->filter($string);
385
    }
386
387
    /**
388
     * Return's the root category for the actual view store.
389
     *
390
     * @return array The store's root category
391
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
392
     */
393 3
    protected function getRootCategory()
394
    {
395 3
        return $this->getSubject()->getRootCategory();
396
    }
397
398
    /**
399
     * Return's TRUE if the passed category IS the root category, else FALSE.
400
     *
401
     * @param array $category The category to query
402
     *
403
     * @return boolean TRUE if the passed category IS the root category
404
     */
405 3
    protected function isRootCategory(array $category)
406
    {
407
408
        // load the root category
409 3
        $rootCategory = $this->getRootCategory();
410
411
        // compare the entity IDs and return the result
412 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
413
    }
414
415
    /**
416
     * Return's the store ID of the actual row, or of the default store
417
     * if no store view code is set in the CSV file.
418
     *
419
     * @param string|null $default The default store view code to use, if no store view code is set in the CSV file
420
     *
421
     * @return integer The ID of the actual store
422
     * @throws \Exception Is thrown, if the store with the actual code is not available
423
     */
424 3
    protected function getRowStoreId($default = null)
425
    {
426 3
        return $this->getSubject()->getRowStoreId($default);
427
    }
428
429
    /**
430
     * Return's the list with category IDs the product is related with.
431
     *
432
     * @return array The product's category IDs
433
     */
434 3
    protected function getProductCategoryIds()
435
    {
436 3
        return $this->getSubject()->getProductCategoryIds();
437
    }
438
439
    /**
440
     * Return's the category with the passed ID.
441
     *
442
     * @param integer $categoryId The ID of the category to return
443
     *
444
     * @return array The category data
445
     */
446 3
    protected function getCategory($categoryId)
447
    {
448 3
        return $this->getSubject()->getCategory($categoryId);
449
    }
450
451
    /**
452
     * Persist's the URL write with the passed data.
453
     *
454
     * @param array $row The URL rewrite to persist
455
     *
456
     * @return void
457
     */
458 3
    protected function persistUrlRewrite($row)
459
    {
460 3
        $this->getSubject()->persistUrlRewrite($row);
461 3
    }
462
463
    /**
464
     * Return's the PK to create the product => attribute relation.
465
     *
466
     * @return integer The PK to create the relation with
467
     */
468 3
    protected function getPrimaryKey()
469
    {
470 3
        return $this->getLastEntityId();
471
    }
472
}
473