Magento 2 ошибка экспорта товаров. Проблемы миграции

Magento 2 ошибка экспорта товаров. Проблемы миграции

Возможно некоторые из вас, а я так точно столкнулся с проблемой, которая случилась во время экспорта товаров в CSV файл, а именно отсутствие половины информации для товаров, из 100 (условных) атрибутов выгрузились только 10 и случилось это после миграции данных Magento 1 Magento 2. Во время миграции не все наборы атрибутов из старого магазина в новый были перенесены, почему именно наборы и в каком то количестве, не могу сказать.

Как определить ошибку

  • В файле экспорта есть не все колонки – атрибуты и их большое количество, но возможно в не которых случаях их меньше и соответственно это сложнее заметить
  • В журнале ошибок exception.log есть ошибка ориентировочного такого содержания main.CRITICAL: Notice: Undefined offset: 1 in /vendor/magento/module-catalog-import-export/Model/Export/Product.php on line 1030

 

Функция в которой произошла ошибка и ее содержимое

<?php

/**
 * Collect export data for all products
 *
 * @return array
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 * @SuppressWarnings(PHPMD.NPathComplexity)
 */
protected function collectRawData()
{
    ...

            if (!empty($data[$itemId][$storeId]) || $this->hasMultiselectData($item, $storeId)) {
                $attrSetId = $item->getAttributeSetId();
                $data[$itemId][$storeId][self::COL_STORE] = $storeCode;
                
                //Проблема - запрашиваемый элемент массива не найден
                $data[$itemId][$storeId][self::COL_ATTR_SET] = $this->_attrSetIdToName[$attrSetId];
                $data[$itemId][$storeId][self::COL_TYPE] = $item->getTypeId();
            }
            
            ....
        }
    }

    return $data;
}

?>

 

Если посмотреть на уровень выше – где вызывается эта функция, то в случае возникновения ошибки, инициализация заголовков для CVS файла обрывается

<?php
$this->setHeaderColumns($multirawData['customOptionsData'], $stockItemRows);
?>
<?php

/**
 * Get export data for collection
 *
 * @return array
 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
 * @SuppressWarnings(PHPMD.NPathComplexity)
 * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
 * @SuppressWarnings(PHPMD.UnusedLocalVariable)
 */
protected function getExportData()
{
    $exportData = [];
    try {
        $rawData = $this->collectRawData();
        $multirawData = $this->collectMultirawData();

        $productIds = array_keys($rawData);
        $stockItemRows = $this->prepareCatalogInventory($productIds);

        $this->rowCustomizer->prepareData(
            $this->_prepareEntityCollection($this->_entityCollectionFactory->create()),
            $productIds
        );

        $this->setHeaderColumns($multirawData['customOptionsData'], $stockItemRows);

        foreach ($rawData as $productId => $productData) {
            foreach ($productData as $storeId => $dataRow) {
                if ($storeId == Store::DEFAULT_STORE_ID && isset($stockItemRows[$productId])) {
                    $dataRow = array_merge($dataRow, $stockItemRows[$productId]);
                }
                $this->appendMultirowData($dataRow, $multirawData);
                if ($dataRow) {
                    $exportData[] = $dataRow;
                }
            }
        }
        
    } catch (\Exception $e) {
        
        //В случае ошибки записать ее
        $this->_logger->critical($e);
    }
    
    return $exportData;
}

?>

 

Результат – отсутствие ранее инициализированных колонок – заголовков.

Причина ошибки – отсутствие элемента в массиве, который хранит информацию о наборах атрибутов. Товар который будет эскпортирован в файл не имеет набора, что в принципе не возможно, так как вы создаете новые товары на базе конкретного атрибута.

И если открыть этот товар в админ панели, то получим следующую ошибку.

1 exception(s):
Exception #0 (Magento\Framework\Exception\NoSuchEntityException): No such entity with attributeSetId = 67

 

Решением проблемы будет поиск товаров, которые имеют не корректные атрибуты и их замена.

SQL скрипт для поиска товаров с наборами, которые не существуют (10 – это тип сущности catalog_product)

SELECT * FROM catalog_product_entity AS e
LEFT JOIN eav_attribute_set AS a ON e.attribute_set_id=a.attribute_set_id AND a.entity_type_id=10
WHERE a.attribute_set_id IS NULL

Результат

 

Пример функции – установка набора атрибутов по умолчанию для товаров

<?php

namespace Sysint\CoreStore\Setup;

use Magento\Catalog\Model\ProductFactory;

class UpgradeData implements UpgradeDataInterface
{

    /**
     * @var ProductFactory
     */
    private $_productFactory;

    /**
     * Catalog config
     *
     * @var \Magento\Catalog\Model\Config
     */
    protected $_catalogConfig;

    /**
     * UpgradeData constructor.
     * @param ProductFactory $productFactory
     * @param \Magento\Catalog\Model\Config $catalogConfig
     */
    public function __construct(
        ProductFactory $productFactory,
        \Magento\Catalog\Model\Config $catalogConfig
    ) {

        $this->_productFactory = $productFactory;
        $this->_catalogConfig = $catalogConfig;

    }

    /**
     * Upgrades data for a module
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {

        if (version_compare($context->getVersion(), '1.1.2', '<')) {
            $this->fixAttributeSet($setup);
        }

    }

    private function fixAttributeSet(ModuleDataSetupInterface $setup){

        $productTable = $setup->getTable('catalog_product_entity');
        $connection   = $setup->getConnection();

        $entityTypeId = $this->_catalogConfig->getEntityType(\Magento\Catalog\Model\Product::ENTITY)->getEntityTypeId();

        $select = $connection->select()
            ->from(['e' => $productTable], ['entity_id'])
            ->joinLeft(['a' => $setup->getTable('eav_attribute_set')],
                sprintf('e.attribute_set_id=a.attribute_set_id AND a.entity_type_id=%s', $entityTypeId)
                , [])
            ->where('a.attribute_set_id IS NULL')
        ;

        $ids = $connection->fetchCol($select);

        $setId = $this->_productFactory->create()->getDefaultAttributeSetId();

        if (is_array($ids) && count($ids) && is_numeric($setId)){

            $where = $connection->quoteInto('entity_id IN(?)', $ids);

            $connection->update($productTable, ['attribute_set_id' => $setId], $where);

        }
    }

}