Wydajność: Dodaj poziomy zapasów magazynowych do list produktów list.phtml wszystkich typów produktów


12

TL; DR , Wymagane jest, aby stan zapasów wyświetlać się na stronie z listą produktów kategorii przy jak najmniejszej liczbie dodatkowych zapytań / pamięci, mając na uwadze wydajność zgodną ze środowiskiem Magento.


Po przeczytaniu artykułu Vinai Kopp na temat wstępnego ładowania w celu zwiększenia skalowalności .

Jaki jest najlepszy sposób na uwzględnienie stanów magazynowych na stronach z listą produktów kategorii ( list.phtml ) przy jak najmniejszej liczbie dodatkowych zapytań / obciążeń ze względu na wydajność?

Mam świadomość kilku podejść:

Wydaje się, że afterLoad () działa dobrze zmedia_gallerywłączeniem bez dodatkowych zapytań, jednak nie udało mi się wdrożyć tego samego podejścia z zapasami.

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

Bezpośredni SQL, aby zebrać wymagane dane równolegle do kolekcji, product_idna przykład za pomocą klucza. Ale szukam więcej środków poprzez ramy.

Obecnie po prostu ładuję stock_itemobiekt poprzez:

$_product->load('stock_item')->getTotalQty(); Co działa, ale zauważam dodanie więcej zapytań, aby uzyskać podsumowanie zapasów magazynowych wszystkich produktów w kolekcji.

...

__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)

...

co dziwne, to działa. Magia dzieje się w Mage_Eav_Model_Entity_Abstract-> load ($ object, $ entityId, $ attribute). Jeśli $ atrybuty są puste, wywoła loadAllAttribute (obiekt $). Więc $ product-> load („bla”) załaduje wszystkie brakujące atrybuty, w tym „media_gallery” - William Tran 19 listopada 14 o 4:45

Dodaj potrzebne wartości do już załadowanej kolekcji.

Wydaje się, że najlepszym rozwiązaniem jest oczywiste proste podejście polegające na dodaniu potrzebnych danych do kolekcji produkcyjnej najwyższego poziomu w warstwie / filtrze.

Zauważyłem, że obserwator addInventoryDataToCollection () w Mage_CatalogInventory_Model_Observer brzmi, jakby to się udało, ale dodanie metody do niestandardowego modułu obserwatora nie wydaje się być kompatybilne.

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

Co skutkuje w:

Ostrzeżenie: Podano niepoprawny argument dla foreach () w /app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php w linii 71


1
Dobry boomer
Amit Bera

Odpowiedzi:


4

Prawdziwym problemem tutaj nie jest wstępne ładowanie, to dokładność. Stosunkowo łatwo jest uzyskać zapasy dla kolekcji produktów:

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

Teraz dzięki dwóm zapytaniom masz wszystkie potrzebne informacje. Po prostu trudno je ze sobą powiązać, co można naprawić, używając tablicy asocjacyjnej 'product_id' => 'stock'i pisząc getter. Ponadto addProductsFilter można zoptymalizować:

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

Oszczędza to sprawdzania typu i klonowania tablicy.

Problemem jest teraz blokowanie pamięci podręcznej HTML. Ta strona kategorii musi zostać wyczyszczona, gdy jakikolwiek towar jest aktualizowany na produkcie w nim zawartym. O ile mi wiadomo, nie jest to standard, ponieważ tylko zmiana stanu magazynowego usuwa stronę kategorii zawierającą produkt (a dokładniej zmianę widoczności). Więc musisz obserwować przynajmniej, cataloginventory_stock_item_before_savea może kilka innych i wyczyścić pamięć podręczną html bloku (i pamięć podręczną FPC) dla tej strony kategorii.


Twój ostatni punkt jest powodem, dla którego wybraliśmy dodatkowe żądanie odzyskania danych giełdowych. Jeśli masz kategorie z szybko zmieniającymi się produktami, pamięć podręczna spędza większość czasu na opróżnianiu / unieważnianiu. jedyne, co musimy wyczyścić pamięć podręczną strony kategorii, to usunięcie produktu z tej kategorii. Nie ma jednego magicznego rozwiązania, które musiałoby wypracować najbardziej efektywne wdrożenie w poszczególnych przypadkach. Jeśli chcesz, możesz buforować dane giełdowe, więc tylko to trzeba przebudować po opróżnieniu, a nie całą stronę.
john-jh

Jeśli zaimplementujesz dane zapasowe w taki sam sposób, jak teraz, używając danych JavaScript do aktualizacji DOM, możesz napisać to przy użyciu bloku z własnym kluczem pamięci podręcznej i unieważnić tylko dane zapasowe. W ten sposób zrobiłbym to w twojej sytuacji i nie używałbym FPC opartego na ESI, ale takiego, który może dziurkować bloki w procesorze żądań. Nie tylko dla bitu routera, ale także do wymagania tylko jednego interpretera php na stronę. Kiedy z jakiegoś powodu maszyna znajduje się pod presją, twój interpreter php jest zasobem najbardziej obciążającym procesor. To zaczyna brzmieć coraz bardziej jak miły weekendowy projekt ;-)
Melvyn,

3
Jest to rodzaj rzeczy, które sprawiają, że praca jest naprawdę interesująca, gdy musisz naprawdę pomyśleć o wdrożeniu oraz potencjalnych problemach i wąskich gardłach. Zdecydowanie widzę, skąd pochodzisz z pojedynczym procesem PHP, w tej chwili nie jest to dla nas problem, ale widzę, jak wpłynie to na twoją skalę. Wartości zapasów wyświetlane na stronie nie są krytyczną funkcjonalnością, więc ładujemy ją jako zasób dodatkowy po załadowaniu bez wpływu na użytkownika. To wstyd na liście produktów, a nie funkcja domyślna.
john-jh

1
Tak, bawię się teraz pomysłem, aby pobrać to bezpośrednio od Redis i odciąć php. Jest tu naprawdę ciekawa praca Yichuna Zhanga na otwartym Resty .
Melvyn,

2

Widzę, że już zaakceptowałeś i bez wątpienia coś zaimplementowałeś, jednak chciałbym zwrócić uwagę na to, jak blisko byłeś, addInventoryDataToCollection()ale wygląda na to, że źle wpisałeś plik konfiguracyjny lub korzystamy z bardzo różnych wersji Magento. Moja kopia CatalogInventory/etc/config.xmlma inną wywoływaną metodęcatalog_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() jest wywoływany <sales_quote_item_collection_products_after_load>

Źródłem addStockStatusToCollection()jest:

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

Możesz albo ustawić flagę require_stock_itemsw kolekcji, zanim się załaduje, prawdopodobnie nie jest to takie łatwe dla bloku za listą kategorii, albo możesz wywoływać Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection)ręcznie w kolekcji, po jej załadowaniu. addItemsToProducts()pobiera dla ciebie wszystkie produkty StockItems i dołącza je do kolekcji produktów

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

Czy w ogóle używasz Lakieru lub FPC, czy planujesz w przyszłości?

Stwierdziliśmy, że przy wymaganej liczbie dziurkaczy / żądań ESI na listach produktów prawie nie warto było umieszczać buforowania na miejscu, dlatego zdecydowaliśmy się na inne podejście.

Wdrożyliśmy rozwiązanie na jednej stronie internetowej, która wykorzystuje żądanie AJAX do niestandardowego kontrolera w celu pobrania danych magazynowych produktów, a javascript obsługuje aktualizacje DOM. Dodatkowe żądanie danych magazynowych zajmuje ~ 100 ms, co wcale nie wpływa na ogólny (widoczny) czas ładowania strony. W połączeniu z gotowym FPC upuszczającym żądanie strony poniżej 100 ms masz jedną szybką witrynę o niskim koszcie wydajności do wyświetlania danych giełdowych na listach produktów.

Wszystko, co musisz zrobić, to dodać szablon productId do html opakowania każdego produktu, aby skrypt JavaScript wiedział, które dane giełdowe mają zastosowanie do każdego produktu.

Jeśli spojrzysz na dodatkowe techniki buforowania rzeczywistych danych o zapasach, możesz spaść to znacznie poniżej 100 ms, bez konieczności inicjowania Maga / trafiania do bazy danych na każde żądanie.

Przepraszamy, jeśli nie jest to zgodne z tym, czego szukasz, ale uznaliśmy, że jest to najlepsze podejście do skalowalności i wydajności w stosunku do naszych wymagań.


2
Wdróż małpę chaosu i zobacz, co się stanie, gdy pamięć podręczna się przewróci. Pytanie wyraźnie wspomina, że ​​nie należy polegać na pamięci podręcznej. Takie odpowiedzi sprawiają, że naszym zadaniem jest przekonanie klienta, że ​​FPC nie naprawi swojego szablonu BuiltIn / MomsBasement o wiele trudniej.
Melvyn

Naprawdę źle rozumiesz moją odpowiedź, jeśli uważasz, że sugeruję FPC / Lakier dla wydajności i jako rozwiązania. Powiedziałem, że JEŚLI używają lub rozważają FPC / Vanish, powinni zbadać to podejście, aby zmniejszyć liczbę dziurkowania / zgłoszeń ESI. Otrzymujemy wymagane dane giełdowe w czasie krótszym niż 100 ms bez opuszczania pamięci podręcznej.
john-jh

I dostaniesz te same dane w tym samym czasie za pomocą ESI. Twoje podejście do ESI jest po prostu zupełnie inne. Dzięki temu bez pamięci podręcznej możesz uzyskać te same dane w krótszym czasie. Zamiast przechodzić przez kolejny router, aby odesłać JSON, piszesz tablicę czasową w JavaScript, która aktualizuje DOM, itp. Do diabła, umieść go w JSON, jeśli chcesz, po prostu wytnij router.
Melvyn,

1
Będzie używane buforowanie, ale pytanie dotyczy raczej optymalizacji wydajności. Trochę tła: magento.stackexchange.com/questions/13957/… (brak pamięci podręcznej / prawie żadnych) Dzięki za odpowiedź, jednak docenić dane wejściowe!
B00MER,

Nie jestem pewien, czy istnieje sposób na „najlepszą praktykę” w M2, ale Twoim rozwiązaniem jest to, jak zawsze to robiliśmy od Magento 1.x.
thdoan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.