Skip to content

Commit bf26370

Browse files
author
Slabko,Michael(mslabko)
committed
Merge pull request #14 from magento-nord/pr-2.0
Pr 2.0
2 parents 794b23b + a545290 commit bf26370

File tree

15 files changed

+412
-77
lines changed

15 files changed

+412
-77
lines changed

app/code/Magento/CatalogImportExport/Model/Import/Product.php

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
137137
*/
138138
const INVENTORY_USE_CONFIG_PREFIX = 'use_config_';
139139

140+
/**
141+
* Url key attribute code
142+
*/
143+
const URL_KEY = 'url_key';
144+
140145
/**
141146
* Attribute cache
142147
*
@@ -233,6 +238,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
233238
ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage',
234239
ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions',
235240
ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid',
241+
ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified url key is already exist',
236242
];
237243

238244
/**
@@ -502,12 +508,24 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
502508
*/
503509
protected $categoryProcessor;
504510

511+
/** @var \Magento\Framework\App\Config\ScopeConfigInterface */
512+
protected $scopeConfig;
513+
514+
/** @var \Magento\Catalog\Model\Product\Url */
515+
protected $productUrl;
516+
505517
/** @var array */
506518
protected $websitesCache = [];
507519

508520
/** @var array */
509521
protected $categoriesCache = [];
510522

523+
/** @var array */
524+
protected $productUrlSuffix = [];
525+
526+
/** @var array */
527+
protected $productUrlKeys = [];
528+
511529
/**
512530
* Instance of product tax class processor.
513531
*
@@ -561,6 +579,12 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
561579
*/
562580
protected $cachedImages = null;
563581

582+
/** @var array */
583+
protected $urlKeys = [];
584+
585+
/** @var array */
586+
protected $rowNumbers = [];
587+
564588
/**
565589
* @param \Magento\Framework\Json\Helper\Data $jsonHelper
566590
* @param \Magento\ImportExport\Helper\Data $importExportData
@@ -596,6 +620,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
596620
* @param ObjectRelationProcessor $objectRelationProcessor
597621
* @param TransactionManagerInterface $transactionManager
598622
* @param Product\TaxClassProcessor $taxClassProcessor
623+
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
599624
* @param array $data
600625
* @throws \Magento\Framework\Exception\LocalizedException
601626
*
@@ -636,6 +661,8 @@ public function __construct(
636661
ObjectRelationProcessor $objectRelationProcessor,
637662
TransactionManagerInterface $transactionManager,
638663
Product\TaxClassProcessor $taxClassProcessor,
664+
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
665+
\Magento\Catalog\Model\Product\Url $productUrl,
639666
array $data = []
640667
) {
641668
$this->_eventManager = $eventManager;
@@ -663,6 +690,8 @@ public function __construct(
663690
$this->objectRelationProcessor = $objectRelationProcessor;
664691
$this->transactionManager = $transactionManager;
665692
$this->taxClassProcessor = $taxClassProcessor;
693+
$this->scopeConfig = $scopeConfig;
694+
$this->productUrl = $productUrl;
666695
parent::__construct(
667696
$jsonHelper,
668697
$importExportData,
@@ -1999,11 +2028,8 @@ protected function _saveStockItem()
19992028
*/
20002029
public function retrieveAttributeByCode($attrCode)
20012030
{
2002-
if (!$this->_resource) {
2003-
$this->_resource = $this->_resourceFactory->create();
2004-
}
20052031
if (!isset($this->_attributeCache[$attrCode])) {
2006-
$this->_attributeCache[$attrCode] = $this->_resource->getAttribute($attrCode);
2032+
$this->_attributeCache[$attrCode] = $this->getResource()->getAttribute($attrCode);
20072033
}
20082034
return $this->_attributeCache[$attrCode];
20092035
}
@@ -2214,7 +2240,25 @@ public function validateRow(array $rowData, $rowNum)
22142240
}
22152241
// validate custom options
22162242
$this->getOptionEntity()->validateRow($rowData, $rowNum);
2217-
2243+
if (!empty($rowData[self::URL_KEY]) || !empty($rowData[self::COL_NAME])) {
2244+
$urlKey = $this->getUrlKey($rowData);
2245+
$storeCodes = empty($rowData[self::COL_STORE_VIEW_CODE])
2246+
? array_flip($this->storeResolver->getStoreCodeToId())
2247+
: explode($this->getMultipleValueSeparator(), $rowData[self::COL_STORE_VIEW_CODE]);
2248+
foreach ($storeCodes as $storeCode) {
2249+
$storeId = $this->storeResolver->getStoreCodeToId($storeCode);
2250+
$productUrlSuffix = $this->getProductUrlSuffix($storeId);
2251+
$urlPath = $urlKey . $productUrlSuffix;
2252+
if (empty($this->urlKeys[$storeId][$urlPath])
2253+
|| ($this->urlKeys[$storeId][$urlPath] == $rowData[self::COL_SKU])
2254+
) {
2255+
$this->urlKeys[$storeId][$urlPath] = $rowData[self::COL_SKU];
2256+
$this->rowNumbers[$storeId][$urlPath] = $rowNum;
2257+
} else {
2258+
$this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum);
2259+
}
2260+
}
2261+
}
22182262
return !$this->getErrorAggregator()->isRowInvalid($rowNum);
22192263
}
22202264

@@ -2319,7 +2363,79 @@ protected function _saveValidatedBunches()
23192363
$this->validateRow($rowData, $source->key());
23202364
$source->next();
23212365
}
2366+
$this->checkUrlKeyDuplicates();
23222367
$this->getOptionEntity()->validateAmbiguousData();
23232368
return parent::_saveValidatedBunches();
23242369
}
2370+
2371+
/**
2372+
* Check that url_keys are not assigned to other products in DB
2373+
*
2374+
* @return void
2375+
*/
2376+
protected function checkUrlKeyDuplicates()
2377+
{
2378+
$resource = $this->getResource();
2379+
foreach ($this->urlKeys as $storeId => $urlKeys) {
2380+
$urlKeyDuplicates = $this->_connection->fetchAssoc(
2381+
$this->_connection->select()->from(
2382+
['url_rewrite' => $resource->getTable('url_rewrite')],
2383+
['request_path', 'store_id']
2384+
)->joinLeft(
2385+
['cpe' => $resource->getTable('catalog_product_entity')],
2386+
"cpe.entity_id = url_rewrite.entity_id"
2387+
)->where('request_path IN (?)', array_keys($urlKeys))
2388+
->where('store_id IN (?)', $storeId)
2389+
->where('cpe.sku not in (?)', array_values($urlKeys))
2390+
);
2391+
foreach ($urlKeyDuplicates as $entityData) {
2392+
$rowNum = $this->rowNumbers[$entityData['store_id']][$entityData['request_path']];
2393+
$this->addRowError(ValidatorInterface::ERROR_DUPLICATE_URL_KEY, $rowNum);
2394+
}
2395+
}
2396+
}
2397+
2398+
/**
2399+
* Retrieve product rewrite suffix for store
2400+
*
2401+
* @param int $storeId
2402+
* @return string
2403+
*/
2404+
protected function getProductUrlSuffix($storeId = null)
2405+
{
2406+
if (!isset($this->productUrlSuffix[$storeId])) {
2407+
$this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue(
2408+
\Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX,
2409+
\Magento\Store\Model\ScopeInterface::SCOPE_STORE,
2410+
$storeId
2411+
);
2412+
}
2413+
return $this->productUrlSuffix[$storeId];
2414+
}
2415+
2416+
/**
2417+
* @param array $rowData
2418+
* @return string
2419+
*/
2420+
protected function getUrlKey($rowData)
2421+
{
2422+
if (!empty($rowData[self::URL_KEY])) {
2423+
$this->productUrlKeys[$rowData[self::COL_SKU]] = $rowData[self::URL_KEY];
2424+
}
2425+
$urlKey = !empty($this->productUrlKeys[$rowData[self::COL_SKU]])
2426+
? $this->productUrlKeys[$rowData[self::COL_SKU]]
2427+
: $this->productUrl->formatUrlKey($rowData[self::COL_NAME]);
2428+
return $urlKey;
2429+
}
2430+
2431+
/**
2432+
* @return Proxy\Product\ResourceModel
2433+
*/
2434+
protected function getResource()
2435+
{
2436+
if (!$this->_resource) {
2437+
$this->_resource = $this->_resourceFactory->create();
2438+
}
2439+
return $this->_resource;
2440+
}
23252441
}

app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn
7575

7676
const ERROR_MEDIA_PATH_NOT_ACCESSIBLE = 'mediaPathNotAvailable';
7777

78+
const ERROR_DUPLICATE_URL_KEY = 'duplicatedUrlKey';
79+
7880
/**
7981
* Value that means all entities (e.g. websites, groups etc.)
8082
*/

app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ class ProductTest extends \Magento\ImportExport\Test\Unit\Model\Import\AbstractI
154154
*/
155155
protected $errorAggregator;
156156

157+
/** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject*/
158+
protected $scopeConfig;
159+
160+
/** @var \Magento\Catalog\Model\Product\Url|\PHPUnit_Framework_MockObject_MockObject*/
161+
protected $productUrl;
162+
157163
/**
158164
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
159165
*/
@@ -315,6 +321,14 @@ protected function setUp()
315321
->disableOriginalConstructor()
316322
->getMock();
317323

324+
$this->scopeConfig = $this->getMockBuilder('\Magento\Framework\App\Config\ScopeConfigInterface')
325+
->disableOriginalConstructor()
326+
->getMockForAbstractClass();
327+
328+
$this->productUrl = $this->getMockBuilder('\Magento\Catalog\Model\Product\Url')
329+
->disableOriginalConstructor()
330+
->getMock();
331+
318332
$this->errorAggregator = $this->getErrorAggregatorObject();
319333

320334
$this->data = [];
@@ -360,6 +374,8 @@ protected function setUp()
360374
$this->objectRelationProcessor,
361375
$this->transactionManager,
362376
$this->taxClassProcessor,
377+
$this->scopeConfig,
378+
$this->productUrl,
363379
$this->data
364380
);
365381
}

app/code/Magento/CatalogImportExport/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"require": {
55
"php": "~5.5.0|~5.6.0|~7.0.0",
66
"magento/module-catalog": "100.0.*",
7+
"magento/module-catalog-url-rewrite": "100.0.*",
78
"magento/module-eav": "100.0.*",
89
"magento/module-import-export": "100.0.*",
910
"magento/module-store": "100.0.*",

app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function __construct(
119119
public function getJsonSwatchConfig()
120120
{
121121
$attributesData = $this->getSwatchAttributesData();
122-
$allOptionIds = $this->getAllOptionsIdsFromAttributeArray($attributesData);
122+
$allOptionIds = $this->getConfigurableOptionsIds($attributesData);
123123
$swatchesData = $this->swatchHelper->getSwatchesByOptionsId($allOptionIds);
124124

125125
$config = [];
@@ -209,9 +209,9 @@ protected function addSwatchDataForAttribute(
209209
$result = [];
210210
foreach ($options as $optionId => $label) {
211211
if (isset($swatchesCollectionArray[$optionId])) {
212-
$result[$optionId]['label'] = $label;
213212
$result[$optionId] = $this->extractNecessarySwatchData($swatchesCollectionArray[$optionId]);
214213
$result[$optionId] = $this->addAdditionalMediaData($result[$optionId], $optionId, $attributeDataArray);
214+
$result[$optionId]['label'] = $label;
215215
}
216216
}
217217

@@ -278,7 +278,8 @@ protected function getVariationMedia($attributeCode, $optionId)
278278
{
279279
$variationProduct = $this->swatchHelper->loadFirstVariationWithSwatchImage(
280280
$this->getProduct(),
281-
[$attributeCode => $optionId]
281+
$attributeCode,
282+
$optionId
282283
);
283284

284285
$variationMediaArray = [];
@@ -325,15 +326,20 @@ protected function isProductHasImage(Product $product, $imageType)
325326
* @param array $attributeData
326327
* @return array
327328
*/
328-
protected function getAllOptionsIdsFromAttributeArray(array $attributeData)
329+
protected function getConfigurableOptionsIds(array $attributeData)
329330
{
330331
$ids = [];
331-
foreach ($attributeData as $item) {
332-
if (isset($item['options'])) {
333-
$ids = array_merge($ids, array_keys($item['options']));
332+
foreach ($this->getAllowProducts() as $product) {
333+
/** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $attribute */
334+
foreach ($this->helper->getAllowAttributes($this->getProduct()) as $attribute) {
335+
$productAttribute = $attribute->getProductAttribute();
336+
$productAttributeId = $productAttribute->getId();
337+
if (isset($attributeData[$productAttributeId])) {
338+
$ids[$product->getData($productAttribute->getAttributeCode())] = 1;
339+
}
334340
}
335341
}
336-
return $ids;
342+
return array_keys($ids);
337343
}
338344

339345
/**

app/code/Magento/Swatches/Helper/Data.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,30 +155,32 @@ public function populateAdditionalDataEavAttribute(Attribute $attribute)
155155

156156
/**
157157
* @param \Magento\Catalog\Model\Product $product
158-
* @param array $attributes
159-
* @return bool|null
158+
* @param string $attributeCode
159+
* @param string|int $attributeValue
160+
* @return \Magento\Catalog\Model\Product|null
160161
* @throws InputException
161162
*/
162-
public function loadFirstVariationWithSwatchImage($product, array $attributes)
163+
public function loadFirstVariationWithSwatchImage($product, $attributeCode, $attributeValue)
163164
{
164165
$product = $this->createSwatchProduct($product);
165166
if (!$product) {
166-
return false;
167+
return null;
167168
}
168169

169-
$productCollection = $this->prepareVariationCollection($product, $attributes);
170-
170+
$products = $product->getTypeInstance()->getUsedProducts($product);
171171
$variationProduct = null;
172-
foreach ($productCollection as $item) {
173-
$currentProduct = $this->productRepository->getById($item->getId());
174-
$media = $this->getProductMedia($currentProduct);
175-
if (! empty($media) && isset($media['swatch_image'])) {
176-
$variationProduct = $currentProduct;
172+
foreach ($products as $item) {
173+
if ($item->getData($attributeCode) != $attributeValue) {
174+
continue;
175+
}
176+
$media = $this->getProductMedia($item);
177+
if (!empty($media) && isset($media['swatch_image'])) {
178+
$variationProduct = $item;
177179
break;
178180
}
179181
if ($variationProduct !== false) {
180182
if (! empty($media) && isset($media['image'])) {
181-
$variationProduct = $currentProduct;
183+
$variationProduct = $item;
182184
} else {
183185
$variationProduct = false;
184186
}

0 commit comments

Comments
 (0)