Saving a Product Attribute without Saving the Entire Product

Today I was faced with an interesting task. I needed to update an attribute on as many as several thousand products at once within a reasonable amount of time. My first thought was to load a collection of products and then save that collection. Under the hood, saving a collection simply loops through all models in that collection and calls the save method on the model.

I started in this direction when I tested my code, Magento threw the following error:

exception ‘Zend_Db_Statement_Exception’ with message ‘SQLSTATE[23000]: Integrity constraint violation: 1048 Column ‘value’ cannot be null’

After consulting with one of the other developers here, I realized that because a collection does not load entire products but only a subset of the products’ attributes. I tried loading every single product and saving it but this turned out to be painfully slow. It would have taken hours to save just a few hundred products. Clearly, this was unacceptable.

I started brainstorming and I realized that I could use the same methods that are used by the mass product attributes update page uses. After looking into this method, I realized that I could only send one value per attribute update. To get around this limitation, I set up an array keyed using the new attribute value with a value of an array of all products that had that specific value. This array was fed through a loop and the update attributes method was called for every value. This turned out to be very fast and it simplified the process enormously.

The following code is what I ended up using to update the attributes.

// $products is a collection of products that need to be updated
// $productArray contains an array with the following format:
// array('productId' => 'value to add')
foreach ($products as $product) {
    $newPopularity = $product->getPopularity() + $productArray[$product->getId()];
    $productData[$newPopularity][] = $product->getId();
}
foreach ($productData as $popularityValue => $pidArray) {
    Mage::getSingleton('catalog/product_action')
        ->updateAttributes($pidArray, array('popularity' => $popularityValue), 0);
}

There is a previous blog post about saving product attributes here. I ran a comparison between the two methods for an 1162 product collection. Using this method it took 6.233485 sec vs 8.680476 sec using the individual save method. Although this method is quicker, the disparity between the two methods is proportional to the number of values that are the same. So a collection with 2000 ranging over 5 values will be helped a lot more than a collection of 2000 with 1000 values.

  • This module is built on Magento 1.4.1.1. It should work on most versions of Magento.

Share it

Topics

Related Posts

Google and Yahoo Have New Requirements for Email Senders

What ROAS Really Means

Everything You Need to Know About Updating to Google Analytics 4

Contact Us