Question

I try to import custom options and translate the titles and row titles during the import. I am not able to translate the row title though. Here is my code:

<?php

error_reporting(E_ALL);
ini_set('display_errors', '1');

require_once 'app/Mage.php';

Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);


$data = array(
    array(
        'sku' => '123456',
        '_type' => 'simple',
        '_attribute_set' => 'Default',
        '_product_websites' => 'base',
        'name' => 'Test Product',
        'price' => 10,
        'description' => 'description',
        'short_description' => 'short_description',
        'status' => 1,
        'visibility' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
        'tax_class_id' => 1,
        'is_in_stock' => 1
    ),
    array(
        '_custom_option_sku'         => 'MY_SKU',
        '_custom_option_sort_order'  => 10,
        '_custom_option_title'       => 'My EN title',
        '_custom_option_type'        => Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX,
        '_custom_option_is_required' => false,
        '_custom_option_row_title'   => 'My EN row title',
        '_custom_option_row_price'   => 0,
        '_custom_option_row_sku'     => 'MY_SKU',
        '_custom_option_row_sort'    => 10
    ),
    array(
        '_custom_option_store'       => 'de',
        '_custom_option_title'       => 'My DE title',
        '_custom_option_row_title'   => 'My DE row title'
    )
);

$import = Mage::getModel('fastsimpleimport/import');
try {
    $import->processProductImport($data);
} catch (Exception $e) {
    print_r($import->getErrorMessages());
}

This works fine for the default view:

Custom Option Default Store

But it only translate the title in the DE store:

enter image description here

It does not translate the row title. How can I translate the row title during the import? If this is not possible (that is what I think), how can the import be extended, so that translations of _custom_option_row_titles can be imported?

Was it helpful?

Solution

Okay, we came up with a solution by altering Mage_ImportExport_Model_Import_Entity_Product::_saveCustomOptions. Here is a full extension, which solves the issue in Magento 1.9.2.4 and works together with AvS_FastSimpleImport:

app/etc/modules/StackExchange_CustomOptionImport.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <StackExchange_CustomOptionImport>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Core/>
                <Mage_ImportExport/>
            </depends>
        </StackExchange_CustomOptionImport>
    </modules>
</config>

app/code/local/StackExchange/CustomOptionImport/etc/config.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <StackExchange_CustomOptionImport>
            <version>1.0.0</version>
        </StackExchange_CustomOptionImport>
    </modules>
    <global>
        <models>
            <stackexchange_customoptionimport>
                <class>StackExchange_CustomOptionImport_Model</class>
            </stackexchange_customoptionimport>
            <fastsimpleimport>
                <rewrite>
                    <import_entity_product>StackExchange_CustomOptionImport_Model_Import_Entity_Product</import_entity_product>
                </rewrite>
            </fastsimpleimport>
            <importexport>
                <rewrite>
                    <import_entity_product>StackExchange_CustomOptionImport_Model_Import_Entity_Product</import_entity_product>
                </rewrite>
            </importexport>
        </models>
    </global>
</config>

app/code/local/StackExchange/CustomOptionImport/Model/Import/Entity/Product.php:

<?php

if (Mage::helper('core')->isModuleEnabled('AvS_FastSimpleImport')) {
    class StackExchange_CustomOptionImport_Model_Import_Entity_Product_Abstract extends AvS_FastSimpleImport_Model_Import_Entity_Product {}
} else {
    class StackExchange_CustomOptionImport_Model_Import_Entity_Product_Abstract extends Mage_ImportExport_Model_Import_Entity_Product {}
}

class StackExchange_CustomOptionImport_Model_Import_Entity_Product extends StackExchange_CustomOptionImport_Model_Import_Entity_Product_Abstract
{

    protected function _saveCustomOptions()
    {
        /** @var $coreResource Mage_Core_Model_Resource */
        $coreResource   = Mage::getSingleton('core/resource');
        $productTable   = $coreResource->getTableName('catalog/product');
        $optionTable    = $coreResource->getTableName('catalog/product_option');
        $priceTable     = $coreResource->getTableName('catalog/product_option_price');
        $titleTable     = $coreResource->getTableName('catalog/product_option_title');
        $typePriceTable = $coreResource->getTableName('catalog/product_option_type_price');
        $typeTitleTable = $coreResource->getTableName('catalog/product_option_type_title');
        $typeValueTable = $coreResource->getTableName('catalog/product_option_type_value');
        $nextOptionId   = Mage::getResourceHelper('importexport')->getNextAutoincrement($optionTable);
        $nextValueId    = Mage::getResourceHelper('importexport')->getNextAutoincrement($typeValueTable);
        $priceIsGlobal  = Mage::helper('catalog')->isPriceGlobal();
        $type           = null;
        $typeSpecific   = array(
            'date'      => array('price', 'sku'),
            'date_time' => array('price', 'sku'),
            'time'      => array('price', 'sku'),
            'field'     => array('price', 'sku', 'max_characters'),
            'area'      => array('price', 'sku', 'max_characters'),
            //'file'      => array('price', 'sku', 'file_extension', 'image_size_x', 'image_size_y'),
            'drop_down' => true,
            'radio'     => true,
            'checkbox'  => true,
            'multiple'  => true
        );

        $alreadyUsedProductIds = array();
        while ($bunch = $this->_dataSourceModel->getNextBunch()) {
            $customOptions = array(
                'product_id'    => array(),
                $optionTable    => array(),
                $priceTable     => array(),
                $titleTable     => array(),
                $typePriceTable => array(),
                $typeTitleTable => array(),
                $typeValueTable => array()
            );

            foreach ($bunch as $rowNum => $rowData) {
                $this->_filterRowData($rowData);
                if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
                    continue;
                }
                if (self::SCOPE_DEFAULT == $this->getRowScope($rowData)) {
                    $productId = $this->_newSku[$rowData[self::COL_SKU]]['entity_id'];
                } elseif (!isset($productId)) {
                    continue;
                }
                if (!empty($rowData['_custom_option_store'])) {
                    if (!isset($this->_storeCodeToId[$rowData['_custom_option_store']])) {
                        continue;
                    }
                    $storeId = $this->_storeCodeToId[$rowData['_custom_option_store']];
                } else {
                    $storeId = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
                }
                if (!empty($rowData['_custom_option_type'])) { // get CO type if its specified
                    if (!isset($typeSpecific[$rowData['_custom_option_type']])) {
                        $type = null;
                        continue;
                    }
                    $type = $rowData['_custom_option_type'];
                    $rowIsMain = true;
                } else {
                    if (null === $type) {
                        continue;
                    }
                    $rowIsMain = false;
                }
                if (!isset($customOptions['product_id'][$productId])) { // for update product entity table
                    $customOptions['product_id'][$productId] = array(
                        'entity_id'        => $productId,
                        'has_options'      => 0,
                        'required_options' => 0,
                        'updated_at'       => now()
                    );
                }
                if ($rowIsMain) {
                    $solidParams = array(
                        'option_id'      => $nextOptionId,
                        'sku'            => '',
                        'max_characters' => 0,
                        'file_extension' => null,
                        'image_size_x'   => 0,
                        'image_size_y'   => 0,
                        'product_id'     => $productId,
                        'type'           => $type,
                        'is_require'     => empty($rowData['_custom_option_is_required']) ? 0 : 1,
                        'sort_order'     => empty($rowData['_custom_option_sort_order'])
                            ? 0 : abs($rowData['_custom_option_sort_order'])
                    );

                    if (true !== $typeSpecific[$type]) { // simple option may have optional params
                        $priceTableRow = array(
                            'option_id'  => $nextOptionId,
                            'store_id'   => Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID,
                            'price'      => 0,
                            'price_type' => 'fixed'
                        );

                        foreach ($typeSpecific[$type] as $paramSuffix) {
                            if (isset($rowData['_custom_option_' . $paramSuffix])) {
                                $data = $rowData['_custom_option_' . $paramSuffix];

                                if (array_key_exists($paramSuffix, $solidParams)) {
                                    $solidParams[$paramSuffix] = $data;
                                } elseif ('price' == $paramSuffix) {
                                    if ('%' == substr($data, -1)) {
                                        $priceTableRow['price_type'] = 'percent';
                                    }
                                    $priceTableRow['price'] = (float) rtrim($data, '%');
                                }
                            }
                        }
                        $customOptions[$priceTable][] = $priceTableRow;
                    }
                    $customOptions[$optionTable][] = $solidParams;
                    $customOptions['product_id'][$productId]['has_options'] = 1;

                    if (!empty($rowData['_custom_option_is_required'])) {
                        $customOptions['product_id'][$productId]['required_options'] = 1;
                    }
                    $prevOptionId = $nextOptionId++; // increment option id, but preserve value for $typeValueTable
                }
                if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])
                    && empty($rowData['_custom_option_store'])) {
                    // complex CO option row
                    $customOptions[$typeValueTable][$prevOptionId][] = array(
                        'option_type_id' => $nextValueId,
                        'sort_order'     => empty($rowData['_custom_option_row_sort'])
                            ? 0 : abs($rowData['_custom_option_row_sort']),
                        'sku'            => !empty($rowData['_custom_option_row_sku'])
                            ? $rowData['_custom_option_row_sku'] : ''
                    );
                    if (!isset($customOptions[$typeTitleTable][$nextValueId][0])) { // ensure default title is set
                        $customOptions[$typeTitleTable][$nextValueId][0] = $rowData['_custom_option_row_title'];
                    }
                    $customOptions[$typeTitleTable][$nextValueId][$storeId] = $rowData['_custom_option_row_title'];

                    if (!empty($rowData['_custom_option_row_price'])) {
                        $typePriceRow = array(
                            'price'      => (float) rtrim($rowData['_custom_option_row_price'], '%'),
                            'price_type' => 'fixed'
                        );
                        if ('%' == substr($rowData['_custom_option_row_price'], -1)) {
                            $typePriceRow['price_type'] = 'percent';
                        }
                        if ($priceIsGlobal) {
                            $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
                        } else {
                            // ensure default price is set
                            if (!isset($customOptions[$typePriceTable][$nextValueId][0])) {
                                $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
                            }
                            $customOptions[$typePriceTable][$nextValueId][$storeId] = $typePriceRow;
                        }
                    }
                    $prevValueId = $nextValueId++;
                }
                // also save the row title for specific stores - see http://magento.stackexchange.com/q/123855/142
                if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])
                    && !empty($rowData['_custom_option_store'])) {
                    $customOptions[$typeTitleTable][$prevValueId][$storeId] = $rowData['_custom_option_row_title'];
                }
                if (!empty($rowData['_custom_option_title'])) {
                    if (!isset($customOptions[$titleTable][$prevOptionId][0])) { // ensure default title is set
                        $customOptions[$titleTable][$prevOptionId][0] = $rowData['_custom_option_title'];
                    }
                    $customOptions[$titleTable][$prevOptionId][$storeId] = $rowData['_custom_option_title'];
                }
            }
            $productIds = array_keys($customOptions['product_id']);
            $productIds = array_diff($productIds, $alreadyUsedProductIds);
            if ($this->getBehavior() != Mage_ImportExport_Model_Import::BEHAVIOR_APPEND
                && !empty($productIds)
            ) { // remove old data?
                $this->_connection->delete(
                    $optionTable,
                    $this->_connection->quoteInto('product_id IN (?)', $productIds)
                );
            }
            // if complex options does not contain values - ignore them
            foreach ($customOptions[$optionTable] as $key => $optionData) {
                if ($typeSpecific[$optionData['type']] === true
                    && !isset($customOptions[$typeValueTable][$optionData['option_id']])
                ) {
                    unset($customOptions[$optionTable][$key], $customOptions[$titleTable][$optionData['option_id']]);
                }
            }

            if ($customOptions[$optionTable]) {
                $this->_connection->insertMultiple($optionTable, $customOptions[$optionTable]);
            }
            $titleRows = array();

            foreach ($customOptions[$titleTable] as $optionId => $storeInfo) {
                foreach ($storeInfo as $storeId => $title) {
                    $titleRows[] = array('option_id' => $optionId, 'store_id' => $storeId, 'title' => $title);
                }
            }
            if ($titleRows) {
                $this->_connection->insertOnDuplicate($titleTable, $titleRows, array('title'));
            }
            if ($customOptions[$priceTable]) {
                $this->_connection->insertOnDuplicate(
                    $priceTable,
                    $customOptions[$priceTable],
                    array('price', 'price_type')
                );
            }
            $typeValueRows = array();

            foreach ($customOptions[$typeValueTable] as $optionId => $optionInfo) {
                foreach ($optionInfo as $row) {
                    $row['option_id'] = $optionId;
                    $typeValueRows[]  = $row;
                }
            }
            if ($typeValueRows) {
                $this->_connection->insertMultiple($typeValueTable, $typeValueRows);
            }
            $optionTypePriceRows = array();
            $optionTypeTitleRows = array();

            foreach ($customOptions[$typePriceTable] as $optionTypeId => $storesData) {
                foreach ($storesData as $storeId => $row) {
                    $row['option_type_id'] = $optionTypeId;
                    $row['store_id']       = $storeId;
                    $optionTypePriceRows[] = $row;
                }
            }
            foreach ($customOptions[$typeTitleTable] as $optionTypeId => $storesData) {
                foreach ($storesData as $storeId => $title) {
                    $optionTypeTitleRows[] = array(
                        'option_type_id' => $optionTypeId,
                        'store_id'       => $storeId,
                        'title'          => $title
                    );
                }
            }
            if ($optionTypePriceRows) {
                $this->_connection->insertOnDuplicate(
                    $typePriceTable,
                    $optionTypePriceRows,
                    array('price', 'price_type')
                );
            }
            if ($optionTypeTitleRows) {
                $this->_connection->insertOnDuplicate($typeTitleTable, $optionTypeTitleRows, array('title'));
            }

            if ($productIds) { // update product entity table to show that product has options
                $customOptionsProducts = $customOptions['product_id'];

                foreach ($customOptionsProducts as $key => $value) {
                    if (!in_array($key, $productIds)) {
                        unset($customOptionsProducts[$key]);
                    }
                }
                $this->_connection->insertOnDuplicate(
                    $productTable,
                    $customOptionsProducts,
                    array('has_options', 'required_options', 'updated_at')
                );
            }

            $alreadyUsedProductIds = array_merge($alreadyUsedProductIds, $productIds);
        }
        return $this;
    }

}

You can compare the class with the default one in order to see what is changed. But there are only two changes:

  1. The $prevValueId is stored:

    $prevValueId = $nextValueId++;
    
  2. The store-specific row-title is saved:

    if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])
        && !empty($rowData['_custom_option_store'])) {
        $customOptions[$typeTitleTable][$prevValueId][$storeId] = $rowData['_custom_option_row_title'];
    }
    
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top