@@ -137,6 +137,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
137
137
*/
138
138
const INVENTORY_USE_CONFIG_PREFIX = 'use_config_ ' ;
139
139
140
+ /**
141
+ * Url key attribute code
142
+ */
143
+ const URL_KEY = 'url_key ' ;
144
+
140
145
/**
141
146
* Attribute cache
142
147
*
@@ -233,6 +238,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
233
238
ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage ' ,
234
239
ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions ' ,
235
240
ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid ' ,
241
+ ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified url key is already exist ' ,
236
242
];
237
243
238
244
/**
@@ -502,12 +508,24 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
502
508
*/
503
509
protected $ categoryProcessor ;
504
510
511
+ /** @var \Magento\Framework\App\Config\ScopeConfigInterface */
512
+ protected $ scopeConfig ;
513
+
514
+ /** @var \Magento\Catalog\Model\Product\Url */
515
+ protected $ productUrl ;
516
+
505
517
/** @var array */
506
518
protected $ websitesCache = [];
507
519
508
520
/** @var array */
509
521
protected $ categoriesCache = [];
510
522
523
+ /** @var array */
524
+ protected $ productUrlSuffix = [];
525
+
526
+ /** @var array */
527
+ protected $ productUrlKeys = [];
528
+
511
529
/**
512
530
* Instance of product tax class processor.
513
531
*
@@ -561,6 +579,12 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
561
579
*/
562
580
protected $ cachedImages = null ;
563
581
582
+ /** @var array */
583
+ protected $ urlKeys = [];
584
+
585
+ /** @var array */
586
+ protected $ rowNumbers = [];
587
+
564
588
/**
565
589
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
566
590
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -596,6 +620,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
596
620
* @param ObjectRelationProcessor $objectRelationProcessor
597
621
* @param TransactionManagerInterface $transactionManager
598
622
* @param Product\TaxClassProcessor $taxClassProcessor
623
+ * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
599
624
* @param array $data
600
625
* @throws \Magento\Framework\Exception\LocalizedException
601
626
*
@@ -636,6 +661,8 @@ public function __construct(
636
661
ObjectRelationProcessor $ objectRelationProcessor ,
637
662
TransactionManagerInterface $ transactionManager ,
638
663
Product \TaxClassProcessor $ taxClassProcessor ,
664
+ \Magento \Framework \App \Config \ScopeConfigInterface $ scopeConfig ,
665
+ \Magento \Catalog \Model \Product \Url $ productUrl ,
639
666
array $ data = []
640
667
) {
641
668
$ this ->_eventManager = $ eventManager ;
@@ -663,6 +690,8 @@ public function __construct(
663
690
$ this ->objectRelationProcessor = $ objectRelationProcessor ;
664
691
$ this ->transactionManager = $ transactionManager ;
665
692
$ this ->taxClassProcessor = $ taxClassProcessor ;
693
+ $ this ->scopeConfig = $ scopeConfig ;
694
+ $ this ->productUrl = $ productUrl ;
666
695
parent ::__construct (
667
696
$ jsonHelper ,
668
697
$ importExportData ,
@@ -1288,13 +1317,16 @@ protected function getExistingImages($images)
1288
1317
if (!$ productMediaGalleryTableName ) {
1289
1318
$ productMediaGalleryTableName = $ resource ->getTable ('catalog_product_entity_media_gallery ' );
1290
1319
}
1320
+ $ linkField = $ this ->metadataPool
1321
+ ->getMetadata (\Magento \Catalog \Api \Data \ProductInterface::class)
1322
+ ->getLinkField ();
1291
1323
$ select = $ this ->_connection ->select ()->from (
1292
1324
['mg ' => $ resource ->getTable ('catalog_product_entity_media_gallery ' )],
1293
1325
['value ' => 'mg.value ' ]
1294
1326
)->joinLeft (
1295
1327
['mgvte ' => $ resource ->getTable ('catalog_product_entity_media_gallery_value_to_entity ' )],
1296
1328
'(mg.value_id = mgvte.value_id) ' ,
1297
- [' entity_id ' => 'mgvte.entity_id ' ]
1329
+ [$ linkField => 'mgvte. ' . $ linkField ]
1298
1330
)->where (
1299
1331
'mg.value IN(?) ' ,
1300
1332
$ images
@@ -1999,11 +2031,8 @@ protected function _saveStockItem()
1999
2031
*/
2000
2032
public function retrieveAttributeByCode ($ attrCode )
2001
2033
{
2002
- if (!$ this ->_resource ) {
2003
- $ this ->_resource = $ this ->_resourceFactory ->create ();
2004
- }
2005
2034
if (!isset ($ this ->_attributeCache [$ attrCode ])) {
2006
- $ this ->_attributeCache [$ attrCode ] = $ this ->_resource ->getAttribute ($ attrCode );
2035
+ $ this ->_attributeCache [$ attrCode ] = $ this ->getResource () ->getAttribute ($ attrCode );
2007
2036
}
2008
2037
return $ this ->_attributeCache [$ attrCode ];
2009
2038
}
@@ -2214,7 +2243,25 @@ public function validateRow(array $rowData, $rowNum)
2214
2243
}
2215
2244
// validate custom options
2216
2245
$ this ->getOptionEntity ()->validateRow ($ rowData , $ rowNum );
2217
-
2246
+ if (!empty ($ rowData [self ::URL_KEY ]) || !empty ($ rowData [self ::COL_NAME ])) {
2247
+ $ urlKey = $ this ->getUrlKey ($ rowData );
2248
+ $ storeCodes = empty ($ rowData [self ::COL_STORE_VIEW_CODE ])
2249
+ ? array_flip ($ this ->storeResolver ->getStoreCodeToId ())
2250
+ : explode ($ this ->getMultipleValueSeparator (), $ rowData [self ::COL_STORE_VIEW_CODE ]);
2251
+ foreach ($ storeCodes as $ storeCode ) {
2252
+ $ storeId = $ this ->storeResolver ->getStoreCodeToId ($ storeCode );
2253
+ $ productUrlSuffix = $ this ->getProductUrlSuffix ($ storeId );
2254
+ $ urlPath = $ urlKey . $ productUrlSuffix ;
2255
+ if (empty ($ this ->urlKeys [$ storeId ][$ urlPath ])
2256
+ || ($ this ->urlKeys [$ storeId ][$ urlPath ] == $ rowData [self ::COL_SKU ])
2257
+ ) {
2258
+ $ this ->urlKeys [$ storeId ][$ urlPath ] = $ rowData [self ::COL_SKU ];
2259
+ $ this ->rowNumbers [$ storeId ][$ urlPath ] = $ rowNum ;
2260
+ } else {
2261
+ $ this ->addRowError (ValidatorInterface::ERROR_DUPLICATE_URL_KEY , $ rowNum );
2262
+ }
2263
+ }
2264
+ }
2218
2265
return !$ this ->getErrorAggregator ()->isRowInvalid ($ rowNum );
2219
2266
}
2220
2267
@@ -2319,7 +2366,79 @@ protected function _saveValidatedBunches()
2319
2366
$ this ->validateRow ($ rowData , $ source ->key ());
2320
2367
$ source ->next ();
2321
2368
}
2369
+ $ this ->checkUrlKeyDuplicates ();
2322
2370
$ this ->getOptionEntity ()->validateAmbiguousData ();
2323
2371
return parent ::_saveValidatedBunches ();
2324
2372
}
2373
+
2374
+ /**
2375
+ * Check that url_keys are not assigned to other products in DB
2376
+ *
2377
+ * @return void
2378
+ */
2379
+ protected function checkUrlKeyDuplicates ()
2380
+ {
2381
+ $ resource = $ this ->getResource ();
2382
+ foreach ($ this ->urlKeys as $ storeId => $ urlKeys ) {
2383
+ $ urlKeyDuplicates = $ this ->_connection ->fetchAssoc (
2384
+ $ this ->_connection ->select ()->from (
2385
+ ['url_rewrite ' => $ resource ->getTable ('url_rewrite ' )],
2386
+ ['request_path ' , 'store_id ' ]
2387
+ )->joinLeft (
2388
+ ['cpe ' => $ resource ->getTable ('catalog_product_entity ' )],
2389
+ "cpe.entity_id = url_rewrite.entity_id "
2390
+ )->where ('request_path IN (?) ' , array_keys ($ urlKeys ))
2391
+ ->where ('store_id IN (?) ' , $ storeId )
2392
+ ->where ('cpe.sku not in (?) ' , array_values ($ urlKeys ))
2393
+ );
2394
+ foreach ($ urlKeyDuplicates as $ urlKey => $ entityData ) {
2395
+ $ rowNum = $ this ->rowNumbers [$ entityData ['store_id ' ]][$ entityData ['request_path ' ]];
2396
+ $ this ->addRowError (ValidatorInterface::ERROR_DUPLICATE_URL_KEY , $ rowNum );
2397
+ }
2398
+ }
2399
+ }
2400
+
2401
+ /**
2402
+ * Retrieve product rewrite suffix for store
2403
+ *
2404
+ * @param int $storeId
2405
+ * @return string
2406
+ */
2407
+ protected function getProductUrlSuffix ($ storeId = null )
2408
+ {
2409
+ if (!isset ($ this ->productUrlSuffix [$ storeId ])) {
2410
+ $ this ->productUrlSuffix [$ storeId ] = $ this ->scopeConfig ->getValue (
2411
+ \Magento \CatalogUrlRewrite \Model \ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX ,
2412
+ \Magento \Store \Model \ScopeInterface::SCOPE_STORE ,
2413
+ $ storeId
2414
+ );
2415
+ }
2416
+ return $ this ->productUrlSuffix [$ storeId ];
2417
+ }
2418
+
2419
+ /**
2420
+ * @param array $rowData
2421
+ * @return string
2422
+ */
2423
+ protected function getUrlKey ($ rowData )
2424
+ {
2425
+ if (!empty ($ rowData [self ::URL_KEY ])) {
2426
+ $ this ->productUrlKeys [$ rowData [self ::COL_SKU ]] = $ rowData [self ::URL_KEY ];
2427
+ }
2428
+ $ urlKey = !empty ($ this ->productUrlKeys [$ rowData [self ::COL_SKU ]])
2429
+ ? $ this ->productUrlKeys [$ rowData [self ::COL_SKU ]]
2430
+ : $ this ->productUrl ->formatUrlKey ($ rowData [self ::COL_NAME ]);
2431
+ return $ urlKey ;
2432
+ }
2433
+
2434
+ /**
2435
+ * @return Proxy\Product\ResourceModel
2436
+ */
2437
+ protected function getResource ()
2438
+ {
2439
+ if (!$ this ->_resource ) {
2440
+ $ this ->_resource = $ this ->_resourceFactory ->create ();
2441
+ }
2442
+ return $ this ->_resource ;
2443
+ }
2325
2444
}
0 commit comments