Make mass operations on collections almost always means foreach, models loading and other stuff that make our code heavier and often resources consuming. Magento gives us a class that allows better code.


Forewords

The Mage_Core_Model_Resource_Iterator class is very light. It exposes only two methods. The one that interests us is walk(). By having a deeper look at it, we can tell that it can receive the following arguments:

  • $query that gets a Zend_Db_Select object
  • $callbacks that gets an array of classes / methods that will be called one by one
  • $args that gets the arguments passed to the classes / methods specified in $callbacks
  • $adapter that may get a DB adapter (almost unuseful)

We mainly note that, on line 48, the $args['row'] = $row; codebit is in charge of populating data for each elements of the collection. Thus the arguments that are passed to the $callbacks methods will have data from the DB for the model currently being iterated. This data is then in $args['row'].

The theorytical code to use this class is like so:

Mage::getSingleton('core/resource_iterator')->walk(  
    $uneCollectionMagento->getSelect(),
    array(
        array(
            $calledClassForEachCollectionElement,
            'methodUsedInThisCalledClass'
        ),
        array(
            $anotherCalledClassForEachCollectionElement,
            'methodUsedInThisOtherCalledClass'
        )
    ),
    array(
        'argumentSentToTheUsedMethods' => $argument,
        'anotherArgumentSentToTheUsedMethods' => $otherArgument
    )
);

We now can see the power of this technic that allows us to call several methods of several classes for each item of a given collection. Each of those methods will get the arguments that have been sent in the last array. It is then possible to do several operations on the same collection at once.

Example : subscribe all customers to the newsletter

Even though this may not be very legal... this example remains interesting and simple. Here is its commented code:

class Namespace_Module_Model_Class extends Mage_Core_Model_Abstract {

    /**
     * Subscribe all customers to newsletter
     *
     * @return void
     */
    public function subscribeAllCustomers()
    {
        // Retrieve subscriber model that will be used to subscribe each customer
        $subscriber = Mage::getModel('newsletter/subscriber');

        // Retrieve customer model that will be used by the $subscriber model
        $customer = Mage::getModel('customer/customer');

        // Retrieve the customers collection to iterate thru
        $customerCollection = $customer->getCollection();

        Mage::getSingleton('core/resource_iterator')->walk(
            // Send the Zend_Dd_Select
            $customerCollection->getSelect(),

            // Set the callback to use
            // Class used is the current class ($this)
            // Method used is the subscribeCustomer() below
            array(array($this, 'subscribeCustomer')),

            // Set arguments that will be passed to the callback (subscribeCustomer())
            array('subscriberModel' => $subscriber, 'customerModel' => $customer)
        );
    }

    /**
     * Subscribe customer callback
     *
     * @param array $args
     */
    public function subscribeCustomer($args)
    {
        // Retrieve models used in this example and previously passed as arguments
        $customer = $args['customerModel'];
        $subscriber = $args['subscriberModel'];

        // Set data on the $customer model
        // Data is in $args['row'] as explained in this post
        $customer->setData($args['row']);

        // Set the 'is_subscribed' data on the $customer model
        // This is required by the subscribeCustomer() method used in the next lines
        $customer->setIsSubscribed(true);

        // Make sure to reset the $subscriber model by setting its ID to null
        $subscriber->setId(null);

        // Call to subscribeCustomer() method in the $subscribe model to proceed to subscription
        $subscriber->subscribeCustomer($customer);
    }
}

All that in less than 15 lines of code.