Data-migration-tool: Undefined offset in Migration/Handler/VisualMerchandiser/SmartAttribute.php on line 30

Created on 10 Jan 2017  路  5Comments  路  Source: magento/data-migration-tool

Environment Information:

Data Migration Tool Version: 2.1.3
Mode: data
Step: VisualMerchandiser Step
Sub-step: data
Source Database Version: Magento EE 1.14.2.0
Destination Database Version: Magento EE 2.1.3

Summary:

If a category's visual merchandiser has been configured to use the same attribute code more than once for that category, the data migration tool will pass the integrity check but fail in data migration due to undefined index.

Additional Information:

Consider the following setup on a category:
foo_bar

The values for this configuration in the merchandiser_category_values table of the MapInterface::TYPE_SOURCE database will be as follows:

attribute_codes: name
smart_attributes: a:2:{s:18:"_1484069909215_215";a:3:{s:9:"attribute";s:2:"65";s:5:"value";s:3:"Foo";s:4:"link";s:2:"OR";}s:18:"_1484069916735_735";a:3:{s:9:"attribute";s:2:"65";s:5:"value";s:3:"Bar";s:4:"link";s:2:"OR";}}

Come migration time, the data migration tool expects the number of comma separated attribute_codes and the size of the serialized array to match. However because the attribute_codes are only stored uniquely in the source database, they will not match if used more than once.

Migration\Handler\VisualMerchandiser\SmartAttribute::handle()

The size of $attributeCodeArr and $attributeValues will not match, causing an error to be thrown on line 30.

[Exception]
Notice: Undefined offset: 1 in /path/to/install/vendor/magento/data-migration-tool/src/Migration/Handler/VisualMerchandiser/SmartAttribute.php on line 30
acknowledged bug

Most helpful comment

Hello,

We also faced this issue. The problem is the way handler is trying to get the attribute codes, I wrote this workaround to get the attribute code using the attribute_id that is the attribute value inside the source data.

  1. Create custom class to extend \Migration\Handler\VisualMerchandiser\SmartAttribute

  2. Inject the adapter factory

   protected $attributesCached = [];

    public function __construct(\Migration\ResourceModel\AdapterFactory $adapterFactory)
    {
        $this->adapter = $adapterFactory->create(['resourceType' => 'source']);
    }
  1. Define method. You may need to update the table name of the eav attribute and the parameter 500 is the size of the attribute page, in our case we have around 350 attributes, you can change that :
    protected function getAttributeCode($attributeId) {
        $attributeTable = 'mg_eav_attribute';
        $attributes = $this->adapter->loadPage($attributeTable, 1, 500, 'attribute_id', 0);
        if (isset($this->attributesCached[$attributeId])) {
            return $this->attributesCached[$attributeId];
        }
        foreach ($attributes as $key => $data) {
            if (isset($data['attribute_id']) && $data['attribute_id'] == $attributeId && isset($data['attribute_code'])) {
                $this->attributesCached[$attributeId] = $data['attribute_code'];
                return $data['attribute_code'];
            }
        }
    }
  1. Update handle(Record $recordToHandle, Record $oppositeRecord) method, replace $attribute['attribute'] = $attributeCodeArr[$count]; with:
$attribute['attribute'] = $this->getAttributeCode($attributeValue['attribute']);

```php
public function handle(Record $recordToHandle, Record $oppositeRecord)
{
$count = 0;
$attributes = [];
$this->validate($recordToHandle);
$attributeCode = $recordToHandle->getValue(self::ATTRIBUTE_CODE_NAME);
$attributeCodeArr = explode(',', $attributeCode);
$attributeValues = unserialize($recordToHandle->getValue(self::ATTRIBUTE_VALUE_NAME));
if (is_array($attributeValues)) {
foreach ($attributeValues as $attributeValue) {
$attribute = $this->parseOperator($attributeValue['value']);
$attribute['attribute'] = $attributeCodeArr[$count];
$attribute['logic'] = $attributeValue['link'];
$count++;
$attributes[] = $attribute;
}
$attributeString = \Zend_Json::encode($attributes);

        $recordToHandle->setValue($this->field, $attributeString);
    }
}
5. Update your visual_merchandiser_map.xml to use the new handler

```xml
           <transform>
                <field>merchandiser_category_values.smart_attributes</field>
                <handler class="< path to custom handler >\SmartAttribute"/>
            </transform>

I hope this help someone else

Regards

All 5 comments

Thank you for reporting the issue! Internal ticket to fix it MAGETWO-63104

I am also facing the same issue when migrating Magento EE 14.1.0 to EE 2.1.8. Please let us know if this issue is resolved or not?

not solved yet

Is there an update to this issue? I hit this overnight trying to do data migration.

Hello,

We also faced this issue. The problem is the way handler is trying to get the attribute codes, I wrote this workaround to get the attribute code using the attribute_id that is the attribute value inside the source data.

  1. Create custom class to extend \Migration\Handler\VisualMerchandiser\SmartAttribute

  2. Inject the adapter factory

   protected $attributesCached = [];

    public function __construct(\Migration\ResourceModel\AdapterFactory $adapterFactory)
    {
        $this->adapter = $adapterFactory->create(['resourceType' => 'source']);
    }
  1. Define method. You may need to update the table name of the eav attribute and the parameter 500 is the size of the attribute page, in our case we have around 350 attributes, you can change that :
    protected function getAttributeCode($attributeId) {
        $attributeTable = 'mg_eav_attribute';
        $attributes = $this->adapter->loadPage($attributeTable, 1, 500, 'attribute_id', 0);
        if (isset($this->attributesCached[$attributeId])) {
            return $this->attributesCached[$attributeId];
        }
        foreach ($attributes as $key => $data) {
            if (isset($data['attribute_id']) && $data['attribute_id'] == $attributeId && isset($data['attribute_code'])) {
                $this->attributesCached[$attributeId] = $data['attribute_code'];
                return $data['attribute_code'];
            }
        }
    }
  1. Update handle(Record $recordToHandle, Record $oppositeRecord) method, replace $attribute['attribute'] = $attributeCodeArr[$count]; with:
$attribute['attribute'] = $this->getAttributeCode($attributeValue['attribute']);

```php
public function handle(Record $recordToHandle, Record $oppositeRecord)
{
$count = 0;
$attributes = [];
$this->validate($recordToHandle);
$attributeCode = $recordToHandle->getValue(self::ATTRIBUTE_CODE_NAME);
$attributeCodeArr = explode(',', $attributeCode);
$attributeValues = unserialize($recordToHandle->getValue(self::ATTRIBUTE_VALUE_NAME));
if (is_array($attributeValues)) {
foreach ($attributeValues as $attributeValue) {
$attribute = $this->parseOperator($attributeValue['value']);
$attribute['attribute'] = $attributeCodeArr[$count];
$attribute['logic'] = $attributeValue['link'];
$count++;
$attributes[] = $attribute;
}
$attributeString = \Zend_Json::encode($attributes);

        $recordToHandle->setValue($this->field, $attributeString);
    }
}
5. Update your visual_merchandiser_map.xml to use the new handler

```xml
           <transform>
                <field>merchandiser_category_values.smart_attributes</field>
                <handler class="< path to custom handler >\SmartAttribute"/>
            </transform>

I hope this help someone else

Regards

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mirkocesaro picture mirkocesaro  路  3Comments

ashvinimarwal picture ashvinimarwal  路  7Comments

croxarens picture croxarens  路  7Comments

snoroozi picture snoroozi  路  6Comments

expertalex picture expertalex  路  4Comments