diff --git a/vendor/magento/module-catalog-import-export/Model/Import/Product.php b/vendor/magento/module-catalog-import-export/Model/Import/Product.php index e1c9c0ba9f19c..25d261964fb30 100644 --- a/vendor/magento/module-catalog-import-export/Model/Import/Product.php +++ b/vendor/magento/module-catalog-import-export/Model/Import/Product.php @@ -1,7 +1,7 @@ 'Imported resource (image: %s) at row %s could not be downloaded from external resource due to timeout or access permissions', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually', + self::ERROR_DUPLICATE_URL_KEY_BY_CATEGORY => 'Url key: \'%s\' was already generated for a %s with the ID: %s. You need to specify the unique URL key manually', ValidatorInterface::ERROR_DUPLICATE_MULTISELECT_VALUES => 'Value for multiselect attribute %s contains duplicated values', 'invalidNewToDateValue' => 'Make sure new_to_date is later than or the same as new_from_date', // Can't add new translated strings in patch release @@ -933,7 +940,7 @@ public function __construct( $this->stockProcessor = $stockProcessor ?: ObjectManager::getInstance() ->get(StockProcessor::class); $this->linkProcessor = $linkProcessor ?? ObjectManager::getInstance() - ->get(LinkProcessor::class); + ->get(LinkProcessor::class); $this->linkProcessor->addNameToIds($this->_linkNameToId); $this->hashAlgorithm = (version_compare(PHP_VERSION, '8.1.0') >= 0) ? 'xxh128' : 'crc32c'; parent::__construct( @@ -949,7 +956,7 @@ public function __construct( $this->_optionEntity = $data['option_entity'] ?? $optionFactory->create(['data' => ['product_entity' => $this]]); $this->skuStorage = $skuStorage ?? ObjectManager::getInstance() - ->get(SkuStorage::class); + ->get(SkuStorage::class); $this->_initAttributeSets() ->_initTypeModels() ->_initSkus() @@ -957,9 +964,9 @@ public function __construct( $this->validator->init($this); $this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance()->get(DateTimeFactory::class); $this->productRepository = $productRepository ?? ObjectManager::getInstance() - ->get(ProductRepositoryInterface::class); + ->get(ProductRepositoryInterface::class); $this->stockItemProcessor = $stockItemProcessor ?? ObjectManager::getInstance() - ->get(StockItemProcessorInterface::class); + ->get(StockItemProcessorInterface::class); $this->fileDriver = $fileDriver ?? ObjectManager::getInstance() ->get(File::class); } @@ -3109,7 +3116,43 @@ protected function _saveValidatedBunches() } /** - * Check that url_keys are not assigned to other products in DB + * Split SKUs into butches + * + * @param array $data + * @return array + */ + private function splitIntoBatches(array $data): array + { + $batches = []; + $batch = []; + $accumulatedSkuLength = 0; + $accumulatedUrlLength = 0; + foreach ($data as $url => $sku) { + $skuLength = strlen($sku) + self::STRING_PADDING; + $urlLength = strlen($url) + self::STRING_PADDING; + if ($skuLength + $accumulatedSkuLength >= self::STRING_OVERFLOW || + $urlLength + $accumulatedUrlLength >= self::STRING_OVERFLOW + ) { + $batches[] = $batch; + $batch = [$url => $sku]; + $accumulatedSkuLength = $skuLength; + $accumulatedUrlLength = $urlLength; + } else { + $batch[$url] = $sku; + $accumulatedSkuLength += $skuLength; + $accumulatedUrlLength += $urlLength; + } + } + + if (!empty($batch)) { + $batches[] = $batch; + } + + return $batches; + } + + /** + * Check that url_keys are not already assigned to others entities in DB * * @return void * @since 100.0.3 @@ -3118,24 +3161,45 @@ protected function checkUrlKeyDuplicates() { $resource = $this->getResource(); foreach ($this->urlKeys as $storeId => $urlKeys) { - $urlKeyDuplicates = $this->_connection->fetchAssoc( - $this->_connection->select()->from( - ['url_rewrite' => $resource->getTable('url_rewrite')], - ['request_path', 'store_id'] - )->joinLeft( - ['cpe' => $resource->getTable('catalog_product_entity')], - "cpe.entity_id = url_rewrite.entity_id" - )->where('request_path IN (?)', array_map('strval', array_keys($urlKeys))) - ->where('store_id IN (?)', $storeId) - ->where('cpe.sku not in (?)', array_values($urlKeys)) - ); + $requestPaths = $this->splitIntoBatches($urlKeys); + + $urlKeyDuplicates = []; + foreach ($requestPaths as $urlKeys) { + $duplicates = $this->_connection->fetchAssoc( + $this->_connection->select()->from( + ['url_rewrite' => $resource->getTable('url_rewrite')], + [ + 'request_path', + 'store_id', + 'entity_type' + ] + )->joinLeft( + ['cpe' => $resource->getTable('catalog_product_entity')], + "cpe.entity_id = url_rewrite.entity_id" + )->where('request_path IN (?)', array_map('strval', array_keys($urlKeys))) + ->where('store_id IN (?)', $storeId) + ->where('cpe.sku not in (?)', array_values($urlKeys)) + ); + $urlKeyDuplicates = [...$urlKeyDuplicates, ...$duplicates]; + } + foreach ($urlKeyDuplicates as $entityData) { $rowNum = $this->rowNumbers[$entityData['store_id']][$entityData['request_path']]; - $message = sprintf( - $this->retrieveMessageTemplate(ValidatorInterface::ERROR_DUPLICATE_URL_KEY), - $entityData['request_path'], - $entityData['sku'] - ); + if ($entityData['entity_type'] === 'category') { + $message = sprintf( + $this->retrieveMessageTemplate(self::ERROR_DUPLICATE_URL_KEY_BY_CATEGORY), + $entityData['request_path'], + $entityData['entity_type'], + $entityData['entity_id'], + ); + } else { + $message = sprintf( + $this->retrieveMessageTemplate(ValidatorInterface::ERROR_DUPLICATE_URL_KEY), + $entityData['request_path'], + $entityData['sku'] + ); + } + $this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum, 'url_key', $message); } }