From aaf33a27cd624ec07ab218bd361733ecd28f7d8c Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Fri, 28 Aug 2015 18:25:46 +0300 Subject: [PATCH 1/4] MAGETWO-41796: Unable to import or export products with multi-select attributes --- .../Model/Export/Product.php | 11 +- .../Model/Import/ContextInterface.php | 18 ++ .../Model/Import/Product.php | 77 +++----- .../Import/Product/RowValidatorInterface.php | 8 + .../Import/Product/Type/AbstractType.php | 47 ++++- .../Model/Import/Product/Validator.php | 186 +++++++++++++++++- .../Model/Import/Product/ValidatorTest.php | 25 ++- .../Test/Unit/Model/Import/ProductTest.php | 163 +++++++-------- .../Model/Import/Entity/AbstractEntity.php | 14 ++ 9 files changed, 407 insertions(+), 142 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 1d2b3366d22bd..fd5cb05d6ad35 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -880,8 +880,15 @@ protected function collectRawData() ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue; } $data[$itemId][$storeId][$fieldName] = $attrValue; - } else { - $this->collectMultiselectValues($item, $code, $storeId); + } + } else { + $this->collectMultiselectValues($item, $code, $storeId); + if (!empty($this->collectedMultiselectsData[$storeId][$itemId][$code])) { + $additionalAttributes[$code] = $fieldName . + ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode( + ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, + $this->collectedMultiselectsData[$storeId][$itemId][$code] + ); } } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/ContextInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/ContextInterface.php index 4931703b59a17..2d81af5eb59cf 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/ContextInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/ContextInterface.php @@ -27,4 +27,22 @@ public function getParams(); * @return mixed */ public function getParam($name); + + /** + * Get product type by name + * + * @param string $type + * + * @return mixed + */ + public function retrieveProductTypeByName($type); + + /** + * Get message template + * + * @param string $templateName + * + * @return mixed + */ + public function retrieveMessageTemplate($templateName); } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 591e2c2d565d5..1045b74c00079 100755 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -226,6 +226,10 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity i ValidatorInterface::ERROR_SUPER_PRODUCTS_SKU_NOT_FOUND => 'Product with specified super products SKU not found', ValidatorInterface::ERROR_MEDIA_DATA_INCOMPLETE => 'Media data is incomplete', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', + ValidatorInterface::ERROR_EXCEEDED_MAX_LENGTH => 'Attribute %s exceeded max length', + ValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE => 'Value for \'%s\' attribute contains incorrect value, acceptable values are in %s format', + ValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE => 'Duplicated unique attribute', + ValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION => 'Value for \'%s\' attribute contains incorrect value, see acceptable values on settings specified for Admin', ]; /** @@ -236,8 +240,6 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity i protected $_fieldsMap = [ 'image' => 'base_image', 'image_label' => "base_image_label", - 'image' => 'base_image', - 'image_label' => 'base_image_label', 'thumbnail' => 'thumbnail_image', 'thumbnail_label' => 'thumbnail_image_label', self::COL_MEDIA_IMAGE => 'additional_images', @@ -670,53 +672,17 @@ public function __construct( * @param array $attrParams Attribute params * @param array $rowData Row data * @param int $rowNum - * - * @return boolean - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @return bool */ public function isAttributeValid($attrCode, array $attrParams, array $rowData, $rowNum) { - switch ($attrParams['type']) { - case 'varchar': - $val = $this->string->cleanString($rowData[$attrCode]); - $valid = $this->string->strlen($val) < self::DB_MAX_VARCHAR_LENGTH; - break; - case 'decimal': - $val = trim($rowData[$attrCode]); - $valid = is_numeric($val); - break; - case 'select': - case 'multiselect': - $valid = isset($attrParams['options'][strtolower($rowData[$attrCode])]); - break; - case 'int': - $val = trim($rowData[$attrCode]); - $valid = (string)(int)$val === $val; - break; - case 'datetime': - $val = trim($rowData[$attrCode]); - $valid = strtotime($val) !== false; - break; - case 'text': - $val = $this->string->cleanString($rowData[$attrCode]); - $valid = $this->string->strlen($val) < self::DB_MAX_TEXT_LENGTH; - break; - default: - $valid = true; - break; - } - - if (!$valid) { - $this->addRowError(__("Please correct the value for '%s'."), $rowNum, $attrCode); - } elseif (!empty($attrParams['is_unique'])) { - if (isset($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]]) && ($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] != $rowData[self::COL_SKU])) { - $this->addRowError(__("Duplicate Unique Attribute for '%s'"), $rowNum, $attrCode); - return false; + if (!$this->validator->isAttributeValid($attrCode, $attrParams, $rowData)) { + foreach ($this->validator->getMessages() as $message) { + $this->addRowError($message, $rowNum, $attrCode); } - $this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[self::COL_SKU]; + return false; } - return (bool)$valid; + return true; } /** @@ -1571,16 +1537,7 @@ protected function _saveProducts() } } foreach ($storeIds as $storeId) { - if ('multiselect' == $attribute->getFrontendInput()) { - if (!isset($attributes[$attrTable][$rowSku][$attrId][$storeId])) { - $attributes[$attrTable][$rowSku][$attrId][$storeId] = ''; - } else { - $attributes[$attrTable][$rowSku][$attrId][$storeId] .= ','; - } - $attributes[$attrTable][$rowSku][$attrId][$storeId] .= $attrValue; - } else { - $attributes[$attrTable][$rowSku][$attrId][$storeId] = $attrValue; - } + $attributes[$attrTable][$rowSku][$attrId][$storeId] = $attrValue; } // restore 'backend_model' to avoid 'default' setting $attribute->setBackendModel($backModel); @@ -2310,4 +2267,16 @@ public function getParam($name) { return isset($this->_parameters[$name]) ? $this->_parameters[$name] : null; } + + /** + * @param string $name + * @return mixed + */ + public function retrieveProductTypeByName($name) + { + if (isset($this->_productTypeModels[$name])) { + return $this->_productTypeModels[$name]; + } + return null; + } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php index 4e986422eee52..4842817120ddf 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php @@ -61,6 +61,14 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn const ERROR_INVALID_WEIGHT = 'invalidWeight'; + const ERROR_EXCEEDED_MAX_LENGTH = 'exceededMaxLength'; + + const ERROR_INVALID_ATTRIBUTE_TYPE = 'invalidAttributeType'; + + const ERROR_DUPLICATE_UNIQUE_ATTRIBUTE = 'duplicatedUniqueAttribute'; + + const ERROR_INVALID_ATTRIBUTE_OPTION = 'absentAttributeOption'; + /** * Value that means all entities (e.g. websites, groups etc.) */ diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index ea310c2cd829f..099e2a1cfc7f0 100755 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -5,6 +5,8 @@ */ namespace Magento\CatalogImportExport\Model\Import\Product\Type; +use Magento\CatalogImportExport\Model\Import\Product; + /** * Import entity abstract product type model * @@ -19,6 +21,13 @@ abstract class AbstractType */ public static $commonAttributesCache = []; + /** + * Attribute Code to Id cache + * + * @var array + */ + public static $attributeCodeToId = []; + /** * Product type attribute sets and attributes parameters. * @@ -358,6 +367,16 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) // check value for non-empty in the case of required attribute? if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) { $error |= !$this->_entityModel->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); + $resultAttrs[$attrCode] = 'select' == $attrParams['type'] ? $attrParams['options'][strtolower( + $rowData[$attrCode] + )] : $rowData[$attrCode]; + if ('multiselect' == $attrParams['type']) { + $resultAttrs[$attrCode] = []; + foreach (explode('|', $rowData[$attrCode]) as $value) { + $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; + } + $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); + } } elseif ($this->_isAttributeRequiredCheckNeeded($attrCode) && $attrParams['is_required']) { // For the default scope - if this is a new product or // for an old product, if the imported doc has the column present for the attrCode @@ -412,10 +431,16 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) { if (!$attrParams['is_static']) { if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) { - $resultAttrs[$attrCode] = 'select' == $attrParams['type'] || - 'multiselect' == $attrParams['type'] ? $attrParams['options'][strtolower( - $rowData[$attrCode] + $resultAttrs[$attrCode] = 'select' == $attrParams['type'] ? $attrParams['options'][strtolower( + $rowData[$attrCode] )] : $rowData[$attrCode]; + if ('multiselect' == $attrParams['type']) { + $resultAttrs[$attrCode] = []; + foreach (explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]) as $value) { + $resultAttrs[$attrCode][] = $attrParams['options'][strtolower($value)]; + } + $resultAttrs[$attrCode] = implode(',', $resultAttrs[$attrCode]); + } } elseif (array_key_exists($attrCode, $rowData)) { $resultAttrs[$attrCode] = $rowData[$attrCode]; } elseif ($withDefaultValue && null !== $attrParams['default_value']) { @@ -452,4 +477,20 @@ public function saveData() { return $this; } + + /** + * Retrieve attribute from cache + * + * @param string $attributeCode + * @return mixed + */ + public function retrieveAttributeFromCache($attributeCode) + { + if (isset(self::$attributeCodeToId[$attributeCode]) && $id = self::$attributeCodeToId[$attributeCode]) { + if (isset(self::$commonAttributesCache[$id])) { + return self::$commonAttributesCache[$id]; + } + } + return []; + } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index ea0ed43bcdff9..bdc7c6722d6a0 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogImportExport\Model\Import\Product; +use Magento\CatalogImportExport\Model\Import\Product; use Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractImportValidator; class Validator extends AbstractImportValidator implements RowValidatorInterface @@ -15,11 +16,187 @@ class Validator extends AbstractImportValidator implements RowValidatorInterface protected $validators = []; /** - * @param RowValidatorInterface[] $validators + * @var \Magento\Framework\Stdlib\String */ - public function __construct($validators = []) - { + protected $string; + + /** + * @var array + */ + protected $_uniqueAttributes; + + /** + * @var array + */ + protected $_rowData; + + /** + * @param \Magento\Framework\Stdlib\String $string + * @param array $validators + */ + public function __construct( + \Magento\Framework\Stdlib\String $string, + $validators = [] + ) { $this->validators = $validators; + $this->string = $string; + } + + + /** + * @param mixed $attrCode + * @param string $type + * @return bool + */ + protected function textValidation($attrCode, $type) + { + $val = $this->string->cleanString($this->_rowData[$attrCode]); + if ($type == 'text') { + $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; + } else { + $valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH; + } + if (!$valid) { + $this->_addMessages([RowValidatorInterface::ERROR_EXCEEDED_MAX_LENGTH]); + } + return $valid; + } + /** + * @param mixed $attrCode + * @param string $type + * @return bool + */ + protected function numericValidation($attrCode, $type) + { + $val = trim($this->_rowData[$attrCode]); + if ($type == 'int') { + $valid = (string)(int)$val === $val; + } else { + $valid = is_numeric($val); + } + if (!$valid) { + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate(RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE), + $attrCode, + $type + ) + ] + ); + } + return $valid; + } + /** + * @param string $attrCode + * @param array $attrParams + * @param array $rowData + * @return bool + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function isAttributeValid($attrCode, array $attrParams, array $rowData) + { + $this->_rowData = $rowData; + if (!empty($attrParams['apply_to']) && !in_array($rowData['product_type'], $attrParams['apply_to'])) { + return true; + } + if ($attrCode == Product::COL_SKU || $attrParams['is_required'] + && ($this->context->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_REPLACE + || ($this->context->getBehavior() == \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND + && !isset($this->context->getOldSku()[$rowData[$attrCode]]))) + ) { + if (!isset($rowData[$attrCode]) || !strlen(trim($rowData[$attrCode]))) { + $valid = false; + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate( + RowValidatorInterface::ERROR_VALUE_IS_REQUIRED + ), + $attrCode + ) + ] + ); + return $valid; + } + } + if (!strlen(trim($rowData[$attrCode]))) { + return true; + } + switch ($attrParams['type']) { + case 'varchar': + case 'text': + $valid = $this->textValidation($attrCode, $attrParams['type']); + break; + case 'decimal': + case 'int': + $valid = $this->numericValidation($attrCode, $attrParams['type']); + break; + case 'select': + case 'multiselect': + $values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]); + $valid = true; + foreach ($values as $value) { + $valid = $valid || isset($attrParams['options'][strtolower($value)]); + } + if (!$valid) { + $this->_addMessages( + [ + sprintf( + $this->context->retrieveMessageTemplate( + RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_OPTION + ), + $attrCode + ) + ] + ); + } + break; + case 'datetime': + $val = trim($rowData[$attrCode]); + $valid = strtotime($val) !== false; + if (!$valid) { + $this->_addMessages([RowValidatorInterface::ERROR_INVALID_ATTRIBUTE_TYPE]); + } + break; + default: + $valid = true; + break; + } + if ($valid && !empty($attrParams['is_unique'])) { + if (isset($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]]) + && ($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] != $rowData[Product::COL_SKU])) { + $this->_addMessages([RowValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE]); + return false; + } + $this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[Product::COL_SKU]; + } + return (bool)$valid; + } + /** + * @return bool + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + protected function isValidAttributes() + { + $this->_clearMessages(); + if (!isset($this->_rowData['product_type'])) { + return false; + } + $entityTypeModel = $this->context->retrieveProductTypeByName($this->_rowData['product_type']); + if ($entityTypeModel) { + foreach ($this->_rowData as $attrCode => $attrValue) { + $attrParams = $entityTypeModel->retrieveAttributeFromCache($attrCode); + if ($attrParams) { + $this->isAttributeValid($attrCode, $attrParams, $this->_rowData); + } + } + if ($this->getMessages()) { + return false; + } + } + return true; } /** @@ -27,8 +204,9 @@ public function __construct($validators = []) */ public function isValid($value) { - $returnValue = true; + $this->_rowData = $value; $this->_clearMessages(); + $returnValue = $this->isValidAttributes(); foreach ($this->validators as $validator) { if (!$validator->isValid($value)) { $returnValue = false; diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php index 2440c44a100e6..338c976d5649c 100755 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/Product/ValidatorTest.php @@ -19,6 +19,9 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase /** @var array */ protected $validators = []; + /** @var \Magento\CatalogImportExport\Model\Import\Product|\PHPUnit_Framework_MockObject_MockObject */ + protected $context; + /** @var Validator\Media|\PHPUnit_Framework_MockObject_MockObject */ protected $validatorOne; @@ -27,6 +30,25 @@ class ValidatorTest extends \PHPUnit_Framework_TestCase protected function setUp() { + $entityTypeModel = $this->getMock( + 'Magento\CatalogImportExport\Model\Import\Product\Type\Simple', + ['retrieveAttributeFromCache'], + [], + '', + false + ); + $entityTypeModel->expects($this->any())->method('retrieveAttributeFromCache')->willReturn([]); + $this->context = $this->getMock( + '\Magento\CatalogImportExport\Model\Import\Product', + ['retrieveProductTypeByName', 'retrieveMessageTemplate'], + [], + '', + false + ); + $this->context->expects($this->any())->method('retrieveProductTypeByName')->willReturn($entityTypeModel); + $this->context->expects($this->any())->method('retrieveMessageTemplate')->willReturn(''); + + $this->validatorOne = $this->getMock( 'Magento\CatalogImportExport\Model\Import\Product\Validator\Media', [], @@ -49,11 +71,12 @@ protected function setUp() 'Magento\CatalogImportExport\Model\Import\Product\Validator', ['validators' => $this->validators] ); + $this->validator->setContext($this->context)->init(); } public function testIsValidCorrect() { - $value = 'val'; + $value = ['product_type' => 'simple']; $this->validatorOne->expects($this->once())->method('isValid')->with($value)->willReturn(true); $this->validatorTwo->expects($this->once())->method('isValid')->with($value)->willReturn(true); $result = $this->validator->isValid($value); diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index f2f0285e19aef..1cbaaf0997bb4 100755 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -530,9 +530,11 @@ public function testIsAttributeValidAssertAttrValid($attrParams, $rowData) { $attrCode = 'code'; $rowNum = 0; - $string = $this->getMockBuilder('\Magento\Framework\Stdlib\String')->setMethods(null)->getMock(); + $string = $this->getMockBuilder('\Magento\Framework\Stdlib\StringUtils')->setMethods(null)->getMock(); $this->setPropertyValue($this->importProduct, 'string', $string); + $this->validator->expects($this->once())->method('isAttributeValid')->willReturn(true); + $result = $this->importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); $this->assertTrue($result); } @@ -544,90 +546,95 @@ public function testIsAttributeValidAssertAttrInvalid($attrParams, $rowData) { $attrCode = 'code'; $rowNum = 0; - $string = $this->getMockBuilder('\Magento\Framework\Stdlib\String')->setMethods(null)->getMock(); + $string = $this->getMockBuilder('\Magento\Framework\Stdlib\StringUtils')->setMethods(null)->getMock(); $this->setPropertyValue($this->importProduct, 'string', $string); + $this->validator->expects($this->once())->method('isAttributeValid')->willReturn(false); + $messages = ['validator message']; + $this->validator->expects($this->once())->method('getMessages')->willReturn($messages); + $result = $this->importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); $this->assertFalse($result); } - public function testIsAttributeValidNotValidAddErrorCall() - { - $attrCode = 'code'; - $attrParams = [ - 'type' => 'decimal', - ]; - $rowData = [ - $attrCode => 'incorrect' - ]; - $rowNum = 0; - - $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') - ->disableOriginalConstructor() - ->setMethods(['addRowError']) - ->getMock(); - $importProduct->expects($this->once())->method('addRowError'); - - $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); - } - - public function testIsAttributeValidOnDuplicateAddErrorCall() - { - $attrCode = 'code'; - $attrCodeVal = 1000; - $expectedSkuVal = 'sku_val'; - $testSkuVal = 'some_sku'; - $attrParams = [ - 'type' => 'decimal', - 'is_unique' => true, - ]; - $rowData = [ - $attrCode => $attrCodeVal, - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal - ]; - $rowNum = 0; - - $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') - ->disableOriginalConstructor() - ->setMethods(['addRowError']) - ->getMock(); - $importProduct->expects($this->once())->method('addRowError'); - $this->setPropertyValue($importProduct, '_uniqueAttributes', [ - $attrCode => [$attrCodeVal => $testSkuVal] - ]); - - $importProduct->expects($this->once())->method('addRowError'); - - $return = $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); - - $this->assertFalse($return); - } - - public function testIsAttributeValidAddIntoUniqueueAttributes() - { - $attrCode = 'code'; - $attrCodeVal = 1000; - $expectedSkuVal = 'sku_val'; - $attrParams = [ - 'type' => 'decimal', - 'is_unique' => true, - ]; - $rowData = [ - $attrCode => $attrCodeVal, - \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal - ]; - $rowNum = 0; - $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') - ->disableOriginalConstructor() - ->setMethods(null) - ->getMock(); - - $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); - - $_uniqueAttributes = $this->getPropertyValue($importProduct, '_uniqueAttributes'); - $this->assertEquals($expectedSkuVal, $_uniqueAttributes[$attrCode][$rowData[$attrCode]]); - } +// public function testIsAttributeValidNotValidAddErrorCall() +// { +// $attrCode = 'code'; +// $attrParams = [ +// 'type' => 'decimal', +// ]; +// $rowData = [ +// $attrCode => 'incorrect' +// ]; +// $rowNum = 0; +// +// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') +// ->disableOriginalConstructor() +// ->setMethods(['addRowError']) +// ->getMock(); +// $importProduct->expects($this->once())->method('addRowError'); +// +// $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); +// } + +// public function testIsAttributeValidOnDuplicateAddErrorCall() +// { +// $attrCode = 'code'; +// $attrCodeVal = 1000; +// $expectedSkuVal = 'sku_val'; +// $testSkuVal = 'some_sku'; +// $attrParams = [ +// 'type' => 'decimal', +// 'is_unique' => true, +// ]; +// $rowData = [ +// $attrCode => $attrCodeVal, +// \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal +// ]; +// $rowNum = 0; +// +// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') +// ->disableOriginalConstructor() +// ->setMethods(['addRowError']) +// ->getMock(); +// $importProduct->expects($this->once())->method('addRowError'); +// $this->setPropertyValue($importProduct, '_uniqueAttributes', [ +// $attrCode => [$attrCodeVal => $testSkuVal] +// ]); +// +// $importProduct->expects($this->once())->method('addRowError'); +// +// $return = $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); +// +// $this->assertFalse($return); +// } + +// public function testIsAttributeValidAddIntoUniqueueAttributes() +// { +// $attrCode = 'code'; +// $attrCodeVal = 1000; +// $expectedSkuVal = 'sku_val'; +// $attrParams = [ +// 'type' => 'decimal', +// 'is_unique' => true, +// ]; +// $rowData = [ +// $attrCode => $attrCodeVal, +// \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal +// ]; +// $rowNum = 0; +// +// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') +// ->disableOriginalConstructor() +// ->setMethods(null) +// ->getMock(); +// +// $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); +// +// $_uniqueAttributes = $this->getPropertyValue($importProduct, '_uniqueAttributes'); +// $this->assertEquals($expectedSkuVal, $_uniqueAttributes[$attrCode][$rowData[$attrCode]]); +// } public function testGetMultipleValueSeparatorDefault() { diff --git a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php index aa893f2bde3dc..5a1074997e446 100644 --- a/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php +++ b/app/code/Magento/ImportExport/Model/Import/Entity/AbstractEntity.php @@ -814,4 +814,18 @@ public function getDeletedItemsCount() { return $this->countItemsDeleted; } + + /** + * Retrieve message template + * + * @param string $errorCode + * @return null|string + */ + public function retrieveMessageTemplate($errorCode) + { + if (isset($this->_messageTemplates[$errorCode])) { + return $this->_messageTemplates[$errorCode]; + } + return null; + } } From c70804b5426f79d51597aaf066b3b520bca775b7 Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Fri, 28 Aug 2015 18:28:54 +0300 Subject: [PATCH 2/4] MAGETWO-41796: Unable to import or export products with multi-select attributes --- .../Model/Import/Product/Validator.php | 4 +- .../Test/Unit/Model/Import/ProductTest.php | 79 ------------------- 2 files changed, 3 insertions(+), 80 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index bdc7c6722d6a0..3b6c709f65729 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -42,7 +42,6 @@ public function __construct( $this->string = $string; } - /** * @param mixed $attrCode * @param string $type @@ -61,6 +60,7 @@ protected function textValidation($attrCode, $type) } return $valid; } + /** * @param mixed $attrCode * @param string $type @@ -87,6 +87,7 @@ protected function numericValidation($attrCode, $type) } return $valid; } + /** * @param string $attrCode * @param array $attrParams @@ -174,6 +175,7 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) } return (bool)$valid; } + /** * @return bool * @SuppressWarnings(PHPMD.UnusedLocalVariable) diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php index 1cbaaf0997bb4..d33d12b3c2be2 100755 --- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php +++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Import/ProductTest.php @@ -557,85 +557,6 @@ public function testIsAttributeValidAssertAttrInvalid($attrParams, $rowData) $this->assertFalse($result); } - -// public function testIsAttributeValidNotValidAddErrorCall() -// { -// $attrCode = 'code'; -// $attrParams = [ -// 'type' => 'decimal', -// ]; -// $rowData = [ -// $attrCode => 'incorrect' -// ]; -// $rowNum = 0; -// -// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') -// ->disableOriginalConstructor() -// ->setMethods(['addRowError']) -// ->getMock(); -// $importProduct->expects($this->once())->method('addRowError'); -// -// $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); -// } - -// public function testIsAttributeValidOnDuplicateAddErrorCall() -// { -// $attrCode = 'code'; -// $attrCodeVal = 1000; -// $expectedSkuVal = 'sku_val'; -// $testSkuVal = 'some_sku'; -// $attrParams = [ -// 'type' => 'decimal', -// 'is_unique' => true, -// ]; -// $rowData = [ -// $attrCode => $attrCodeVal, -// \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal -// ]; -// $rowNum = 0; -// -// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') -// ->disableOriginalConstructor() -// ->setMethods(['addRowError']) -// ->getMock(); -// $importProduct->expects($this->once())->method('addRowError'); -// $this->setPropertyValue($importProduct, '_uniqueAttributes', [ -// $attrCode => [$attrCodeVal => $testSkuVal] -// ]); -// -// $importProduct->expects($this->once())->method('addRowError'); -// -// $return = $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); -// -// $this->assertFalse($return); -// } - -// public function testIsAttributeValidAddIntoUniqueueAttributes() -// { -// $attrCode = 'code'; -// $attrCodeVal = 1000; -// $expectedSkuVal = 'sku_val'; -// $attrParams = [ -// 'type' => 'decimal', -// 'is_unique' => true, -// ]; -// $rowData = [ -// $attrCode => $attrCodeVal, -// \Magento\CatalogImportExport\Model\Import\Product::COL_SKU => $expectedSkuVal -// ]; -// $rowNum = 0; -// -// $importProduct = $this->getMockBuilder('\Magento\CatalogImportExport\Model\Import\Product') -// ->disableOriginalConstructor() -// ->setMethods(null) -// ->getMock(); -// -// $importProduct->isAttributeValid($attrCode, $attrParams, $rowData, $rowNum); -// -// $_uniqueAttributes = $this->getPropertyValue($importProduct, '_uniqueAttributes'); -// $this->assertEquals($expectedSkuVal, $_uniqueAttributes[$attrCode][$rowData[$attrCode]]); -// } - public function testGetMultipleValueSeparatorDefault() { $this->setPropertyValue($this->importProduct, '_parameters', null); From 6140aff204e936bd1d18278888002f245425b89c Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Fri, 28 Aug 2015 20:23:49 +0300 Subject: [PATCH 3/4] MAGETWO-41796: Unable to import or export products with multi-select attributes --- .../Import/Product/Type/AbstractType.php | 3 ++- .../Model/Import/Product/Validator.php | 26 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index 099e2a1cfc7f0..f55b6a0c163cb 100755 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -486,7 +486,8 @@ public function saveData() */ public function retrieveAttributeFromCache($attributeCode) { - if (isset(self::$attributeCodeToId[$attributeCode]) && $id = self::$attributeCodeToId[$attributeCode]) { + if (isset(self::$attributeCodeToId[$attributeCode])) { + $id = self::$attributeCodeToId[$attributeCode]; if (isset(self::$commonAttributesCache[$id])) { return self::$commonAttributesCache[$id]; } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php index 3b6c709f65729..136317cc80e8e 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php @@ -23,12 +23,12 @@ class Validator extends AbstractImportValidator implements RowValidatorInterface /** * @var array */ - protected $_uniqueAttributes; + protected $uniqueAttributes; /** * @var array */ - protected $_rowData; + protected $rowData; /** * @param \Magento\Framework\Stdlib\String $string @@ -49,7 +49,7 @@ public function __construct( */ protected function textValidation($attrCode, $type) { - $val = $this->string->cleanString($this->_rowData[$attrCode]); + $val = $this->string->cleanString($this->rowData[$attrCode]); if ($type == 'text') { $valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH; } else { @@ -68,7 +68,7 @@ protected function textValidation($attrCode, $type) */ protected function numericValidation($attrCode, $type) { - $val = trim($this->_rowData[$attrCode]); + $val = trim($this->rowData[$attrCode]); if ($type == 'int') { $valid = (string)(int)$val === $val; } else { @@ -98,7 +98,7 @@ protected function numericValidation($attrCode, $type) */ public function isAttributeValid($attrCode, array $attrParams, array $rowData) { - $this->_rowData = $rowData; + $this->rowData = $rowData; if (!empty($attrParams['apply_to']) && !in_array($rowData['product_type'], $attrParams['apply_to'])) { return true; } @@ -166,12 +166,12 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) break; } if ($valid && !empty($attrParams['is_unique'])) { - if (isset($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]]) - && ($this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] != $rowData[Product::COL_SKU])) { + if (isset($this->uniqueAttributes[$attrCode][$rowData[$attrCode]]) + && ($this->uniqueAttributes[$attrCode][$rowData[$attrCode]] != $rowData[Product::COL_SKU])) { $this->_addMessages([RowValidatorInterface::ERROR_DUPLICATE_UNIQUE_ATTRIBUTE]); return false; } - $this->_uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[Product::COL_SKU]; + $this->uniqueAttributes[$attrCode][$rowData[$attrCode]] = $rowData[Product::COL_SKU]; } return (bool)$valid; } @@ -183,15 +183,15 @@ public function isAttributeValid($attrCode, array $attrParams, array $rowData) protected function isValidAttributes() { $this->_clearMessages(); - if (!isset($this->_rowData['product_type'])) { + if (!isset($this->rowData['product_type'])) { return false; } - $entityTypeModel = $this->context->retrieveProductTypeByName($this->_rowData['product_type']); + $entityTypeModel = $this->context->retrieveProductTypeByName($this->rowData['product_type']); if ($entityTypeModel) { - foreach ($this->_rowData as $attrCode => $attrValue) { + foreach ($this->rowData as $attrCode => $attrValue) { $attrParams = $entityTypeModel->retrieveAttributeFromCache($attrCode); if ($attrParams) { - $this->isAttributeValid($attrCode, $attrParams, $this->_rowData); + $this->isAttributeValid($attrCode, $attrParams, $this->rowData); } } if ($this->getMessages()) { @@ -206,7 +206,7 @@ protected function isValidAttributes() */ public function isValid($value) { - $this->_rowData = $value; + $this->rowData = $value; $this->_clearMessages(); $returnValue = $this->isValidAttributes(); foreach ($this->validators as $validator) { From e9b095110925169ad4c4b11469a18ab946b64cdc Mon Sep 17 00:00:00 2001 From: Roman Ganin Date: Tue, 1 Sep 2015 19:02:38 +0300 Subject: [PATCH 4/4] MAGETWO-41796: Unable to import or export products with multi-select attributes - static tests fixes --- .../Magento/CatalogImportExport/Model/Export/Product.php | 7 ++++--- .../Model/Import/Product/Type/AbstractType.php | 6 +++--- .../Import/Product/Validator/AbstractImportValidator.php | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index fd5cb05d6ad35..bc2dde2da2ce2 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -881,11 +881,12 @@ protected function collectRawData() } $data[$itemId][$storeId][$fieldName] = $attrValue; } - } else { + } else { $this->collectMultiselectValues($item, $code, $storeId); if (!empty($this->collectedMultiselectsData[$storeId][$itemId][$code])) { - $additionalAttributes[$code] = $fieldName . - ImportProduct::PAIR_NAME_VALUE_SEPARATOR . implode( + $additionalAttributes[$code] = $fieldName + . ImportProduct::PAIR_NAME_VALUE_SEPARATOR + . implode( ImportProduct::PSEUDO_MULTI_LINE_SEPARATOR, $this->collectedMultiselectsData[$storeId][$itemId][$code] ); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php index f55b6a0c163cb..cd1400a06527c 100755 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Type/AbstractType.php @@ -431,9 +431,9 @@ public function prepareAttributesWithDefaultValueForSave(array $rowData, $withDe foreach ($this->_getProductAttributes($rowData) as $attrCode => $attrParams) { if (!$attrParams['is_static']) { if (isset($rowData[$attrCode]) && strlen($rowData[$attrCode])) { - $resultAttrs[$attrCode] = 'select' == $attrParams['type'] ? $attrParams['options'][strtolower( - $rowData[$attrCode] - )] : $rowData[$attrCode]; + $resultAttrs[$attrCode] = 'select' == $attrParams['type'] + ? $attrParams['options'][strtolower($rowData[$attrCode])] + : $rowData[$attrCode]; if ('multiselect' == $attrParams['type']) { $resultAttrs[$attrCode] = []; foreach (explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]) as $value) { diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/AbstractImportValidator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/AbstractImportValidator.php index c70b6663f4d89..a27be55fb8912 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/AbstractImportValidator.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator/AbstractImportValidator.php @@ -51,4 +51,4 @@ public function getContext() } return $this->context; } -} \ No newline at end of file +}