3

We are importing products using a PHP script using AvS_FastSimpleImport which is an extension of Mage_ImportExport.

The CSV files / array do not contain the field url_key.

So currently, this value is empty for the products.

We would like to have the URL key auto generated, just as if we enter a product manually in the backend and leave the field empty.

The script already contains a part to load admin events:

Mage::getConfig()->loadEventObservers('adminhtml');
Mage::app()->addEventArea('adminhtml');

Edit This bug is fixed in EE 1.13.1.0

  • Did you look into Enterprise_ImportExport_Model_Import_Entity_Product this seems to build a list of all the current urlkeys. – David Manners Feb 25 '14 at 17:07
  • I was having a similar problem with my own CSV import module, but it turned out that import wasn't creating any records in the catalog_product_website table. Including a _product_websites column set to 'base' and reindexing after the import fixed the issue. – Matthew Lowe May 17 '16 at 20:28
7

A basic solution seems to be to set the url_key column to the product name.

This will run through \Mage_Catalog_Model_Attribute_Backend_Urlkey_Abstract::beforeSave which automatically formats the URL key properly (removing spaces and so on).

I would actually expect \Enterprise_Catalog_Model_Product_Attribute_Backend_Urlkey::beforeSave to be used during the export, because we are on Enterprise, which is not the case.

Even if the Enteprise-only method would be called during import, this would not work, because it checks the database for duplicate URL keys - which is not yet updated during that stage of the import.

Solution: After importing, detect missing URL keys using:

$urlKeyMissing = Mage::getModel('catalog/product')->getCollection()
    ->addWebsiteFilter($website)
    ->addAttributeToFilter('url_key',  array('null' => ''), 'left');

and update these products (load, setDataChanged, save).

In addition I am setting the following in the config.xml of my import module (the standard import-export is disabling the URL key checks)

<global>
    <importexport>
        <import>
            <catalog_product>
                <attributes>
                    <url_key>
                        <backend_model>enterprise_catalog/product_attribute_backend_urlkey</backend_model>
                    </url_key>
                </attributes>
            </catalog_product>
        </import>
    </importexport>
</global>
2

I'm using the following code (refactored ofcouse) to make sure a unique URL key is created:

// Unique URL keys
$read = Mage::getSingleton('core/resource')->getConnection('core_read');
$urlKey = Mage::getModel('catalog/product_url')->formatUrlKey($productName);
$sql = 'SELECT * FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_key' AND entity_type_id = 4) AND value = ? and store_id = 0';
$row = $read->fetchRow($sql, array($urlKey));
$idx = 1;
while ($row != false) {
    $urlKey = Mage::getModel('catalog/product_url')->formatUrlKey($productName) . '-' . $idx;
    $idx++;
    $row = $read->fetchRow($sql, array($urlKey));
}

Magento version: 1.8.0.0-alpha1

After digging in the code Magento seems to handle this in the Mage_Catalog_Model_Product_Attribute_Backend_Urlkey class:

     /**
     * Format url_key value
     *
     * @param Mage_Catalog_Model_Product $object
     * @return Mage_Catalog_Model_Product_Attribute_Backend_Urlkey
     */
    public function beforeSave($object)
    {
        $attributeName = $this->getAttribute()->getName();

        $urlKey = $object->getData($attributeName);
        if ($urlKey === false) {
            return $this;
        }
        if ($urlKey == '') {
            $urlKey = $object->getName();
        }
        $urlKey = $object->formatUrlKey($urlKey);
        if (empty($urlKey)) {
            $urlKey = Mage::helper('core')->uniqHash();
        }
        $object->setData($attributeName, $urlKey);

        $this->_validateUrlKey($object);
        return $this;
    }

    /**
     * Check unique url_key value in catalog_product_entity_url_key table.
     *
     * @param Mage_Catalog_Model_Product $object
     * @return Mage_Catalog_Model_Product_Attribute_Backend_Urlkey
     * @throws Mage_Core_Exception
     */
    protected function _validateUrlKey($object)
    {
        $connection = $object->getResource()->getReadConnection();

        $select = $connection->select()
            ->from($this->getAttribute()->getBackendTable(), array('count' => new Zend_Db_Expr('COUNT(\'value_id\')')))
            ->where($connection->quoteInto('entity_id <> ?', $object->getId()))
            ->where($connection->quoteInto('value = ?', $object->getUrlKey()));
        $result = $connection->fetchOne($select);
        if ((int)$result) {
           throw new Mage_Core_Exception(
               Mage::helper('catalog')->__("Product with the '%s' url_key attribute already exists.",
                   $object->getUrlKey())
           );
        }

        return $this;
    }
  • I want to do this only for newly imported products. And it would be great to use the same code as when saving a product. – Alex Sep 2 '13 at 10:50
  • I have added the way Magento does this, since that's what you are asking for... I guess you could use the same logic or call the beforesave method yourself. – Kenny Sep 2 '13 at 10:55
  • thanks for the update - any clue why this is not called during import? – Alex Sep 2 '13 at 11:14
  • By the way: In EE 1.13.0.2 Mage_Catalog_Model_Product_Attribute_Backend_Urlkey is empty. – Alex Sep 2 '13 at 11:41
  • In EE I think the logic is located in Mage_Catalog_Model_Indexer_Url and Mage_Catalog_Model_Url, it isn't called because the module you use may have work directly against the database... – Kenny Sep 2 '13 at 12:16
1

What I found is the url generataion for new products in the enterprise import system happens in the file Enterprise_ImportExport_Model_Import_Entity_Product which extends Mage_ImportExport_Model_Import_Entity_Product.

First it loads an array of all the current url key and linked skus on construct via the function _initUrlKeys.

Then when it called _prepareAttributes first it will check the url key via _prepareUrlKey and then call the parent _prepareAttributes function.

protected function _prepareUrlKey($rowData, $rowScope, $sku)
{
    if (self::SCOPE_DEFAULT != $rowScope) {
        return $rowData;
    }
    if (!empty($rowData['name']) && empty($rowData['url_key']) && array_search($sku, $this->_urlKeys) === false) {
        $rowData['url_key'] = Mage::getModel('catalog/product')->formatUrlKey($rowData['name']);
        if (isset($this->_urlKeys[$rowData['url_key']])) {
            $rowData['url_key'] = sprintf(
                '%s-%s',
                $rowData['url_key'],
                substr(Mage::helper('core')->uniqHash(), 0, 6)
            );
        }
        $this->_urlKeys[$rowData['url_key']] = $sku;
    }
    return $rowData;
}

So if you update AvS_FastSimpleImport_Model_Import_Entity_Product to extend Enterprise_ImportExport_Model_Import_Entity_Product. In my case I have already rewriten AvS_FastSimpleImport_Model_Import_Entity_Product and so I simply added the function I needed into my new class as the function simply add onto the core functionality and do not rewrite them, but I understand why this is not an ideal solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.