Prestazioni: aggiungere i livelli di inventario nella lista di elenchi di prodotti


12

TL; DR , il requisito è che un livello di inventario sia visualizzato nella pagina di elenco dei prodotti della categoria con un numero di query / memoria aggiuntivo con in mente le prestazioni che aderiscono al framework Magento.


Dopo aver letto l'articolo di Vinai Kopp sul precaricamento per la scalabilità .

Qual è il modo migliore per includere i livelli delle scorte nelle pagine di elenco dei prodotti della categoria ( list.phtml ) con un numero di query / carichi aggiuntivi per prestazioni elevate?

Sono a conoscenza di alcuni approcci:

afterLoad () sembra funzionare bene con l'media_galleryinclusione senza ulteriori query, tuttavia non sono riuscito a implementare lo stesso approccio con l'inventario.

$attributes = $_product->getTypeInstance(true)->getSetAttributes($_product);
$media_gallery = $attributes['media_gallery'];
$backend = $media_gallery->getBackend();
$backend->afterLoad($_product);

Dirigere SQL per raccogliere i dati richiesti parallelamente alla raccolta con una product_idchiave, ad esempio. Ma alla ricerca di più di un mezzo attraverso il framework.

Attualmente sto semplicemente caricando l' stock_itemoggetto tramite:

$_product->load('stock_item')->getTotalQty(); Il che funziona, ma noto l'aggiunta di più query per ottenere i totali dello stock di inventario di tutti i prodotti della collezione.

...

__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)
__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)
__EAV_LOAD_MODEL__ (Mage_Catalog_Model_Product, id: stock_item, attributes: NULL)

...

stranamente, funziona. La magia si verifica in Mage_Eav_Model_Entity_Abstract-> load ($ oggetto, $ entityId, $ attributi). Se $ attributi è vuoto, chiamerà loadAllAttribute ($ object). Quindi $ product-> load ('blah') caricherà tutti gli attributi mancanti, incluso 'media_gallery' - William Tran, 19 novembre 14 alle 16:45

Aggiungi i valori necessari alla raccolta già caricata.

L'approccio semplice ovvio di aggiungere i dati necessari alla raccolta di produzione di livello superiore nel layer / filtro sembrerebbe l'approccio migliore.

Ho notato un osservatore addInventoryDataToCollection () in Mage_CatalogInventory_Model_Observer che suona come se potesse raggiungere tale obiettivo, ma l'aggiunta del metodo a un osservatore di moduli personalizzati non sembra compatibile.

<events>
    <catalog_product_collection_load_after>
        <observers>
            <inventory>
                <class>cataloginventory/observer</class>
                <method>addInventoryDataToCollection</method>
            </inventory>
        </observers>
    </catalog_product_collection_load_after>
</events>

Che si traduce in:

Avvertenza: argomento non valido fornito per foreach () in /app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php sulla riga 71


1
Buona domanda boom
Amit Bera

Risposte:


4

Il vero problema qui non è il precaricamento, è la precisione. È relativamente facile ottenere la quantità di stock per una raccolta di prodotti:

$products = Mage::getModel('catalog/product')->getCollection()
    ->addCategoryFilter($_category);
$stockCollection = Mage::getModel('cataloginventory/stock_item')
    ->getCollection()
    ->addProductsFilter($products);

Ora con due query, hai tutte le informazioni di cui hai bisogno. Sono difficili da mettere in relazione l'uno con l'altro, cosa che può essere risolta usando una matrice associativa 'product_id' => 'stock'e scrivendo un getter. Inoltre, addProductsFilter può essere ottimizzato:

public function addProductIdsFilter(array $productIds)
{
    if(empty($productIds) {
        $this->_setIsLoaded(true);
    }
    $this->addFieldToFilter('main_table.product_id', array('in' => $productIds));
    return $this;
}

Ciò consente di risparmiare il controllo del tipo e la clonazione dell'array.

Il problema ora è bloccare la cache HTML. Questa pagina di categoria deve essere eliminata quando uno stock viene aggiornato su un prodotto in esso contenuto. Per quanto ne so, questo non è standard, poiché solo una modifica dello stato delle scorte elimina una pagina di categoria contenente un prodotto (o più precisamente, una modifica della visibilità). Quindi dovrai osservare almeno cataloginventory_stock_item_before_savee forse alcuni altri ed eliminare la cache html del blocco (e la cache FPC) per quella pagina di categoria.


Il tuo ultimo punto è il motivo per cui abbiamo richiesto la richiesta aggiuntiva di recuperare i dati di magazzino. Se disponi di categorie con prodotti in rapido movimento, la cache impiegherà la maggior parte del tempo a svuotare / invalidare. l'unica volta in cui è necessario eliminare una cache della pagina di categoria è se un prodotto viene rimosso da quella categoria. Non esiste una soluzione magica per elaborare l'implementazione più efficiente caso per caso. Se lo si desidera, è possibile memorizzare nella cache i dati dello stock in modo che solo questi debbano essere ricostruiti dopo lo scaricamento anziché l'intera pagina.
john-jh,

Se si implementano i dati di borsa nello stesso modo in cui si fa ora, utilizzando i dati JavaScript per aggiornare il DOM, è possibile scriverli utilizzando un blocco con la propria chiave di cache e invalidare solo i dati di borsa. Questo è il modo in cui lo farei per la tua situazione e non utilizzerei un FPC basato su ESI, ma uno che può bucare blocchi nel processore delle richieste. Non solo per il bit del router, ma anche per richiedere un solo interprete php per pagina. Quando una macchina viene messa sotto pressione per qualsiasi motivo, il tuo interprete php è la risorsa più intensiva della CPU. Sta iniziando a sembrare sempre più un bel progetto per il fine settimana ;-)
Melvyn,

3
Questo è il tipo di cose che rendono il lavoro davvero interessante in cui devi davvero pensare all'implementazione e ai potenziali problemi e strozzature. Vedo con aria di sfida da dove vieni con il singolo processo PHP, questo al momento non è un problema per noi, ma posso vedere come avrebbe un impatto quando scala. I valori di stock visualizzati nella pagina non sono funzionalità critiche, quindi lo cariciamo come risorsa secondaria dopo il caricamento senza influire sull'utente. È uno stock vergognoso nell'elenco dei prodotti, non una funzionalità predefinita.
john-jh,

1
Sì, sto giocando con l'idea ora di prendere questo direttamente da Redis e tagliare php. C'è un lavoro davvero interessante qui di Yichun Zhang sull'Open Resty .
Melvyn,

2

Vedo che hai già accettato e senza dubbio implementato qualcosa ormai, tuttavia vorrei sottolineare quanto eri vicino addInventoryDataToCollection()ma sembra che tu abbia citato erroneamente il file di configurazione o che stiamo usando versioni di magento molto diverse. La mia copia di CatalogInventory/etc/config.xmlha richiesto un metodo diversocatalog_product_collection_load_after

 <catalog_product_collection_load_after>
    <observers>
        <inventory>
            <class>cataloginventory/observer</class>
            <method>addStockStatusToCollection</method>
        </inventory>
    </observers>
 </catalog_product_collection_load_after>

addInventoryDataToCollection() viene chiamato <sales_quote_item_collection_products_after_load>

La fonte per addStockStatusToCollection()è:

public function addStockStatusToCollection($observer)
{
    $productCollection = $observer->getEvent()->getCollection();
    if ($productCollection->hasFlag('require_stock_items')) {
        Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection);
    } else {
        Mage::getModel('cataloginventory/stock_status')->addStockStatusToProducts($productCollection);
    }
    return $this;
}

Puoi impostare il flag require_stock_itemssulla raccolta prima che sia caricato, probabilmente non è così facile per il blocco dietro l'elenco delle categorie, oppure puoi chiamare Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection)manualmente sulla raccolta, dopo che è già stato caricato. addItemsToProducts()ottiene tutti gli articoli per te e li allega al tuo ProductCollection

public function addItemsToProducts($productCollection)
{
    $items = $this->getItemCollection()
        ->addProductsFilter($productCollection)
        ->joinStockStatus($productCollection->getStoreId())
        ->load();
    $stockItems = array();
    foreach ($items as $item) {
        $stockItems[$item->getProductId()] = $item;
    }
    foreach ($productCollection as $product) {
        if (isset($stockItems[$product->getId()])) {
            $stockItems[$product->getId()]->assignProduct($product);
        }
    }
    return $this;
}

1

Stai usando Varnish o FPC o stai pianificando di farlo in futuro?

Abbiamo riscontrato che la quantità di perforazioni / richieste ESI richieste negli elenchi dei prodotti non valeva quasi la pena mettere in atto la memorizzazione nella cache, quindi ho optato per un approccio diverso.

Abbiamo implementato una soluzione su un sito Web che utilizza una richiesta AJAX a un controller personalizzato per recuperare i dati di magazzino per i prodotti e javascript gestisce gli aggiornamenti DOM. La richiesta aggiuntiva per i dati di stock richiede ~ 100 ms, il che non ha alcun impatto sul tempo di caricamento complessivo della pagina (visibile). Abbina che con FPC innescato che riduce la richiesta della pagina fino a meno di 100 ms hai un sito rapido con sovraccarico a basse prestazioni per la visualizzazione dei dati di magazzino negli elenchi dei prodotti.

Tutto quello che devi fare per quanto riguarda il modello è aggiungere il productId a ciascun wrapper di prodotti html in modo che il tuo javascript sappia quali dati di magazzino applicare a ciascun prodotto.

Se si esaminano le tecniche di memorizzazione nella cache aggiuntive per i dati di stock effettivi, è possibile che scendano ben al di sotto di 100 ms senza dover avviare Mage / colpire il database su ogni richiesta.

Ci scusiamo se questo non è in linea con quello che stai cercando, ma abbiamo trovato che fosse l'approccio migliore per la scalabilità e le prestazioni rispetto ai nostri requisiti.


2
Distribuisci la scimmia del caos e vedi cosa succede quando la tua memoria cache cade. La domanda menziona specificamente di non fare affidamento sulla cache. Sono risposte come questa che rendono il nostro lavoro di convincere un cliente che FPC non risolverà il modello BuiltIn / MomsBasement in modo molto più difficile.
Melvyn,

Hai frainteso davvero la mia risposta se pensi che sto suggerendo FPC / Vernice per le prestazioni e come soluzione. Ho affermato che se stanno usando o considerando FPC / Vanish dovrebbero studiare questo approccio per ridurre i fori / richieste ESI. Stiamo ottenendo i dati di stock necessari in meno di 100 ms senza perdite di cache.
john-jh,

E otterresti gli stessi dati contemporaneamente usando ESI. Il tuo approccio con ESI è sostanzialmente diverso. E così, senza alcuna cache è ancora possibile ottenere gli stessi dati, in meno tempo. Invece di passare attraverso un altro router per inviare indietro JSON, scrivi un array stock in JavaScript che aggiorna il DOM, ecc. Inferno, inseriscilo in JSON se vuoi, basta tagliare il router.
Melvyn,

1
Verrà utilizzata la memorizzazione nella cache, ma la domanda è più in linea con le ottimizzazioni delle prestazioni. Alcuni retroscena: magento.stackexchange.com/questions/13957/… (nessuna cache / quasi nessuna) Grazie comunque per la risposta, apprezzo l'input!
B00MER,

Non sono sicuro che ci sia un modo "best practice" per farlo in M2, ma la tua soluzione è come l'abbiamo sempre fatto da Magento 1.x.
thdoan
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.