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 aZend_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.