Raccolta, filtro e caricamento efficienti delle raccolte


15

In questo momento sto riutilizzando molte raccolte nidificate all'interno di cicli di foreach. È possibile spostare queste cose su alcuni livelli? Attualmente sono costretto a ricaricare collezioni che hanno 51k + entità più e più volte che rallentano enormemente le cose. In particolare le collezioni kitinventory.

<?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();
    ...
}

EDIT: questa è l'attuale funzionalità che mi è venuta in mente, penso ancora che ci sia un modo migliore per gestire queste raccolte.


A quale tipo di collezione viene passato functionOne($collection)? In quale ordine sarebbero le dimensioni / il conteggio degli articoli? È necessario passarci sopra per ottenere gli SKU?
7

@ 7ochem È una raccolta personalizzata basata su nuovi dati di inventario che otteniamo dal nostro sistema di gestione dell'inventario. contiene il nome dell'articolo, la quantità dell'articolo a portata di mano e lo sku dell'articolo. Può contenere potenzialmente 60k + elementi.
easymoden00b

Risposte:


9

Ci sono alcune cose su cui puoi lavorare;

  • non passando per riferimento, quindi utilizzando memoria aggiuntiva, è possibile passare oggetti, ma le matrici non possono essere passate per riferimento per impostazione predefinita. Oppure aggiungi a &nella dichiarazione del parametro funzione comefunction hello(array &$world)
  • controlli non validi, se qualcosa non è presente restituire immediatamente. Non cercare di trovare qualcosa che non c'è
  • la leggibilità a volte può essere difficile
    • aggiungi un documento (così puoi capire se lo vedi tra pochi giorni, mesi, anni)
    • ifdichiarazioni più intelligenti per ottenere meno rientri
  • le funzioni dovrebbero avere un solo scopo, aggiornare lo stock o l'aggiornamento, non entrambi, quindi forse anche tagliare alcune funzioni in funzioni ancora più piccole. Cerca di creare una tale logica nella tua mente e rielaborale da lì.
  • Date un'occhiata a ->cleanModelCache()->clearInstance()da Mage_Core_Model_Model_Abstracta cancellare i dati sottostanti per alcuni oggetti, può accelerare le cose.
  • di tutte le altre cose che sono già state dette.

Aggiunta una versione aggiornata del tuo codice con alcuni consigli in linea sul tuo codice attuale, potrei continuare un po ', ma al momento non ne aggiungerebbe altro.

Funzione 1: Scopo è percorrere la collezione

    /**
     * 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
        }

        // ...
    }

Funzione 2: Scopo è l'aggiornamento dello stock se modificato

    /**
     * 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;
    }

Funzione 3: Scopo dell'aggiornamento degli articoli di magazzino correlati

    /**
     * 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;
    }

Funzione 4: ho dovuto fare alcune ipotesi fortunate (o sfortunate), per ora è una funzione inutile, potrebbe essere aggiunta come nella Funzione 3.

    /**
     * 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();
        }        

        // ...

    }
}

Tu sei l'uomo. Sono relativamente certo che funzionerà, e se questo mostra un netto miglioramento nei tempi di elaborazione, potrebbe essere il mio riferimento per manipolare le raccolte!
easymoden00b

Alcuni piccoli errori, ma è molto meglio costruito del mio.
easymoden00b,

5

Volevo aggiungere questo come commento ma non ho ancora abbastanza rappresentante. Dai un'occhiata a come le griglie core di Magento uniscono la qtà del prodotto al catalogo / collezione di prodotti qui: https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml /Block/Catalog/Product/Grid.php#L65

Se ti unisci al tavolo per ottenere il qty, non devi chiamarlo in un ciclo: 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.
}

L'altra alternativa è vedere se è possibile memorizzare nella cache i risultati di questo processo intensivo del sistema. Forse potresti creare una seconda tabella di database per archiviare i risultati e aggiornarla come farebbe un indice magento.


ti interessa condividere come potrei quindi chiamare queste informazioni in modo simile al codice sopra? Inoltre, se generassi le raccolte di prodotti e le assegnassi a una variabile di classe sarei in grado di chiamare quella raccolta in tutto il codice? Il filtro (come mostrato nel codice) avrebbe effetto sulla variabile di classe o rimarrebbe invariato se assegnassi questa raccolta filtrata a un'altra variabile?
easymoden00b

Ho aggiunto un esempio di come aderire al campo, ma questa è solo una piccola ottimizzazione. Sì: è possibile salvare i risultati come variabili di classe e chiamarli altrove. Ma: penso che dovrai istanziare un nuovo modello ogni volta che vuoi cambiare il filtro (potrebbe vanificare lo scopo.)
Eric Seastrand

Grazie, sembra come temevo. Grazie per questo esempio di ottimizzazione. Altri a cui riesci a pensare? Spiegherò un po 'gli usi di ciascuna raccolta nell'esempio di codice sopra.
easymoden00b

3

Non è necessario ricaricare il modello più e più volte, Mage::getModel()con un riferimento è sufficiente senza sapere come sono impostati i modelli delle risorse, è difficile dire se viene reinizializzato ogni volta in memoria e in quei loop si finisce colare / a corto di memoria che potrebbe causare lo scambio del disco.

Una raccolta per dominarli tutti. Rifattorizzare le funzioni per fare riferimento solo a una raccolta. Questo è lo stesso con SQL standard e programmazione procedurale. Dedica un po 'più di tempo a studiare le tue raccolte e i tuoi modelli di risorse su come ottenere tutti i dati di cui hai bisogno da SQL una volta, forse due volte e quindi disporre di memoria sufficiente e fare riferimento ai dati per eseguire il ciclo per la visualizzazione / manipolazione. È anche più facile archiviare un risultato nella cache rispetto a molti, questo è lo stesso caso anche per i meccanismi di cache integrati di MySQL, poiché le richieste frequenti che sono abbastanza grandi causeranno lo stesso problema di scambio del disco.

Salva l'I / O

Vinai ha un buon esempio di implementazione dello stesso approccio:

Riferimenti :

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.