Magento集合代表了分组模型一种很方便的方式。得益于集合模型提供的方法,我们可以用很少或者几乎不用SQL语句来从数据库中获取一组模型。集合可能很大(新产品,金额互,类别,大量的属性等),成为一件棘手的工作。让我们看看怎么做能让大集合变得更易于管理。

如果说我们想让我们客户的名字大写,那么我们可能会这么做:

$customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToSelect(array('firstname'), 'inner');
foreach ($customers as $customer) {
    $customer->setFirstname(strtoupper($customer->getFirstname()));
    $customer->save();
}

这段代码在小集合里运行得很不错,但是作用于大一点的集合时就会耗尽内存,产生下面的错误:

“Fatal error: Allowed memory size of X bytes exhausted (tried to allocate X bytes) in magento_site\lib\Varien\Data\Collection.php on line 550“.

为什么会这样呢?

集合基本上是一个对象数组,这里是一个客户对象数组。对象越多,选取的属性越多,产生的结果数组就越大,这样就导致高内存的使用。

解决Magento中的这个问题要实施到Mage_Core_Model_Resource_Iterator模型的形式。它允许我们从数据库里一个一个地抓取数据,这与我们上面例子中一次性抓取数据相对。这个迭代器中最主要的就是walk()方法,它需要两个强制参数去工作,收集查询语句和回调方法。

如果我们看下方法的实现:

public function walk($query, array $callbacks, array $args=array(), $adapter = null)
{
    $stmt = $this->_getStatement($query, $adapter);
    $args['idx'] = 0;
    while ($row = $stmt->fetch()) {
        $args['row'] = $row;
        foreach ($callbacks as $callback) {
            $result = call_user_func($callback, $args);
            if (!empty($result)) {
                $args = array_merge($args, $result);
            }
        }
        $args['idx']++;
    }
    return $this;
}

我们可以看到,它需要查询语句,执行语句,一个一个地获取结果然后传送到回调方法。通过回调方法的结果被放到$args里,在回调功能里,我们可以访问$args[‘row’]的数据。

下面是我类似于迭代器的例子:

public function uppercaseAction()
{
    $customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToSelect(array('firstname'), 'inner');
    Mage::getSingleton('core/resource_iterator')->walk($customers->getSelect(), array(array($this, 'customerCallback')));
}

public function customerCallback($args)
{
    // get customer model
    $customer = Mage::getModel('customer/customer’);
    $customer->setData($args['row']);
    $customer->setFirstname(strtoupper($customer->getFirstname()));
    $customer->getResource()->saveAttribute($customer, 'firstname');
}

事情在这里变得相当简单。我们定义集合并传送它的sql查询语句给迭代器的walk()方法。迭代器执行给出的语句,并在每个数据库返回的结果后面调用customerCallback() 方法。在调用方法中,我们实例化客户模型,映射$args[‘row’]中的数据到模型并改变其名字属性为大写。

有一件要注意的是对象保存。为了我们的脚本尽可能有效地执行,我们节省了大量模型。使用"classic"保存是一件耗时耗资源的工作。由于我们只改变一个属性,所以最好使用saveAttribute()方法,这要快得多。

这样,再也没有"out of memory"报警了。

results matching ""

    No results matching ""