Category Performance – How to Update One Attribute

categoryperformanceupgrade

I'm trying to speed up the process of updating the description and category image in a few hundred categories in 16 storeviews. Each storeview has an different description and category image.

Using the save()-Method on the category object works ok, but is quite slow.

I tried using

$_category->getResource()->saveAttribute( $_category, "description" );

but that does not work for me.

Is there any other way for faster updating of single category attributes, maybe something like

Mage::getSingleton('catalog/resource_product_action')

(i could not find anything similar for categories)?

Best Answer

Your initial code should be working; see:

<?php

include 'app/Mage.php';
Mage::app();

$obj = new Varien_Object(array('entity_id' => 10,'description'=>'test','store_id'=>0));
$resource = Mage::getModel('catalog/category')->getResource();
/* @var $resource Mage_Catalog_Model_Resource_Category */

$resource->saveAttribute($obj,'description');

So, that's the basic save approach for one attribute at a time.

You might want to create a category action resource, e.g. Mage_Catalog_Model_Resource_Category_Action (drop it in local codepool to keep the kids from weeping), however, you'll need to adjust the updateAttributes method to save different store scopes and attribute values per entity - this is different from the catalog action resource behavior.

<?php

/**
 * Catalog Category Mass processing resource model
 *
 * @category    Mage
 * @package     Mage_Catalog
 * @author      Ben Marks <ben@blueacorn.com>
 */
class Mage_Catalog_Model_Resource_Category_Action extends Mage_Catalog_Model_Resource_Abstract
{
    /**
     * Initialize connection
     *
     */
    protected function _construct()
    {
        $resource = Mage::getSingleton('core/resource');
        $this->setType(Mage_Catalog_Model_Category::ENTITY) //note the change from the product action resource
            ->setConnection(
                $resource->getConnection('catalog_read'),
                $resource->getConnection('catalog_write')
            );
    }

    /**
     * Update entity attribute values individually by scope
     *
     * $dataArray = array(
     *     {attribute code} => array (
     *         array (
     *             'entity_id' => {category id},
     *             'value      => {value},
     *             'store_id'  => {store id}
     *         ),
     *         array (
     *             //...
     *         )
     *     )
     * );
     *
     * @param  array
     * @return Mage_Catalog_Model_Resource_Category_Action
     * @throws Exception
     */
    public function updateAttributes($dataArray)
    {
        $this->_getWriteAdapter()->beginTransaction();

        try {
            $i = 0;
            foreach ($dataArray as $code => $entities) {
                $attribute = $this->getAttribute($code);
                if (!$attribute->getAttributeId()) {
                    continue;
                }
                foreach ($entities as $entity) {
                    $object = new Varien_Object();
                    $object->setIdFieldName('entity_id')
                        ->setStoreId($entity['store_id']);

                    $object->setId($entity['entity_id']);
                    // collect data for save
                    $this->_saveAttributeValue($object, $attribute, $entity['value']);
                }
            }
            $this->_processAttributeValues();
            $this->_getWriteAdapter()->commit();
        } catch (Exception $e) {
            $this->_getWriteAdapter()->rollBack();
            throw $e;
        }

        return $this;
    }
}

Obviously you should change the array structure to whatever works for you. Test data & script for the above class & method are below, and will work for sample data.

<?php

include 'app/Mage.php';
Mage::app();

$action = Mage::getResourceModel('catalog/category_action');

$test = array(
    'description' => array(
        array (
            'entity_id' => 10,      //Furniture category
            'value'     => 'ONE',
            'store_id'  => 1        //English store scope
        ),
        array (
            'entity_id' => 10,      //Furniture category
            'value'     => 'TWO',
            'store_id'  => 2        //German store scope
        ),
        array (
            'entity_id' => 22,      //Living Room category
            'value'     => 'THREE',
            'store_id'  => 3        //French store scope
        )
    ),
    'meta_keywords' => array(
        array (
            'entity_id' => 22,      //Living Room category
            'value'     => 'FOUR',
            'store_id'  => 1        //English store scope
        )
    )
);

$action->updateAttributes($test);