Richard Parnaby-King

Add products to category quickly!

When dealing with Magento, the common knowledge to bulk update products is to pull in a collection and, in a loop, load the product, change the attribute and save.

A client is syncing their product database from their system to Magento, but their system only allows a product to be in one category. I was asked to run a cron script nightly to check for an attribute on the product and place those products into an additional category.

Knowing how slow save() is, I started to dig around and come up with the below code.

Benchmarking this code against the regular way of doing it, I saw the times drop from about an hour and a half for 1,200 products to five seconds!

This is the minimum amount of code required to alter a collection of products to add them to a new category. I’ve removed the extra code regarding my scenario above.


// Set indexing to manual before starting updates, otherwise it'll continually get slower as you update
$processes = Mage::getSingleton('index/indexer')->getProcessesCollection();
$processes->walk('setMode', array(Mage_Index_Model_Process::MODE_MANUAL));

/** @var Varien_Db_Adapter_Pdo_Mysql **/
$dbw = Mage::getSingleton('core/resource')->getConnection('core_write');
$productCategoryTable = Mage::getSingleton('core/resource')->getTableName('catalog/category_product');

$product_ids = Mage::getModel('catalog/product')->getCollection()

$category_id = 123;

// Add product ids and category ids to database for insertion
$data = array();
foreach($product_ids as $id) {
    $data[] = array(
        'category_id' => (int)$category_id,
        'product_id'  => (int)$id,
        'position'    => 1,

if(count($data)) {
    // Updates a single attribute, much faster than calling a full product save
    try {
        $dbw->insertMultiple($productCategoryTable, $data);
    } catch (Exception $e) {
        //ignore duplicate entry error messages
        if(strpos(strtolower($e->getMessage()), 'duplicate entry') === FALSE) {
Mage::log('Products Put in Category:');

// Reindex all
// Set indexing back to realtime, if you have it set to manual normally you can comment this line out
$processes->walk('setMode', array(Mage_Index_Model_Process::MODE_REAL_TIME));