Est-il possible de parcourir les collections Magento avec une pagination native?

21

Ce que je veux dire par là, est-il possible de faire:

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

De telle manière que même si la collection avait 100 000 lignes, elle ne chargerait qu'une page de lignes à la fois depuis MySQL et les paginerait comme par magie pour vous dans les coulisses.

En regardant Varien_Data_Collection_Db::load()cela ne semble pas possible, mais je voulais juste vérifier. Cela semble être quelque chose qui devrait être un besoin commun.

kalenjordan
la source

Réponses:

18

Vous devriez vraiment utiliser

Mage::getSingleton('core/resource_iterator')

à cet effet, car il existe uniquement pour les raisons de performance que vous avez mentionnées.

Sinon, vous pouvez utiliser une solution légèrement moins élégante en utilisant une boucle avec setPageSize- il y a un bon exemple ici, /programming/3786826/how-to-loop-a-magento-collection

Ben Lessani - Sonassi
la source
1
Vous, Monsieur, êtes un gentleman et un savant.
kalenjordan
+1 setPageSizecar c'est sémantique.
philwinkle
Une autre chose que j'ai réalisée est que la core/resource_iteratorsolution ne pagine pas réellement la requête mysql. Il charge l'ensemble du jeu de résultats à la fois, mais il vous donne ensuite une ligne à la fois dans votre code PHP. Donc, cela évite les défauts de mémoire dans PHP, mais à un moment donné, il déclenchera des tailles de paquet max mysql, si l'ensemble de résultats est très grand. Je pense que je vais essayer de construire un gentil résumé de bloc de ressources en utilisantsetPageSize()
kalenjordan
Ouais, j'ai en quelque sorte omis ça à la recherche de points mwoar! C'est vraiment destiné au chargement d'un seul produit par rapport à une collection paginée. Mais il devrait servir de base sur laquelle s'appuyer.
Ben Lessani - Sonassi
J'ai implémenté un itérateur par lots générique qui regroupe les requêtes sur MySQL mais fournit également un rappel d'élément de collection individuel. Curieux de savoir ce que vous pensez: gist.github.com/kalenjordan/5483065
kalenjordan
5

Je suis d'accord avec Ben Lessani que vous devez utiliser le core/iteratormodèle de ressource pour charger les grandes collections une ligne à la fois si possible .

Cependant, il y a des limites. Comme expliqué dans « addAttributeToSelect ne fonctionne pas avec core / resource_iterator? », Cela ne fonctionne pas bien avec les modèles EAV si vous devez inclure des valeurs des tables de valeurs d'attribut.

Et l'exemple lié de StackOverflow n'est pas vraiment bon car il répète la même requête avec des LIMITexpressions différentes . Pour les requêtes complexes, cela peut être un problème de performances, mais plus important encore, vous obtiendrez des doublons si de nouvelles lignes sont ajoutées entre les deux.

Une meilleure façon de gérer les collections en morceaux consiste à charger d'abord tous les ID, puis à utiliser ces ID comme filtre pour la collection paginée réelle.

Exemple simple pour les produits:

$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());
Fabian Schmengler
la source