1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* TechDivision\Import\Product\UrlRewrite\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-url-rewrite |
18
|
|
|
* @link http://www.techdivision.com |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace TechDivision\Import\Product\UrlRewrite\Observers; |
22
|
|
|
|
23
|
|
|
use TechDivision\Import\Product\UrlRewrite\Utils\CoreConfigDataKeys; |
24
|
|
|
use TechDivision\Import\Utils\StoreViewCodes; |
25
|
|
|
use TechDivision\Import\Product\Utils\VisibilityKeys; |
26
|
|
|
use TechDivision\Import\Product\Observers\AbstractProductImportObserver; |
27
|
|
|
use TechDivision\Import\Product\UrlRewrite\Utils\ColumnKeys; |
28
|
|
|
use TechDivision\Import\Product\UrlRewrite\Utils\MemberNames; |
29
|
|
|
use TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface; |
30
|
|
|
use TechDivision\Import\Subjects\SubjectInterface; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Observer that creates/updates the product's URL rewrites. |
34
|
|
|
* |
35
|
|
|
* @author Tim Wagner <[email protected]> |
36
|
|
|
* @copyright 2016 TechDivision GmbH <[email protected]> |
37
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
38
|
|
|
* @link https://github.com/techdivision/import-product-url-rewrite |
39
|
|
|
* @link http://www.techdivision.com |
40
|
|
|
*/ |
41
|
|
|
class UrlRewriteObserver extends AbstractProductImportObserver |
42
|
|
|
{ |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* The entity type to load the URL rewrites for. |
46
|
|
|
* |
47
|
|
|
* @var string |
48
|
|
|
*/ |
49
|
|
|
const ENTITY_TYPE = 'product'; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* The key for the category in the metadata. |
53
|
|
|
* |
54
|
|
|
* @var string |
55
|
|
|
*/ |
56
|
|
|
const CATEGORY_ID = 'category_id'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* The URL key from the CSV file column that has to be processed by the observer. |
60
|
|
|
* |
61
|
|
|
* @var string |
62
|
|
|
*/ |
63
|
|
|
protected $urlKey; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* The actual category ID to process. |
67
|
|
|
* |
68
|
|
|
* @var integer |
69
|
|
|
*/ |
70
|
|
|
protected $categoryId; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* The actual entity ID to process. |
74
|
|
|
* |
75
|
|
|
* @var integer |
76
|
|
|
*/ |
77
|
|
|
protected $entityId; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* The ID of the recently created URL rewrite. |
81
|
|
|
* |
82
|
|
|
* @var integer |
83
|
|
|
*/ |
84
|
|
|
protected $urlRewriteId; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* The array with the URL rewrites that has to be created. |
88
|
|
|
* |
89
|
|
|
* @var array |
90
|
|
|
*/ |
91
|
|
|
protected $urlRewrites = array(); |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* The array with the category IDs related with the product. |
95
|
|
|
* |
96
|
|
|
* @var array |
97
|
|
|
*/ |
98
|
|
|
protected $productCategoryIds = array(); |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* The product bunch processor instance. |
102
|
|
|
* |
103
|
|
|
* @var \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface |
104
|
|
|
*/ |
105
|
|
|
protected $productUrlRewriteProcessor; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Initialize the observer with the passed product URL rewrite processor instance. |
109
|
|
|
* |
110
|
|
|
* @param \TechDivision\Import\Product\UrlRewrite\Services\ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor The product URL rewrite processor instance |
111
|
|
|
*/ |
112
|
3 |
|
public function __construct(ProductUrlRewriteProcessorInterface $productUrlRewriteProcessor) |
113
|
|
|
{ |
114
|
3 |
|
$this->productUrlRewriteProcessor = $productUrlRewriteProcessor; |
115
|
3 |
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Return's the product bunch processor instance. |
119
|
|
|
* |
120
|
|
|
* @return \TechDivision\Import\Product\Services\ProductBunchProcessorInterface The product bunch processor instance |
121
|
|
|
*/ |
122
|
3 |
|
protected function getProductUrlRewriteProcessor() |
123
|
|
|
{ |
124
|
3 |
|
return $this->productUrlRewriteProcessor; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Will be invoked by the action on the events the listener has been registered for. |
129
|
|
|
* |
130
|
|
|
* @param \TechDivision\Import\Subjects\SubjectInterface $subject The subject instance |
131
|
|
|
* |
132
|
|
|
* @return array The modified row |
133
|
|
|
* @throws \Exception Is thrown, if the product is not available or no URL key has been specified |
134
|
|
|
* @see \TechDivision\Import\Observers\ObserverInterface::handle() |
135
|
|
|
*/ |
136
|
3 |
|
public function handle(SubjectInterface $subject) |
137
|
|
|
{ |
138
|
|
|
|
139
|
|
|
// initialize the row |
140
|
3 |
|
$this->setSubject($subject); |
141
|
3 |
|
$this->setRow($subject->getRow()); |
142
|
|
|
|
143
|
|
|
// try to load the entity ID for the product with the passed SKU |
144
|
3 |
|
if ($product = $this->loadProduct($sku = $this->getValue(ColumnKeys::SKU))) { |
145
|
3 |
|
$this->setLastEntityId($this->entityId = $product[MemberNames::ENTITY_ID]); |
146
|
|
View Code Duplication |
} else { |
|
|
|
|
147
|
|
|
// prepare a log message |
148
|
|
|
$message = sprintf('Product with SKU "%s" can\'t be loaded to create URL rewrites', $sku); |
149
|
|
|
// query whether or not we're in debug mode |
150
|
|
|
if ($this->getSubject()->isDebugMode()) { |
151
|
|
|
$this->getSubject()->getSystemLogger()->warning($message); |
152
|
|
|
return $this->getRow(); |
153
|
|
|
} else { |
154
|
|
|
throw new \Exception($this->appendExceptionSuffix($message)); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
// try to load the URL key |
159
|
3 |
|
if ($this->hasValue(ColumnKeys::URL_KEY)) { |
160
|
3 |
|
$this->urlKey = $this->getValue(ColumnKeys::URL_KEY); |
161
|
|
View Code Duplication |
} else { |
|
|
|
|
162
|
|
|
// prepare a log message |
163
|
|
|
$message = sprintf('Can\'t find a value in column "url_key" for product with SKU "%s"', $sku); |
164
|
|
|
// query whether or not we're in debug mode |
165
|
|
|
if ($this->getSubject()->isDebugMode()) { |
166
|
|
|
$this->getSubject()->getSystemLogger()->warning($message); |
167
|
|
|
return $this->getRow(); |
168
|
|
|
} else { |
169
|
|
|
throw new \Exception($this->appendExceptionSuffix($message)); |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// initialize the store view code |
174
|
3 |
|
$this->getSubject()->prepareStoreViewCode(); |
175
|
|
|
|
176
|
|
|
// load the store view - if no store view has been set we use the admin store view as default |
177
|
3 |
|
$storeViewCode = $this->getSubject()->getStoreViewCode(StoreViewCodes::ADMIN); |
178
|
|
|
|
179
|
|
|
// query whether or not the row has already been processed |
180
|
3 |
View Code Duplication |
if ($this->storeViewHasBeenProcessed($sku, $storeViewCode)) { |
|
|
|
|
181
|
|
|
// log a message |
182
|
|
|
$this->getSubject() |
183
|
|
|
->getSystemLogger() |
184
|
|
|
->debug( |
185
|
|
|
sprintf( |
186
|
|
|
'URL rewrites for SKU "%s" + store view code "%s" has already been processed', |
187
|
|
|
$sku, |
188
|
|
|
$storeViewCode |
189
|
|
|
) |
190
|
|
|
); |
191
|
|
|
|
192
|
|
|
// return without creating any rewrites |
193
|
|
|
return $this->getRow(); |
194
|
|
|
}; |
195
|
|
|
|
196
|
|
|
// stop processing as we don't want to create URL rewrites for the admin store view |
197
|
3 |
View Code Duplication |
if ($storeViewCode === StoreViewCodes::ADMIN) { |
|
|
|
|
198
|
|
|
// log a message and return |
199
|
|
|
$this->getSubject() |
200
|
|
|
->getSystemLogger() |
201
|
|
|
->debug( |
202
|
|
|
sprintf( |
203
|
|
|
'Store with code "%s" is not active, no URL rewrites will be created for product with SKU "%s"', |
204
|
|
|
$storeViewCode, |
205
|
|
|
$sku |
206
|
|
|
) |
207
|
|
|
); |
208
|
|
|
|
209
|
|
|
// return without creating any rewrites |
210
|
|
|
return $this->getRow(); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// stop processing if the store is NOT active |
214
|
3 |
View Code Duplication |
if (!$this->getSubject()->storeIsActive($storeViewCode)) { |
|
|
|
|
215
|
|
|
// log a message and return |
216
|
|
|
$this->getSubject() |
217
|
|
|
->getSystemLogger() |
218
|
|
|
->debug( |
219
|
|
|
sprintf( |
220
|
|
|
'Store with code "%s" is not active, no URL rewrites will be created for product with SKU "%s"', |
221
|
|
|
$storeViewCode, |
222
|
|
|
$sku |
223
|
|
|
) |
224
|
|
|
); |
225
|
|
|
|
226
|
|
|
// return without creating any rewrites |
227
|
|
|
return $this->getRow(); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
// only map the visibility for the product row related to the default store view |
231
|
3 |
|
if (!$this->hasBeenProcessed($sku)) { |
232
|
3 |
|
$this->addEntityIdVisibilityIdMapping($this->getValue(ColumnKeys::VISIBILITY)); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
// do NOT create new URL rewrites, if the product is NOT visible (any more), BUT |
236
|
|
|
// handle existing URL rewrites, e. g. to remove and clean up the URL rewrites |
237
|
3 |
|
if (!$this->isVisible()) { |
238
|
|
|
// log a message |
239
|
|
|
$this->getSubject() |
240
|
|
|
->getSystemLogger() |
241
|
|
|
->debug( |
242
|
|
|
sprintf( |
243
|
|
|
'Product with SKU "%s" is not visible, so no URL rewrites will be created', |
244
|
|
|
$sku |
245
|
|
|
) |
246
|
|
|
); |
247
|
|
|
|
248
|
|
|
// return without creating any rewrites |
249
|
|
|
return $this->getRow(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
// process the functionality and return the row |
253
|
3 |
|
$this->process(); |
254
|
|
|
|
255
|
|
|
// return the processed row |
256
|
3 |
|
return $this->getRow(); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Process the observer's business logic. |
261
|
|
|
* |
262
|
|
|
* @return void |
263
|
|
|
*/ |
264
|
3 |
|
protected function process() |
265
|
|
|
{ |
266
|
|
|
|
267
|
|
|
// prepare the URL rewrites |
268
|
3 |
|
$this->prepareUrlRewrites(); |
269
|
|
|
|
270
|
|
|
// iterate over the categories and create the URL rewrites |
271
|
3 |
|
foreach ($this->urlRewrites as $categoryId => $urlRewrite) { |
272
|
|
|
// initialize and persist the URL rewrite |
273
|
3 |
|
if ($urlRewrite = $this->initializeUrlRewrite($urlRewrite)) { |
274
|
|
|
// initialize URL rewrite and catagory ID |
275
|
3 |
|
$this->categoryId = $categoryId; |
|
|
|
|
276
|
|
|
|
277
|
|
|
try { |
278
|
|
|
// persist the URL rewrite |
279
|
3 |
|
$this->urlRewriteId = $this->persistUrlRewrite($urlRewrite); |
|
|
|
|
280
|
|
|
|
281
|
|
|
/* |
282
|
|
|
* Attention! Stop processing, if this is a root category, because Magento needs explicitly |
283
|
|
|
* NO URL rewrite product category relation to render canonical and meta og:url tag! |
284
|
|
|
*/ |
285
|
3 |
|
if ($this->isRootCategory($this->getCategory($categoryId))) { |
286
|
3 |
|
continue; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
// initialize and persist the URL rewrite product => category relation |
290
|
2 |
|
$urlRewriteProductCategory = $this->initializeUrlRewriteProductCategory( |
291
|
2 |
|
$this->prepareUrlRewriteProductCategoryAttributes() |
292
|
|
|
); |
293
|
|
|
|
294
|
|
|
// persist the URL rewrite product category relation |
295
|
2 |
|
$this->persistUrlRewriteProductCategory($urlRewriteProductCategory); |
296
|
|
|
} catch (\Exception $e) { |
297
|
|
|
// query whether or not debug mode has been enabled |
298
|
|
View Code Duplication |
if ($this->getSubject()->isDebugMode()) { |
|
|
|
|
299
|
|
|
$this->getSubject() |
300
|
|
|
->getSystemLogger() |
301
|
|
|
->warning($this->getSubject()->appendExceptionSuffix($e->getMessage())); |
302
|
|
|
} else { |
303
|
2 |
|
throw $e; |
304
|
|
|
} |
305
|
|
|
} |
306
|
|
|
} |
307
|
|
|
} |
308
|
3 |
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Initialize the category product with the passed attributes and returns an instance. |
312
|
|
|
* |
313
|
|
|
* @param array $attr The category product attributes |
314
|
|
|
* |
315
|
|
|
* @return array The initialized category product |
316
|
|
|
*/ |
317
|
2 |
|
protected function initializeUrlRewrite(array $attr) |
318
|
|
|
{ |
319
|
2 |
|
return $attr; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Initialize the URL rewrite product => category relation with the passed attributes |
324
|
|
|
* and returns an instance. |
325
|
|
|
* |
326
|
|
|
* @param array $attr The URL rewrite product => category relation attributes |
327
|
|
|
* |
328
|
|
|
* @return array The initialized URL rewrite product => category relation |
329
|
|
|
*/ |
330
|
1 |
|
protected function initializeUrlRewriteProductCategory($attr) |
331
|
|
|
{ |
332
|
1 |
|
return $attr; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Prepare's the URL rewrites that has to be created/updated. |
337
|
|
|
* |
338
|
|
|
* @return void |
339
|
|
|
*/ |
340
|
3 |
|
protected function prepareUrlRewrites() |
341
|
|
|
{ |
342
|
|
|
|
343
|
|
|
// (re-)initialize the array for the URL rewrites and the product category IDs |
344
|
3 |
|
$this->urlRewrites = array(); |
345
|
3 |
|
$this->productCategoryIds = array(); |
346
|
|
|
|
347
|
|
|
// load the root category, because we need that to create the default product URL rewrite |
348
|
3 |
|
$rootCategory = $this->getRootCategory(); |
349
|
|
|
|
350
|
|
|
// at least, add the root category ID to the category => product relations |
351
|
3 |
|
$this->productCategoryIds[] = $rootCategory[MemberNames::ENTITY_ID]; |
352
|
|
|
|
353
|
3 |
|
$storeViewCode = $this->getValue(ColumnKeys::STORE_VIEW_CODE); |
354
|
|
|
|
355
|
|
|
// append the category => product relations found |
356
|
3 |
|
foreach ($this->getValue(ColumnKeys::CATEGORIES, array(), array($this, 'explode')) as $path) { |
357
|
|
|
try { |
358
|
|
|
// downgrade the path |
359
|
2 |
|
$path = implode('/', $this->explode($path, '/')); |
360
|
|
|
// try to load the category for the given path |
361
|
2 |
|
$category = $this->getCategoryByPath(trim($path), $storeViewCode); |
362
|
|
|
// resolve the product's categories recursively |
363
|
2 |
|
$this->resolveCategoryIds($category[MemberNames::ENTITY_ID], true, $storeViewCode); |
364
|
|
|
} catch (\Exception $e) { |
365
|
|
|
// query whether or not debug mode has been enabled |
366
|
|
View Code Duplication |
if ($this->getSubject()->isDebugMode()) { |
|
|
|
|
367
|
|
|
$this->getSubject() |
368
|
|
|
->getSystemLogger() |
369
|
|
|
->warning($this->getSubject()->appendExceptionSuffix($e->getMessage())); |
370
|
|
|
} else { |
371
|
2 |
|
throw $e; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
// prepare the URL rewrites |
377
|
3 |
|
foreach ($this->productCategoryIds as $categoryId) { |
378
|
|
|
// set the category ID |
379
|
3 |
|
$this->categoryId = $categoryId; |
380
|
|
|
|
381
|
|
|
// prepare the attributes for each URL rewrite |
382
|
3 |
|
$this->urlRewrites[$categoryId] = $this->prepareAttributes($storeViewCode); |
383
|
|
|
} |
384
|
3 |
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Resolve's the parent categories of the category with the passed ID and relate's |
388
|
|
|
* it with the product with the passed ID, if the category is top level OR has the |
389
|
|
|
* anchor flag set. |
390
|
|
|
* |
391
|
|
|
* @param integer $categoryId The ID of the category to resolve the parents |
392
|
|
|
* @param boolean $topLevel TRUE if the passed category has top level, else FALSE |
393
|
|
|
* @param string $storeViewCode The store view code to resolve the category IDs for |
394
|
|
|
* |
395
|
|
|
* @return void |
396
|
|
|
*/ |
397
|
2 |
|
protected function resolveCategoryIds($categoryId, $topLevel = false, $storeViewCode = StoreViewCodes::ADMIN) |
398
|
|
|
{ |
399
|
|
|
|
400
|
|
|
// return immediately if this is the absolute root node |
401
|
2 |
|
if ((integer) $categoryId === 1) { |
402
|
2 |
|
return; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
// load the data of the category with the passed ID |
406
|
2 |
|
$category = $this->getCategory($categoryId, $storeViewCode); |
407
|
|
|
|
408
|
|
|
// create the product category relation for the current category |
409
|
2 |
|
$this->createProductCategoryRelation($category, $topLevel); |
410
|
2 |
|
|
411
|
|
|
// load the root category |
412
|
2 |
|
$rootCategory = $this->getRootCategory(); |
413
|
2 |
|
|
414
|
|
|
// try to resolve the parent category IDs |
415
|
|
|
if ($rootCategory[MemberNames::ENTITY_ID] !== ($parentId = $category[MemberNames::PARENT_ID])) { |
416
|
|
|
$this->resolveCategoryIds($parentId, false); |
417
|
2 |
|
} |
418
|
2 |
|
} |
419
|
2 |
|
|
420
|
|
|
/** |
421
|
|
|
* Adds the entity product relation if necessary. |
422
|
|
|
* |
423
|
|
|
* @param array $category The category to create the relation for |
424
|
2 |
|
* @param boolean $topLevel Whether or not the category has top level |
425
|
|
|
* |
426
|
|
|
* @return void |
427
|
2 |
|
*/ |
428
|
2 |
|
protected function createProductCategoryRelation($category, $topLevel) |
429
|
|
|
{ |
430
|
2 |
|
|
431
|
|
|
// query whether or not the product has already been related |
432
|
|
|
if (in_array($category[MemberNames::ENTITY_ID], $this->productCategoryIds)) { |
433
|
|
|
return; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
// load the backend configuration value for whether or not the catalog product rewrites should be generated |
437
|
|
|
$generateCategoryRewrites = $this->getGenerateCategoryProductRewritesOptionValue(); |
438
|
|
|
|
439
|
3 |
|
// abort if generating product categories is disabled and category is not root |
440
|
|
|
if ($generateCategoryRewrites === false && $this->isRootCategory($category) === false) { |
441
|
|
|
return; |
442
|
|
|
} |
443
|
3 |
|
|
444
|
|
|
// create relation if the category is top level or has the anchor flag set |
445
|
|
|
if ($topLevel || (integer) $category[MemberNames::IS_ANCHOR] === 1) { |
446
|
3 |
|
$this->productCategoryIds[] = $category[MemberNames::ENTITY_ID]; |
447
|
|
|
return; |
448
|
|
|
} |
449
|
3 |
|
|
450
|
3 |
|
$this->getSubject() |
451
|
3 |
|
->getSystemLogger() |
452
|
|
|
->debug( |
453
|
|
|
sprintf( |
454
|
3 |
|
'Don\'t create URL rewrite for category "%s" because of missing anchor flag', |
455
|
|
|
$category[MemberNames::PATH] |
456
|
3 |
|
) |
457
|
3 |
|
); |
458
|
3 |
|
} |
459
|
3 |
|
|
460
|
3 |
|
/** |
461
|
3 |
|
* Returns the option value for whether or not to generate product catalog rewrites as well. |
462
|
3 |
|
* |
463
|
3 |
|
* @return bool |
464
|
3 |
|
*/ |
465
|
|
|
protected function getGenerateCategoryProductRewritesOptionValue() |
466
|
|
|
{ |
467
|
|
|
return (bool) $this->getSubject()->getCoreConfigData( |
468
|
|
|
CoreConfigDataKeys::CATALOG_SEO_GENERATE_CATEGORY_PRODUCT_REWRITES, |
469
|
|
|
true |
470
|
|
|
); |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
2 |
|
* Prepare the attributes of the entity that has to be persisted. |
475
|
|
|
* |
476
|
|
|
* @param string $storeViewCode The store view code to prepare the attributes for |
477
|
|
|
* |
478
|
2 |
|
* @return array The prepared attributes |
479
|
|
|
*/ |
480
|
2 |
|
protected function prepareAttributes($storeViewCode) |
481
|
2 |
|
{ |
482
|
2 |
|
|
483
|
|
|
// load the store ID to use |
484
|
|
|
$storeId = $this->getSubject()->getRowStoreId(); |
|
|
|
|
485
|
|
|
|
486
|
|
|
// load the category to create the URL rewrite for |
487
|
|
|
$category = $this->getCategory($this->categoryId, $storeViewCode); |
488
|
|
|
|
489
|
|
|
// initialize the values |
490
|
|
|
$metadata = $this->prepareMetadata($category); |
491
|
|
|
$targetPath = $this->prepareTargetPath($category); |
492
|
|
|
$requestPath = $this->prepareRequestPath($category); |
493
|
|
|
|
494
|
3 |
|
// return the prepared URL rewrite |
495
|
|
|
return $this->initializeEntity( |
496
|
|
|
array( |
497
|
|
|
MemberNames::ENTITY_TYPE => UrlRewriteObserver::ENTITY_TYPE, |
498
|
3 |
|
MemberNames::ENTITY_ID => $this->entityId, |
499
|
3 |
|
MemberNames::REQUEST_PATH => $requestPath, |
500
|
|
|
MemberNames::TARGET_PATH => $targetPath, |
501
|
2 |
|
MemberNames::REDIRECT_TYPE => 0, |
502
|
|
|
MemberNames::STORE_ID => $storeId, |
503
|
|
|
MemberNames::DESCRIPTION => null, |
504
|
|
|
MemberNames::IS_AUTOGENERATED => 1, |
505
|
3 |
|
MemberNames::METADATA => $metadata ? json_encode($metadata) : null |
506
|
|
|
) |
507
|
|
|
); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
/** |
511
|
|
|
* Prepare's the URL rewrite product => category relation attributes. |
512
|
|
|
* |
513
|
|
|
* @return array The prepared attributes |
514
|
|
|
*/ |
515
|
|
|
protected function prepareUrlRewriteProductCategoryAttributes() |
516
|
3 |
|
{ |
517
|
|
|
|
518
|
|
|
// return the prepared product |
519
|
|
|
return $this->initializeEntity( |
520
|
3 |
|
array( |
521
|
|
|
MemberNames::PRODUCT_ID => $this->entityId, |
522
|
|
|
MemberNames::CATEGORY_ID => $this->categoryId, |
523
|
3 |
|
MemberNames::URL_REWRITE_ID => $this->urlRewriteId |
524
|
3 |
|
) |
525
|
|
|
); |
526
|
|
|
} |
527
|
2 |
|
|
528
|
2 |
|
/** |
529
|
|
|
* Prepare's the target path for a URL rewrite. |
530
|
|
|
* |
531
|
|
|
* @param array $category The categroy with the URL path |
532
|
|
|
* |
533
|
|
|
* @return string The target path |
534
|
|
|
*/ |
535
|
|
|
protected function prepareTargetPath(array $category) |
536
|
|
|
{ |
537
|
|
|
|
538
|
|
|
// query whether or not, the category is the root category |
539
|
|
|
if ($this->isRootCategory($category)) { |
540
|
|
|
$targetPath = sprintf('catalog/product/view/id/%d', $this->entityId); |
541
|
|
|
} else { |
542
|
|
|
$targetPath = sprintf('catalog/product/view/id/%d/category/%d', $this->entityId, $category[MemberNames::ENTITY_ID]); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
// return the target path |
546
|
|
|
return $targetPath; |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
/** |
550
|
|
|
* Prepare's the request path for a URL rewrite or the target path for a 301 redirect. |
551
|
3 |
|
* |
552
|
|
|
* @param array $category The categroy with the URL path |
553
|
|
|
* |
554
|
|
|
* @return string The request path |
555
|
3 |
|
* @throws \RuntimeException Is thrown, if the passed category has no or an empty value for attribute "url_path" |
556
|
|
|
*/ |
557
|
|
|
protected function prepareRequestPath(array $category) |
558
|
3 |
|
{ |
559
|
3 |
|
|
560
|
|
|
// load the product URL suffix to use |
561
|
|
|
$urlSuffix = $this->getSubject()->getCoreConfigData(CoreConfigDataKeys::CATALOG_SEO_PRODUCT_URL_SUFFIX, '.html'); |
562
|
|
|
|
563
|
2 |
|
// query whether or not, the category is the root category |
564
|
|
|
if ($this->isRootCategory($category)) { |
565
|
|
|
return sprintf('%s%s', $this->urlKey, $urlSuffix); |
566
|
2 |
|
} else { |
567
|
|
|
// query whether or not the category's "url_path" attribute, necessary to create a valid "request_path", is available |
568
|
|
|
if (isset($category[MemberNames::URL_PATH]) && $category[MemberNames::URL_PATH]) { |
569
|
|
|
return sprintf('%s/%s%s', $category[MemberNames::URL_PATH], $this->urlKey, $urlSuffix); |
570
|
|
|
} |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
// throw an exception if the category's "url_path" attribute is NOT available |
574
|
3 |
|
throw new \RuntimeException( |
575
|
|
|
$this->appendExceptionSuffix( |
576
|
3 |
|
sprintf( |
577
|
|
|
'Can\'t find mandatory attribute "%s" for category ID "%d", necessary to build a valid "request_path"', |
578
|
|
|
MemberNames::URL_PATH, |
579
|
|
|
$category[MemberNames::ENTITY_ID] |
580
|
|
|
) |
581
|
|
|
) |
582
|
|
|
); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* Prepare's the URL rewrite's metadata with the passed category values. |
587
|
|
|
* |
588
|
3 |
|
* @param array $category The category used for preparation |
589
|
|
|
* |
590
|
3 |
|
* @return array|null The metadata |
591
|
|
|
*/ |
592
|
|
|
protected function prepareMetadata(array $category) |
593
|
|
|
{ |
594
|
|
|
|
595
|
|
|
// initialize the metadata |
596
|
|
|
$metadata = array(); |
597
|
|
|
|
598
|
|
|
// query whether or not, the passed category IS the root category |
599
|
3 |
|
if ($this->isRootCategory($category)) { |
600
|
|
|
return; |
601
|
3 |
|
} |
602
|
|
|
|
603
|
|
|
// if not, set the category ID in the metadata |
604
|
|
|
$metadata[UrlRewriteObserver::CATEGORY_ID] = (string) $category[MemberNames::ENTITY_ID]; |
605
|
|
|
|
606
|
|
|
// return the metadata |
607
|
|
|
return $metadata; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
3 |
|
* Query whether or not the actual entity is visible. |
612
|
|
|
* |
613
|
|
|
* @return boolean TRUE if the entity is visible, else FALSE |
614
|
|
|
*/ |
615
|
3 |
|
protected function isVisible() |
616
|
|
|
{ |
617
|
|
|
return $this->getEntityIdVisibilityIdMapping() !== VisibilityKeys::VISIBILITY_NOT_VISIBLE; |
618
|
3 |
|
} |
619
|
|
|
|
620
|
|
|
/** |
621
|
|
|
* Return's the visibility for the passed entity ID, if it already has been mapped. The mapping will be created |
622
|
|
|
* by calling <code>\TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue</code> which will |
623
|
|
|
* be done by the <code>\TechDivision\Import\Product\Callbacks\VisibilityCallback</code>. |
624
|
|
|
* |
625
|
|
|
* @return integer The visibility ID |
626
|
|
|
* @throws \Exception Is thrown, if the entity ID has not been mapped |
627
|
|
|
* @see \TechDivision\Import\Product\Subjects\BunchSubject::getVisibilityIdByValue() |
628
|
|
|
*/ |
629
|
2 |
|
protected function getEntityIdVisibilityIdMapping() |
630
|
|
|
{ |
631
|
2 |
|
return $this->getSubject()->getEntityIdVisibilityIdMapping(); |
|
|
|
|
632
|
|
|
} |
633
|
|
|
|
634
|
|
|
/** |
635
|
|
|
* Return's the root category for the actual view store. |
636
|
|
|
* |
637
|
|
|
* @return array The store's root category |
638
|
|
|
* @throws \Exception Is thrown if the root category for the passed store code is NOT available |
639
|
|
|
*/ |
640
|
|
|
protected function getRootCategory() |
641
|
|
|
{ |
642
|
2 |
|
return $this->getSubject()->getRootCategory(); |
|
|
|
|
643
|
|
|
} |
644
|
2 |
|
|
645
|
|
|
/** |
646
|
|
|
* Return's TRUE if the passed category IS the root category, else FALSE. |
647
|
|
|
* |
648
|
|
|
* @param array $category The category to query |
649
|
|
|
* |
650
|
|
|
* @return boolean TRUE if the passed category IS the root category |
651
|
|
|
*/ |
652
|
|
|
protected function isRootCategory(array $category) |
653
|
|
|
{ |
654
|
3 |
|
|
655
|
|
|
// load the root category |
656
|
3 |
|
$rootCategory = $this->getRootCategory(); |
657
|
|
|
|
658
|
|
|
// compare the entity IDs and return the result |
659
|
|
|
return $rootCategory[MemberNames::ENTITY_ID] === $category[MemberNames::ENTITY_ID]; |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
/** |
663
|
|
|
* Return's the category with the passed path. |
664
|
|
|
* |
665
|
|
|
* @param string $path The path of the category to return |
666
|
2 |
|
* @param string $storeViewCode The store view code of the category to return |
667
|
|
|
* |
668
|
2 |
|
* @return array The category |
669
|
|
|
*/ |
670
|
|
|
protected function getCategoryByPath($path, $storeViewCode = StoreViewCodes::ADMIN) |
671
|
|
|
{ |
672
|
|
|
return $this->getSubject()->getCategoryByPath($path, $storeViewCode); |
|
|
|
|
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Return's the category with the passed ID. |
677
|
|
|
* |
678
|
|
|
* @param integer $categoryId The ID of the category to return |
679
|
3 |
|
* @param string $storeViewCode The store view code of category to return |
680
|
|
|
* |
681
|
3 |
|
* @return array The category data |
682
|
|
|
*/ |
683
|
|
|
protected function getCategory($categoryId, $storeViewCode = StoreViewCodes::ADMIN) |
684
|
|
|
{ |
685
|
|
|
return $this->getSubject()->getCategory($categoryId, $storeViewCode); |
|
|
|
|
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
/** |
689
|
|
|
* Persist's the URL rewrite with the passed data. |
690
|
|
|
* |
691
|
3 |
|
* @param array $row The URL rewrite to persist |
692
|
|
|
* |
693
|
3 |
|
* @return string The ID of the persisted entity |
694
|
3 |
|
*/ |
695
|
|
|
protected function persistUrlRewrite($row) |
696
|
|
|
{ |
697
|
|
|
return $this->getProductUrlRewriteProcessor()->persistUrlRewrite($row); |
698
|
|
|
} |
699
|
|
|
|
700
|
|
|
/** |
701
|
|
|
* Persist's the URL rewrite product => category relation with the passed data. |
702
|
|
|
* |
703
|
3 |
|
* @param array $row The URL rewrite product => category relation to persist |
704
|
|
|
* |
705
|
3 |
|
* @return void |
706
|
3 |
|
*/ |
707
|
|
|
protected function persistUrlRewriteProductCategory($row) |
708
|
|
|
{ |
709
|
|
|
return $this->getProductUrlRewriteProcessor()->persistUrlRewriteProductCategory($row); |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* Queries whether or not the passed SKU and store view code has already been processed. |
714
|
|
|
* |
715
|
3 |
|
* @param string $sku The SKU to check been processed |
716
|
|
|
* @param string $storeViewCode The store view code to check been processed |
717
|
3 |
|
* |
718
|
|
|
* @return boolean TRUE if the SKU and store view code has been processed, else FALSE |
719
|
|
|
*/ |
720
|
|
|
protected function storeViewHasBeenProcessed($sku, $storeViewCode) |
721
|
|
|
{ |
722
|
|
|
return $this->getSubject()->storeViewHasBeenProcessed($sku, $storeViewCode); |
|
|
|
|
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
/** |
726
|
|
|
* Add the entity ID => visibility mapping for the actual entity ID. |
727
|
|
|
* |
728
|
|
|
* @param string $visibility The visibility of the actual entity to map |
729
|
|
|
* |
730
|
|
|
* @return void |
731
|
|
|
*/ |
732
|
|
|
protected function addEntityIdVisibilityIdMapping($visibility) |
733
|
|
|
{ |
734
|
|
|
$this->getSubject()->addEntityIdVisibilityIdMapping($visibility); |
|
|
|
|
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
/** |
738
|
|
|
* Set's the ID of the product that has been created recently. |
739
|
|
|
* |
740
|
|
|
* @param string $lastEntityId The entity ID |
741
|
|
|
* |
742
|
|
|
* @return void |
743
|
|
|
*/ |
744
|
|
|
protected function setLastEntityId($lastEntityId) |
745
|
|
|
{ |
746
|
|
|
$this->getSubject()->setLastEntityId($lastEntityId); |
|
|
|
|
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/** |
750
|
|
|
* Load's and return's the product with the passed SKU. |
751
|
|
|
* |
752
|
|
|
* @param string $sku The SKU of the product to load |
753
|
|
|
* |
754
|
|
|
* @return array The product |
755
|
|
|
*/ |
756
|
|
|
protected function loadProduct($sku) |
757
|
|
|
{ |
758
|
|
|
return $this->getProductUrlRewriteProcessor()->loadProduct($sku); |
759
|
|
|
} |
760
|
|
|
} |
761
|
|
|
|
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.