Можно ли перебирать коллекции Magento с нумерацией страниц?

21

Под этим я подразумеваю - есть ли способ сделать:

$collection = $model->getCollection();
foreach ($collection as $item) { 
    $item->doStuff();
}

Таким образом, что даже если бы коллекция имела 100 тыс. Строк, она бы загружала только страницу строк за один раз из MySQL и волшебным образом разбивала их на страницы для вас за кулисами.

Смотреть на Varien_Data_Collection_Db::load()это не представляется возможным, но просто хотелось проверить. Это похоже на то, что должно быть общей потребностью.

kalenjordan
источник

Ответы:

18

Вы должны действительно использовать

Mage::getSingleton('core/resource_iterator')

для этой цели, поскольку он существует исключительно по причинам производительности, которые вы упомянули.

В противном случае вы можете использовать чуть менее элегантное решение, используя цикл с setPageSize- здесь есть хороший пример, /programming/3786826/how-to-loop-a-magento-collection

Бен Лессани - Сонасси
источник
1
Вы, сэр, джентльмен и ученый.
Календжордан
+1 setPageSizeпотому что это семантика.
Philwinkle
Еще одна вещь, которую я понял, заключается в том, что core/resource_iteratorрешение на самом деле не разбивает запрос на MySQL. Он загружает весь набор результатов одновременно, но затем он дает вам строку за раз, чтобы иметь дело с вашим кодом PHP. Таким образом, он предотвращает сбои памяти в PHP, но в какой-то момент запускает максимальные размеры пакетов mysql, если результирующий набор очень большой. Я думаю, что я собираюсь попытаться создать хороший абстрактный chunked resource_iterator, используяsetPageSize()
kalenjordan
Да, я как бы пропустил это в погоне за минвоаром! Это действительно нацелено на единственную загрузку продукта против разбитой на страницы коллекции. Но это должно послужить основой для дальнейшего развития.
Бен Лессани - Сонасси
Я реализовал универсальный пакетный итератор, который пакетирует запросы к MySQL, но также предоставляет отдельный обратный вызов элемента коллекции. Любопытно, что вы думаете: gist.github.com/kalenjordan/5483065
kalenjordan
5

Я согласен с Беном Лессани в том, что вам следует использовать core/iteratorмодель ресурсов для загрузки больших коллекций по одной строке, если это возможно .

Тем не менее, есть ограничения. Как объясняется в разделе « addAttributeToSelect не работает с core / resource_iterator? », Он не очень хорошо работает с моделями EAV, если вам нужно включить значения из таблиц значений атрибутов.

И связанный пример из StackOverflow на самом деле не так хорош, потому что он повторяет один и тот же запрос с разными LIMITвыражениями. Для сложных запросов это может быть проблемой производительности, но что еще более важно, вы получите дубликаты, если между ними будут добавлены новые строки.

Лучший способ обработки коллекций в чанках - это сначала загрузить все идентификаторы, а затем использовать эти идентификаторы в качестве фильтра для действительной выгружаемой коллекции.

Простой пример для продуктов:

$ids = Mage::getModel('catalog/product')
    ->getCollection()
    ->getAllIds();

$page = 1;
do {
    $collection = Mage::getModel('catalog/product')
        ->getCollection()
        ->addIdFilter($ids)
        ->setPageSize(100)
        ->setCurPage($page);

    $results = $collection->load();

    // do stuff ......

    $page++;

} while ($results->count());
Фабиан Шменглер
источник