Trabalhando com grandes collections no Magento

Esta semana me deparei com um problema aparentemente simples: precisava fazer um script que fazia um loop em todos os produtos de uma loja para remover algumas imagens duplicadas.

O problema é que a collection de produtos possuía 100.000 registros, mesmo após vários filtros. Ao fazer o ->count() da collection no Magento, me deparei com estouro de memória (Exhausted X bytes in ..), antes mesmo de chegar no usual foreach.

$products = Mage::getModel('catalog/product')->getCollection();
$total = $products->count();

O Magento carrega a collection ao acionarmos oo método count(), jogando seus valores em array, ou seja, colocando em memória como podemos ver abaixo:

//Varien_Data_Collection::count
public function count()
{
 $this->load();
 return count($this->_items);
}

O problema do count() é conhecido e de simples solução. Basta usar o método getSize() em seu lugar.

$total = $products->getSize();

O getSize() é bem menos custoso, pois invocará uma chamada apenas com o count de registros. Veja:

//Varien_Data_Collection_Db::getSize
public function getSize()
 {
 if (is_null($this->_totalRecords)) {
 $sql = $this->getSelectCountSql();
 $this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams);
 }
 return intval($this->_totalRecords);
 }

Problema resolvido né?  Não. Ainda temos o foreach.

foreach($products as $product) {
 //F...!
}

Ao chamar o foreach a coleção é carregada no load() novamente em memória, estourando novamente o erro classico de memória do PHP.

A solução foi utilizar o Resource Iterator do Magento. Ele fará a leitura de cada linha do meu SQL chamando um método de callback.

Funcionou mais ou menos assim:

Mage::getSingleton('core/resource_iterator')
 ->walk($products->getSelect(),
 array('meuMetodo')
);

function meuMetodo($args){
 Zend_Debug::dump($args, 'Detalhes da linha atual');
}

Desta forma os 100 mil registros da minha collection não são carregados de uma vez na memória, e consigo fazer o loop normalmente, registro por registro, sem me preocupar (muito) com a quantidade de registros.

Em resumo, aprendemos que:

* É melhor getSize() do que count()

* É melhor Mage::getSingleton(‘core/resource_iterator’) do que foreach

Ahh se eu soubesse disso antes…

2017-01-24T20:24:06+00:00

RECEBA DICAS VALIOSAS NO SEU EMAIL

x