It is not currently natively supported by Magento.
I found a workaround to manage it. I add multi-select new options dynamically during CSV validation. It works for manual import and Schedule EE import. It is not the best because you need to reload attribute option cache for each line. If you found some optimization, please share it.
Solution:
Create a new ImportExport
module.
Add these dependencies in etc/di.xml
:
<preference for="Magento\CatalogImportExport\Model\Import\Product\Validator" type="Hublot\ImportExport\Model\Import\Product\Validator" />
<preference for="Magento\ConfigurableImportExport\Model\Import\Product\Type\Configurable" type="Hublot\ImportExport\Model\Import\Product\Type\Configurable" />
<preference for="Magento\CatalogImportExport\Model\Import\Product\Type\Simple" type="Hublot\ImportExport\Model\Import\Product\Type\Simple" />
Extend the class app/code/Vendor/ImportExport/Model/Import/Product/Validator.php
:
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Vendor\ImportExport\Model\Import\Product;
use Braintree\Exception;
use \Magento\CatalogImportExport\Model\Import\Product as Product;
use \Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as RowValidatorInterface;
class Validator extends \Magento\CatalogImportExport\Model\Import\Product\Validator
{
protected $dynamicallyOptionAdded = array();
/**
* @param \Magento\Framework\Stdlib\StringUtils $string
* @param RowValidatorInterface[] $validators
*/
public function __construct(
\Magento\Framework\Stdlib\StringUtils $string,
$validators = [],
\Magento\Catalog\Model\Product\Attribute\OptionManagement $optionManagement,
\Magento\Eav\Api\Data\AttributeOptionInterfaceFactory $optionDataFactory,
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
\Psr\Log\LoggerInterface $logger
) {
$this->optionManagement = $optionManagement;
$this->optionDataFactory = $optionDataFactory;
$this->dataObjectHelper = $dataObjectHelper;
$this->_logger = $logger;
parent::__construct($string, $validators);
}
/**
* @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 (isset($rowData['product_type']) && !empty($attrParams['apply_to'])
&& !in_array($rowData['product_type'], $attrParams['apply_to'])
) {
return true;
}
if (!$this->isRequiredAttributeValid($attrCode, $attrParams, $rowData)) {
$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 'boolean':
case 'multiselect':
$values = explode(Product::PSEUDO_MULTI_LINE_SEPARATOR, $rowData[$attrCode]);
$valid = true;
// Start custom
foreach ($values as $value) {
// If option not exist and not already dynamically added
if (!empty($value) && !isset($attrParams['options'][strtolower($value)]) && !isset($this->dynamicallyOptionAdded[$attrCode][strtolower($value)])) {
// Create option value
$optionDataObject = $this->optionDataFactory->create();
$this->dataObjectHelper->populateWithArray(
$optionDataObject,
array(
'label' => $value,
'sort_order' => 100,
'is_default' => true
),
'\Magento\Eav\Api\Data\AttributeOptionInterface'
);
// Add option dynamically
if ($this->optionManagement->add($attrCode, $optionDataObject)) {
// Add new option value dynamically created to the different entityTypeModel cache
$entityTypeModel = $this->context->retrieveProductTypeByName($rowData['product_type']);
$configurableEntityTypeModel = $this->context->retrieveProductTypeByName('configurable');
// Refresh attributes cache for entityTypeModel cache
if ($entityTypeModel) {
$entityTypeModel->refreshCacheAttributes();
}
if ($configurableEntityTypeModel) {
$configurableEntityTypeModel->refreshCacheAttributes();
}
$this->dynamicallyOptionAdded[$attrCode][strtolower($value)] = true;
$attrParams['options'][strtolower($value)] = true;
}
}
}
if (isset($this->dynamicallyOptionAdded[$attrCode])) {
foreach ($this->dynamicallyOptionAdded[$attrCode] as $key => $value) {
$attrParams['options'][$key] = $value;
}
}
// end custom
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];
}
if (!$valid) {
$this->setInvalidAttribute($attrCode);
}
return (bool)$valid;
}
}
Extend the class app/code/Vendor/ImportExport/Model/Import/Product/Type/Configurable.php
:
<?php
// @codingStandardsIgnoreFile
namespace Vendor\ImportExport\Model\Import\Product\Type;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\CatalogImportExport\Model\Import\Product as ImportProduct;
/**
* Importing configurable products
* @package Magento\ConfigurableImportExport\Model\Import\Product\Type
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Configurable extends \Magento\ConfigurableImportExport\Model\Import\Product\Type\Configurable
{
/**
* In case we've dynamically added new attribute option during import we need to add it to our cache
* in order to keep it up to date.
*
* @todo Try an optimal solution in order to update only the need part of the cache (Check AddAttributeOption)
*
*/
public function refreshCacheAttributes()
{
// Need to force reload attribute cache
self::$commonAttributesCache = [];
$this->_initAttributes();
}
}
Extend the class app/code/Vendor/ImportExport/Model/Import/Product/Type/Simple.php
:
<?php
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Vendor\ImportExport\Model\Import\Product\Type;
/**
* Import entity simple product type
*
* @author Magento Core Team <[email protected]>
*/
class Simple extends \Magento\CatalogImportExport\Model\Import\Product\Type\Simple
{
/**
* In case we've dynamically added new attribute option during import we need to add it to our cache
* in order to keep it up to date.
*
* @todo Try an optimal solution in order to update only the need part of the cache (Check AddAttributeOption)
*
*/
public function refreshCacheAttributes()
{
// Need to force reload attribute cache
self::$commonAttributesCache = [];
$this->_initAttributes();
}
}