Completed
Push — master ( bce332...18fde1 )
by Tim
9s
created

UrlRewriteObserver::getDefaultStore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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\Filter\ConvertLiteralUrl;
27
use TechDivision\Import\Product\Observers\AbstractProductImportObserver;
28
29
/**
30
 * Observer that creates/updates the product's URL rewrites.
31
 *
32
 * @author    Tim Wagner <[email protected]>
33
 * @copyright 2016 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/techdivision/import-product
36
 * @link      http://www.techdivision.com
37
 */
38
class UrlRewriteObserver extends AbstractProductImportObserver
39
{
40
41
    /**
42
     * The entity type to load the URL rewrites for.
43
     *
44
     * @var string
45
     */
46
    const ENTITY_TYPE = 'product';
47
48
    /**
49
     * The URL key from the CSV file column that has to be processed by the observer.
50
     *
51
     * @var string
52
     */
53
    protected $urlKey;
54
55
    /**
56
     * Will be invoked by the action on the events the listener has been registered for.
57
     *
58
     * @param array $row The row to handle
59
     *
60
     * @return array The modified row
61
     * @see \TechDivision\Import\Product\Observers\ImportObserverInterface::handle()
62
     */
63 3
    public function handle(array $row)
64
    {
65
66
        // load the header information
67 3
        $headers = $this->getHeaders();
68
69
        // query whether or not, we've found a new SKU => means we've found a new product
70 3
        if ($this->isLastSku($sku = $row[$headers[ColumnKeys::SKU]])) {
71
            return $row;
72
        }
73
74
        // prepare the URL key, return immediately if not available
75 3
        if ($this->prepareUrlKey($row) == null) {
76
            return $row;
77
        }
78
79
        // initialize the store view code
80 3
        $this->prepareStoreViewCode($row);
81
82
        // load the ID of the last entity
83 3
        $lastEntityId = $this->getLastEntityId();
84
85
        // initialize the entity type to use
86 3
        $entityType = UrlRewriteObserver::ENTITY_TYPE;
87
88
        // load the product category IDs
89 3
        $productCategoryIds = $this->getProductCategoryIds();
90
91
        // load the URL rewrites for the entity type and ID
92 3
        $urlRewrites = $this->getUrlRewritesByEntityTypeAndEntityId($entityType, $lastEntityId);
93
94
        // prepare the existing URLs => unserialize the metadata
95 3
        $existingProductCategoryUrlRewrites = $this->prepareExistingCategoryUrlRewrites($urlRewrites);
96
97
        // delete/create/update the URL rewrites
98 3
        $this->deleteUrlRewrites($existingProductCategoryUrlRewrites);
99 3
        $this->updateUrlRewrites(array_intersect_key($existingProductCategoryUrlRewrites, $productCategoryIds));
100 3
        $this->createUrlRewrites($productCategoryIds);
101
102
        // returns the row
103 3
        return $row;
104
    }
105
106
    /**
107
     * Prepare's and set's the URL key from the passed row of the CSV file.
108
     *
109
     * @param array $row The row with the CSV data
110
     *
111
     * @return boolean TRUE, if the URL key has been prepared, else FALSE
112
     */
113 3
    protected function prepareUrlKey($row)
114
    {
115
116
        // load the header information
117 3
        $headers = $this->getHeaders();
118
119
        // query whether or not we've a URL key available in the CSV file row
120 3
        if (isset($row[$headers[ColumnKeys::URL_KEY]])) {
121 3
            $urlKey = $row[$headers[ColumnKeys::URL_KEY]];
122 3
        }
123
124
        // query whether or not an URL key has been specified in the CSV file
125 3
        if (empty($urlKey)) {
126
            // if not, try to use the product name
127
            if (isset($row[$headers[ColumnKeys::NAME]])) {
128
                $productName = $row[$headers[ColumnKeys::NAME]];
129
            }
130
131
            // if nor URL key AND product name are empty, return immediately
132
            if (empty($productName)) {
133
                return false;
134
            }
135
136
            // initialize the URL key with product name
137
            $urlKey = $productName;
138
        }
139
140
        // convert and set the URL key
141 3
        $this->setUrlKey($this->convertNameToUrlKey($urlKey));
142
143
        // return TRUE if the URL key has been prepared
144 3
        return true;
145
    }
146
147
    /**
148
     * Set's the prepared URL key.
149
     *
150
     * @param string $urlKey The prepared URL key
151
     *
152
     * @return void
153
     */
154 3
    protected function setUrlKey($urlKey)
155
    {
156 3
        $this->urlKey = $urlKey;
157 3
    }
158
159
    /**
160
     * Return's the prepared URL key.
161
     *
162
     * @return string The prepared URL key
163
     */
164 3
    protected function getUrlKey()
165
    {
166 3
        return $this->urlKey;
167
    }
168
169
    /**
170
     * Initialize's and return's the URL key filter.
171
     *
172
     * @return \TechDivision\Import\Product\Utils\ConvertLiteralUrl The URL key filter
173
     */
174 3
    protected function getUrlKeyFilter()
175
    {
176 3
        return new ConvertLiteralUrl();
177
    }
178
179
    /**
180
     * Convert's the passed string into a valid URL key.
181
     *
182
     * @param string $string The string to be converted, e. g. the product name
183
     *
184
     * @return string The converted string as valid URL key
185
     */
186 3
    protected function convertNameToUrlKey($string)
187
    {
188 3
        return $this->getUrlKeyFilter()->filter($string);
189
    }
190
191
    /**
192
     * Convert's the passed URL rewrites into an array with the category ID from the
193
     * metadata as key and the URL rewrite as value.
194
     *
195
     * If now category ID can be found in the metadata, the ID of the store's root
196
     * category is used.
197
     *
198
     * @param array $urlRewrites The URL rewrites to convert
199
     *
200
     * @return array The converted array with the de-serialized category IDs as key
201
     */
202 3
    protected function prepareExistingCategoryUrlRewrites(array $urlRewrites)
203
    {
204
205
        // initialize the array for the existing URL rewrites
206 3
        $existingProductCategoryUrlRewrites = array();
207
208
        // load the store's root category
209 3
        $rootCategory = $this->getRootCategory();
210
211
        // iterate over the URL rewrites and convert them
212 3
        foreach ($urlRewrites as $urlRewrite) {
213
            // initialize the array with the metadata
214 2
            $metadata = array();
215
216
            // de-serialize the category ID from the metadata
217 2
            if ($md = $urlRewrite['metadata']) {
218 2
                $metadata = unserialize($md);
219 2
            }
220
221
            // use the store's category ID if not serialized metadata is available
222 2
            if (!isset($metadata['category_id'])) {
223 2
                $metadata['category_id'] = $rootCategory[MemberNames::ENTITY_ID];
224 2
            }
225
226
            // append the URL rewrite with the found category ID
227 2
            $existingProductCategoryUrlRewrites[$metadata['category_id']] = $urlRewrite;
228 3
        }
229
230
        // return the array with the existing URL rewrites
231 3
        return $existingProductCategoryUrlRewrites;
232
    }
233
234
    /**
235
     * Remove's the URL rewrites with the passed data.
236
     *
237
     * @param array $existingProductCategoryUrlRewrites The array with the URL rewrites to remove
238
     *
239
     * @return void
240
     */
241 3
    protected function deleteUrlRewrites(array $existingProductCategoryUrlRewrites)
242
    {
243
244
        // query whether or not we've any URL rewrites that have to be removed
245 3
        if (sizeof($existingProductCategoryUrlRewrites) === 0) {
246 1
            return;
247
        }
248
249
        // remove the URL rewrites
250 2
        foreach ($existingProductCategoryUrlRewrites as $urlRewrite) {
251 2
            $this->removeUrlRewrite(array(MemberNames::URL_REWRITE_ID => $urlRewrite[MemberNames::URL_REWRITE_ID]));
252 2
        }
253 2
    }
254
255
    /**
256
     * Create's the URL rewrites from the passed data.
257
     *
258
     * @param array $productCategoryIds The categories to create a URL rewrite for
259
     *
260
     * @return void
261
     */
262 3
    protected function createUrlRewrites(array $productCategoryIds)
263
    {
264
265
        // query whether or not if there is any category to create a URL rewrite for
266 3
        if (sizeof($productCategoryIds) === 0) {
267
            return;
268
        }
269
270
        // iterate over the categories and create the URL rewrites
271 3
        foreach ($productCategoryIds as $categoryId => $entityId) {
272
            // load the category to create the URL rewrite for
273 3
            $category = $this->getCategory($categoryId);
274
275
            // initialize the values
276 3
            $requestPath = $this->prepareRequestPath($category);
277 3
            $targetPath = $this->prepareTargetPath($category);
278 3
            $metadata = serialize($this->prepareMetadata($category));
279
280
            // initialize the URL rewrite data
281 3
            $params = array('product', $entityId, $requestPath, $targetPath, 0, 1, null, 1, $metadata);
282
283
            // create the URL rewrite
284 3
            $this->persistUrlRewrite($params);
285 3
        }
286 3
    }
287
288
    /**
289
     * Update's existing URL rewrites by creating 301 redirect URL rewrites for each.
290
     *
291
     * @param array $existingProductCategoryUrlRewrites The array with the existing URL rewrites
292
     *
293
     * @return void
294
     */
295 3
    protected function updateUrlRewrites(array $existingProductCategoryUrlRewrites)
296
    {
297
298
        // query whether or not, we've existing URL rewrites that need to be redirected
299 3
        if (sizeof($existingProductCategoryUrlRewrites) === 0) {
300 1
            return;
301
        }
302
303
        // iterate over the URL redirects that have to be redirected
304 2
        foreach ($existingProductCategoryUrlRewrites as $categoryId => $urlRewrite) {
305
            // load the category data
306 2
            $category = $this->getCategory($categoryId);
307
308
            // initialize the values
309 2
            $entityId = $urlRewrite[MemberNames::ENTITY_ID];
310 2
            $requestPath = sprintf('%s', $urlRewrite['request_path']);
311 2
            $targetPath = $this->prepareRequestPath($category);
312 2
            $metadata = serialize($this->prepareMetadata($category));
313
314
            // initialize the URL rewrite data
315 2
            $params = array('product', $entityId, $requestPath, $targetPath, 301, 1, null, 0, $metadata);
316
317
            // create the 301 redirect URL rewrite
318 2
            $this->persistUrlRewrite($params);
319 2
        }
320 2
    }
321
322
    /**
323
     * Prepare's the target path for a URL rewrite.
324
     *
325
     * @param array $category The categroy with the URL path
326
     *
327
     * @return string The target path
328
     */
329 3
    protected function prepareTargetPath(array $category)
330
    {
331
332
        // load the actual entity ID
333 3
        $lastEntityId = $this->getLastEntityId();
334
335
        // query whether or not, the category is the root category
336 3
        if ($this->isRootCategory($category)) {
337 3
            $targetPath = sprintf('catalog/product/view/id/%d', $lastEntityId);
338 3
        } else {
339 2
            $targetPath = sprintf('catalog/product/view/id/%d/category/%d', $lastEntityId, $category[MemberNames::ENTITY_ID]);
340
        }
341
342
        // return the target path
343 3
        return $targetPath;
344
    }
345
346
    /**
347
     * Prepare's the request path for a URL rewrite or the target path for a 301 redirect.
348
     *
349
     * @param array $category The categroy with the URL path
350
     *
351
     * @return string The request path
352
     */
353 3
    protected function prepareRequestPath(array $category)
354
    {
355
356
        // query whether or not, the category is the root category
357 3
        if ($this->isRootCategory($category)) {
358 3
            $requestPath = sprintf('%s.html', $this->getUrlKey());
359 3
        } else {
360 2
            $requestPath = sprintf('%s/%s.html', $category[MemberNames::URL_PATH], $this->getUrlKey());
361
        }
362
363
        // return the request path
364 3
        return $requestPath;
365
    }
366
367
    /**
368
     * Prepare's the URL rewrite's metadata with the passed category values.
369
     *
370
     * @param array $category The category used for preparation
371
     *
372
     * @return array The metadata
373
     */
374 3
    protected function prepareMetadata(array $category)
375
    {
376
377
        // initialize the metadata
378 3
        $metadata = array();
379
380
        // query whether or not, the passed category IS the root category
381 3
        if ($this->isRootCategory($category)) {
382 3
            return $metadata;
383
        }
384
385
        // if not, set the category ID in the metadata
386 2
        $metadata['category_id'] = $category[MemberNames::ENTITY_ID];
387
388
        // return the metadata
389 2
        return $metadata;
390
    }
391
392
    /**
393
     * Set's the store view code the create the product/attributes for.
394
     *
395
     * @param string $storeViewCode The store view code
396
     *
397
     * @return void
398
     */
399 3
    public function setStoreViewCode($storeViewCode)
400
    {
401 3
        $this->getSubject()->setStoreViewCode($storeViewCode);
402 3
    }
403
404
    /**
405
     * Return's the root category for the actual view store.
406
     *
407
     * @return array The store's root category
408
     * @throws \Exception Is thrown if the root category for the passed store code is NOT available
409
     */
410 3
    public function getRootCategory()
411
    {
412 3
        return $this->getSubject()->getRootCategory();
413
    }
414
415
    /**
416
     * Return's TRUE if the passed category IS the root category, else FALSE.
417
     *
418
     * @param array $category The category to query
419
     *
420
     * @return boolean TRUE if the passed category IS the root category
421
     */
422 3
    public function isRootCategory(array $category)
423
    {
424
425
        // load the root category
426 3
        $rootCategory = $this->getRootCategory();
427
428
        // compare the entity IDs and return the result
429 3
        return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID];
430
    }
431
432
    /**
433
     * Return's the list with category IDs the product is related with.
434
     *
435
     * @return array The product's category IDs
436
     */
437 4
    public function getProductCategoryIds()
438
    {
439 4
        return $this->getSubject()->getProductCategoryIds();
440
    }
441
442
    /**
443
     * Return's the category with the passed ID.
444
     *
445
     * @param integer $categoryId The ID of the category to return
446
     *
447
     * @return array The category data
448
     */
449 3
    public function getCategory($categoryId)
450
    {
451 3
        return $this->getSubject()->getCategory($categoryId);
452
    }
453
454
    /**
455
     * Return's the URL rewrites for the passed URL entity type and ID.
456
     *
457
     * @param string  $entityType The entity type to load the URL rewrites for
458
     * @param integer $entityId   The entity ID to laod the rewrites for
459
     *
460
     * @return array The URL rewrites
461
     */
462 4
    public function getUrlRewritesByEntityTypeAndEntityId($entityType, $entityId)
463
    {
464 4
        return $this->getSubject()->getUrlRewritesByEntityTypeAndEntityId($entityType, $entityId);
465
    }
466
467
    /**
468
     * Return's the default store.
469
     *
470
     * @return array The default store
471
     */
472
    public function getDefaultStore()
473
    {
474
        return $this->getSubject()->getDefaultStore();
475
    }
476
477
    /**
478
     * Persist's the URL write with the passed data.
479
     *
480
     * @param array $row The URL rewrite to persist
481
     *
482
     * @return void
483
     */
484 4
    public function persistUrlRewrite($row)
485
    {
486 4
        $this->getSubject()->persistUrlRewrite($row);
487 4
    }
488
489
    /**
490
     * Delete's the URL rewrite with the passed attributes.
491
     *
492
     * @param array $row The attributes of the entity to remove
493
     *
494
     * @return void
495
     */
496 3
    public function removeUrlRewrite($row)
497
    {
498 3
        $this->getSubject()->removeUrlRewrite($row);
499 3
    }
500
}
501