Effizientes Aufrufen, Filtern und Laden von Sammlungen

15

Im Moment verwende ich viele Sammlungen, die in foreach-Schleifen verschachtelt sind. Ist es möglich, diese Dinge ein paar Ebenen nach oben zu bewegen? Derzeit bin ich gezwungen, Sammlungen mit mehr als 51.000 Entitäten immer wieder neu zu laden, was die Dinge enorm verlangsamt. Speziell die Kitinventory-Kollektionen.

<?php
class Codespace_Module_Helper_Item extends other_one{

function functionOne($collection){
    ...
    $data = $collection->getData();
    foreach($data as $item){
        $this->_functionTwo($item);
    }
    ...
}

function _functionTwo($item){
    $model = Mage::getModel('catalog/product');
    $id = $model->getIdBySku($item['sku']);
    $inventoryStatus = Mage::getResourceSingleton('catalog/product')->getAttributeRawValue($id, 'product_inventory_status', 1);
    $invStatus = $model->getResource()->getAttribute('product_inventory_status')->getSource()->getOptionText($inventoryStatus);
    if ($invStatus && $id) {
        if ($invStatus !== 'Z') {
            $stockItem = Mage::getModel('cataloginventory/stock_item');
            $stockItem->setData(array());
            $stockItem->loadByProduct($id);
            if ($stockItem->getQty() != $item['quantity']) {
                $stockItem->setQty(item['quantity']);
                $stockItem->save();
                $this->functionThree($item['sku']);
            }
        }
    }
}

function functionThree($sku){
    $collectionOfKits = Mage::getModel('kitinventory/kitinventory')->getCollection()->addFieldToFilter('related_sku',$sku);
    if($collectionOfKits->getSize()){
        foreach($collectionOfKits as $kit){
            $kitSku = $kit->getSku();
            $kitCollection = Mage::getModel('kitinventory/kitinventory')->getCollection()->addFieldToFilter('kit_sku',$kitSku)->setOrder('related_sku','ASC');
            ...
            foreach($kitCollection as $component){
                $componentSkus[] = $component->getRelatedSku();
                $componentRequiredQuantity[] = $component->getRequiredQuantity();
            }
            $componentProductCollection = Mage::getModel('catalog/product')->getCollection();
            $componentProductCollection->joinField('qty',
                'cataloginventory/stock_item',
                'qty',
                'product_id=entity_id',
                '{{table}}.stock_id=1',
                'left');
            $componentProductCollection->addAttributeToFilter('sku', array('in' => $componentSkus));
            foreach($componentProductCollection as $component){
                $quantity = $component->getQty();
                ...
            }
            $kitId= Mage::getModel('catalog/product')->getIdBySku($kitSku)
            $kitStockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($kitId);
            $this->functionFour($kitStockItem,$kitSku,$amountOfKitsPossible);
        }
    }
}

function functionFour($kitStockItem,$kitSku,$amountOfKitsPossible){
    ...
    $kitStockItem->setQty($quantity);
    $kitStockItem->save();
    ...
}

BEARBEITEN: Dies ist die aktuelle Funktionalität, die ich mir ausgedacht habe. Ich denke immer noch, dass es eine bessere Möglichkeit gibt, mit diesen Sammlungen umzugehen.

easymoden00b
quelle
An welche Art von Sammlung wird übergeben functionOne($collection)? In welcher Reihenfolge würde die Größe / Anzahl der Artikel liegen? Muss eine Schleife ausgeführt werden, um die SKUs zu erhalten?
Chem.
@ 7ochem Es handelt sich um eine benutzerdefinierte Sammlung, die aus neuen Inventardaten erstellt wurde, die wir von unserem Inventarverwaltungssystem erhalten. Es enthält den Namen des Artikels, die Menge des vorliegenden Artikels und die Artikelnummer des Artikels. Es kann möglicherweise mehr als 60.000 Elemente enthalten.
easymoden00b

Antworten:

9

Es gibt einige Dinge, an denen Sie arbeiten können.

  • Da keine Referenz übergeben wird, können Sie mithilfe von zusätzlichem Speicher Objekte übergeben, Arrays können jedoch nicht standardmäßig als Referenz übergeben werden. Oder fügen Sie eine &in der Funktion angegebene Parameterdeklaration hinzufunction hello(array &$world)
  • Ungültige Schecks, wenn etwas nicht da ist, sofort zurücksenden. Versuchen Sie nicht, etwas zu finden, das nicht da ist
  • Die Lesbarkeit kann manchmal schwierig sein
    • füge ein paar Dokumente hinzu (damit du verstehst, ob du sie in ein paar Tagen, Monaten oder Jahren siehst)
    • Intelligentere ifAnweisungen, um weniger Einrückungen zu erzielen
  • funktionen sollten nur einen zweck haben, den bestand aktualisieren oder die aktualisierung betreffen, nicht beides, also vielleicht sogar einige funktionen in noch kleinere funktionen schneiden. Versuchen Sie, eine solche Logik in Ihrem Kopf zu erstellen und von dort aus zu überarbeiten.
  • Werfen Sie einen Blick auf ->cleanModelCache()->clearInstance()von Mage_Core_Model_Model_Abstract, um zugrunde liegende Daten für einige Objekte zu löschen und die Dinge zu beschleunigen.
  • grob alle anderen Dinge, die bereits gesagt wurden.

Es wurde eine aktualisierte Version Ihres Codes mit einigen Inline-Empfehlungen zu Ihrem aktuellen Code hinzugefügt. Ich könnte ein bisschen weitermachen, aber es würde momentan nicht mehr dazu beitragen.

Funktion 1: Zweck ist das Durchlaufen der Sammlung

    /**
     * Walk collection
     * 
     * @param Mage_Core_Model_Resource_Db_Collection_Abstract $collection
     * @return void
     */
    public function functionOne($collection)
    {
        // ...

        // Walk collection, create references instead of passing array data
        foreach ($collection as $item) {

            // Update stock for product
            if (!$this->_functionTwo($item)) {
                // Not updated, continue next
                continue;
            }

            // Update related products
            $this->_functionThree($item); // Use same object again, no extra memory is used
        }

        // ...
    }

Funktion 2: Zweck ist die Aktualisierung des Bestands bei Änderung

    /**
     * Update stock item if changed, returns true if updated
     * 
     * @param Mage_Core_Model_Model_Abstract $item
     * @return bool
     */
    function _functionTwo($item)
    {
        $model = Mage::getModel('catalog/product');
        /** @var $model Mage_Catalog_Model_Product */

        $id = $model->getIdBySku($item->getData('sku'));

        if (!$id) {
            // no id found, so stop looking nothing up
            return false;
        }

        // Get option value for store 1
        $inventoryStatus = $model->getResource()
                ->getAttributeRawValue($id, 'product_inventory_status', 1);

        if (!$inventoryStatus) {
            // No need for another lookup in db, because the status isn't set
            return false;
        }

        $invStatus = $model->getResource()
                ->getAttribute('product_inventory_status')
                ->setStoreId(0) // Get admin value
                ->getSource()
                ->getOptionText($inventoryStatus);

        if (!$invStatus) {
            // No need for another lookup in db, because the status has no text
            return false;
        }

        if ($invStatus === 'Z') {
            // Inventory status to not change something
            return false;
        }

        $stockItem = Mage::getModel('cataloginventory/stock_item');
        /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */

        // $stockItem->setData(array()); // unneeded piece of code
        $stockItem->loadByProduct($id);

        if ($stockItem->getQty() == $item->getData('quantity')) {
            // Valid stock
            return false;
        }

        // Update stock
        $stockItem->setQty($item->getData('quantity'));
        $stockItem->save();

        // End function and call function three separately, does something else
        return true;
    }

Funktion 3: Zweck der Aktualisierung zugehöriger Lagerartikel

    /**
     * Update related stock items, return false if no related items are found
     * 
     * @param Mage_Core_Model_Model_Abstract $item
     * @return bool
     */
    function functionThree($item)
    {

        $collectionOfKits = Mage::getModel('kitinventory/kitinventory')
                ->getCollection()
                ->addFieldToFilter('related_sku', $item->getData('sku')); // Check if your indexes are set on these columns

        if (!$collectionOfKits->getSize()) {
            // Nothing found to relate to
            return false;
        }

        $connection = Mage::getSingleton('core/resource')
                ->getConnection('core_write');

        // Walk kits
        foreach ($collectionOfKits as $kit) {

            // getData is slightly faster then getSku(unless you've implemented it in your model)
            // getSku -> __call('getSku') -> get -> lowercase('sku') -> getData('sku') | note, Magento has some internal caching in this 
            $kitSku = $kit->getData('sku');

            $kitCollection = Mage::getModel('kitinventory/kitinventory')
                    ->getCollection()
                    ->addFieldToFilter('kit_sku', $kitSku)
                    ->setOrder('related_sku', 'ASC');

            // Use just a fetchAll to create a fast db query
            $select = $kitCollection->getSelect();

            $select->reset(Zend_Db_Select::COLUMNS)
                    ->distinct()
                    ->columns('related_sku')
                    ->columns('required_quantity');

            // Fetch component sku
            $componentSkus = $connection->fetchAll($select, 0);

            // Fetch required quantity
            $componentRequiredQuantity = $connection->fetchCol($select, 1);

            // ...

            $componentProductCollection = Mage::getModel('catalog/product')
                    ->getCollection()
                    ->joinField('qty',
                    'cataloginventory/stock_item',
                    'qty',
                    'product_id = entity_id',
                    '{{table}}.stock_id = 1',
                    'left');
            $componentProductCollection->addAttributeToFilter('sku', array('in' => $componentSkus));

            // Next line will invoke a load on the product collection
            foreach ($componentProductCollection as $component) {
                $quantity = $component->getQty();

                // ...

            }
            // You could choose to do a fetchAll here instead to get just the data you need
            $connection = $componentProductCollection->getConnection();

            foreach ($connection->fetchAll($componentProductCollection->getSelect()) as $row) {
                // Will have a array here
                $quantity = $row['quantity'];

                // ... -- do not not which funky magic happens here
            }


            $kitId = Mage::getModel('catalog/product')
                    ->getIdBySku($kitSku);
            if (!$kitId) {
                // No id
                continue;
            }

            // You could also take a look if you can sum the stock and do a single update instead
            $kitStockItem = Mage::getModel('cataloginventory/stock_item')
                    ->loadByProduct($kitId);
            $this->functionFour($kitStockItem, $kitSku, $amountOfKitsPossible);

            // Or something like this, update single field
            $connection->update($kitStockItem->getResource()->getMainTable(), array('qty' => $quantity), 'item_id = ' . $kitStockItem->getId());
        }

        return true;
    }

Funktion 4: Musste einige glückliche (oder unglückliche) Vermutungen anstellen, für den Moment ist es eine nutzlose Funktion, die wie in Funktion 3 hinzugefügt werden kann.

    /**
     * Save stock item if changed and something else, rather not say ;-)
     * 
     * @param Mage_Catalog_Inventory_Model_Stock_Item $kitStockItem
     * @param string $kitSku
     * @param int $amountOfKitsPossible Guessed it
     */
    function functionFour($kitStockItem, $kitSku, $amountOfKitsPossible)
    {

        // ...

        // Do not know the rest of the code, so I wouldn't know which I could optimize here
        // If it isn't to serious, you could look at a single query and not hitting extra functions

        // Check if changed
        if ($quantity !=$kitStockItem->getData('qty')) {
            $kitStockItem->setQty($quantity);
            $kitStockItem->save();
        }        

        // ...

    }
}
Jeroen
quelle
Du bist der Mann. Ich bin mir ziemlich sicher, dass dies funktionieren wird, und wenn dies eine deutliche Verbesserung der Verarbeitungszeit zeigt, kann dies meine Referenz für die Bearbeitung von Sammlungen sein!
easymoden00b
Ein paar kleine Fehler, aber es ist viel besser gebaut als mein eigenes.
Easymoden00b
5

Ich wollte dies als Kommentar hinzufügen, aber ich habe noch nicht genug Repräsentanten. Schauen Sie sich hier an, wie die Magento-Kerngitter die Produktmenge mit der Katalog- / Produktkollektion verbinden: https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml /Block/Catalog/Product/Grid.php#L65

Wenn Sie sich der Tabelle anschließen, um die Menge zu erhalten, müssen Sie dies nicht in einer Schleife aufrufen: Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();

$productCollection = Mage::getModel('catalog/product')->getCollection();
$productCollection->joinField('qty',
    'cataloginventory/stock_item',
    'qty',
    'product_id=entity_id',
    '{{table}}.stock_id=1',
    'left');
$productCollection->addAttributeToFilter('sku',array('in' => $relatedSkus));
foreach($productCollection as $product){
    $quantity = $product->getQty();
    ...// now you have your qty without having to load the product model.
}

Die andere Alternative besteht darin, zu prüfen, ob Sie die Ergebnisse dieses systemintensiven Prozesses zwischenspeichern können. Vielleicht könnten Sie eine zweite Datenbanktabelle erstellen, um die Ergebnisse zu speichern, und sie aktualisieren lassen, wie es ein Magento-Index tun würde.

Eric Seastrand
quelle
Möchten Sie mitteilen, wie ich diese Informationen dann auf ähnliche Weise wie im obigen Code aufrufen könnte? Wenn ich die Produktkollektionen generiere und einer Klassenvariablen zuordne, kann ich diese Sammlung dann im gesamten Code aufrufen? Würde das Filtern (wie im Code gezeigt) die Klassenvariable beeinflussen oder würde es unverändert bleiben, wenn ich diese gefilterte Auflistung einer anderen Variablen zuweisen würde?
easymoden00b
Ich habe ein Beispiel hinzugefügt, wie man sich dem Feld anschließt, aber dies ist nur eine kleine Optimierung. Ja: Sie können die Ergebnisse als Klassenvariablen speichern und an anderer Stelle aufrufen. Aber: Ich denke, Sie müssen jedes Mal ein neues Modell instanziieren, wenn Sie die Filterung ändern möchten (kann den Zweck
zunichte machen
Vielen Dank, es scheint, als hätte ich befürchtet. Vielen Dank für dieses Optimierungsbeispiel. Können Sie sich noch andere vorstellen? Ich werde die Verwendung jeder Sammlung im obigen Codebeispiel etwas erläutern.
easymoden00b
3

Sie müssen das Modell nicht immer wieder neu laden. Mage::getModel()Eine Referenz ist ausreichend, ohne zu wissen, wie Ihre Ressourcenmodelle eingerichtet sind. Es ist schwierig zu sagen, ob sie jedes Mal im Speicher neu initialisiert werden und in diesen Schleifen ein Leck auftritt oder nicht mehr ausreicht Speicher, der möglicherweise zu einem Plattenwechsel führt.

Eine Sammlung, die alle regiert. Refactoring der Funktionen, um nur die eine Sammlung zu referenzieren. Dies gilt auch für die Standard-SQL-Programmierung und die prozedurale Programmierung. Nehmen Sie sich etwas mehr Zeit und untersuchen Sie Ihre Sammlungen und Ressourcenmodelle, um herauszufinden, wie Sie alle Daten, die Sie benötigen, einmal, möglicherweise zweimal aus SQL abrufen und dann über genügend Arbeitsspeicher verfügen. Es ist auch einfacher, ein Ergebnis im Cache zu speichern als viele. Dies gilt auch für die in MySQL integrierten Caching-Mechanismen, da häufige Anfragen, die groß genug sind, dasselbe Problem beim Austausch von Festplatten verursachen.

Speichern Sie die E / A

Vinai hat ein gutes Beispiel für die Umsetzung des gleichen Ansatzes:

Referenzen :

B00MER
quelle