Продуктивність: Додайте рівні запасів запасів у списки товарів.phtml усіх типів товарів


12

TL; DR , вимога полягає в тому, щоб запас запасів відображався на сторінці списку продуктів категорії з якомога меншими додатковими запитами / пам'яттю з врахуванням продуктивності, що дотримується рамки Magento.


Після прочитання статті Віная Коппа про попереднє завантаження для масштабованості .

Який найкращий спосіб включити запаси запасів на сторінки списку продуктів категорії ( list.phtml ) з якомога меншими додатковими запитами / завантаженнями для підвищення продуктивності?

Мені відомо кілька підходів:

afterLoad (), здається, добре працює зmedia_galleryвключенням без додаткових запитів, проте мені не вдалося реалізувати той самий підхід із інвентарем.

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

Прямий SQL для збору необхідних даних паралельно колекції, product_idнаприклад, ключем. Але шукати більше засобів через рамки.

В даний час я просто завантажую stock_itemоб'єкт через:

$_product->load('stock_item')->getTotalQty(); Що працює, але я помічаю додавання більше запитів, щоб отримати загальні запаси запасів усіх продуктів колекції.

...

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

...

як не дивно, це працює. Магія відбувається в Mage_Eav_Model_Entity_Ab Abstract-> load ($ object, $ entitId, $ attributes). Якщо атрибути $ порожні, він викличе loadAllAttribute ($ об'єкт). Тож $ product-> load ('blah') завантажить усі відсутні атрибути, включаючи 'media_gallery' - Вільям Тран 19 листопада '14 о 4:45

Додайте потрібні значення до вже завантаженої колекції.

Очевидний простий підхід додавання необхідних даних до колекції вищого рівня в шарі / фільтрі, здавалося б, є найкращим підходом.

Я помітив спостерігача addInventoryDataToCollection () в Mage_CatalogInventory_Model_Observer, який звучить так, як би досягти такого, але додавання методу до користувальницьких модулів спостерігач не здається сумісним.

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

Результати:

Попередження: Неправильний аргумент подається для foreach () у /app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php у рядку 71


1
Хороший бумер питання
Аміт Бера

Відповіді:


4

Справжня проблема тут не передзавантажена, це точність. Отримати суму запасів для колекції продуктів порівняно просто:

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

Тепер з двома запитами ви маєте всю необхідну інформацію. Їх просто важко пов'язати один з одним, що можна виправити за допомогою асоціативного масиву 'product_id' => 'stock'та написання геттера. Також addProductsFilter можна оптимізувати:

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

Це дозволяє економити перевірку типу та клонування масиву.

Зараз проблема - блокувати кеш HTML. Сторінку категорії потрібно очистити, коли будь-який запас оновлюється на продукт, що міститься на ній. Наскільки я знаю, це не є стандартним, оскільки лише зміна статусу запасів очищує сторінку категорії, що містить товар (або, точніше, зміну видимості). Таким чином, вам потрібно буде дотримуватися принаймні, cataloginventory_stock_item_before_saveа може, і декількох інших, і очистити блок кеш html (і кеш FPC) для цієї категорії категорій.


Ваше останнє питання - це причина, коли ми звернулися за додатковим запитом на отримання даних про запаси. Якщо у вас є категорії з швидкодіючими продуктами, кеш буде витрачати більшу частину часу на очищення / недійсність. єдиний час, коли нам потрібно очистити кеш сторінки сторінки, якщо продукт видалено з цієї категорії. Немає жодного магічного рішення, з якого потрібно виробити найефективнішу реалізацію у кожному конкретному випадку. Якщо ви хочете, ви можете кешувати дані про запаси, так що лише вони повинні бути відновлені після флеш, а не всієї сторінки.
john-jh

Якщо ви реалізуєте дані запасів так само, як зараз, використовуючи дані JavaScript для оновлення DOM, ви можете записати це, використовуючи блок із власним кеш-ключем, і лише визнати недійсними дані про запас. Ось як я зробив би це для вашої ситуації і не використовував FPC на базі ESI, а той, який може відкривати перфораційні блоки в процесорі запитів. Не тільки для біта маршрутизатора, але і для того, щоб вимагати лише одного перекладача php на сторінку. Коли машина з будь-якої причини потрапляє під тиск, ваш інтерпретатор php - це найінтенсивніший ресурс процесора. Це починає звучати все більше, як приємний проект на вихідних ;-)
Мельвін

3
Цей тип речей робить роботу по-справжньому цікавою, де потрібно по-справжньому подумати про впровадження та потенційні проблеми та вузькі місця. Я заперечно бачу, звідки ви беретесь за допомогою єдиного процесу PHP, на даний момент це не є проблемою для нас, але я бачу, як це може вплинути на масштабність. Значення запасів, що відображаються на сторінці, не є важливою функціональністю, тому ми завантажуємо її як вторинний ресурс після завантаження, не впливаючи на користувача. Соромно, що запас у списку продуктів не є функцією за замовчуванням.
john-jh

1
Так, я зараз граю з ідеєю, щоб отримати це від Redis безпосередньо і вирізати php. Тут є кілька справді цікавих робіт Ічуна Чжана про відкритий Ресті .
Мельвін

2

Я бачу, що ви вже прийняли і, без сумніву, реалізували щось до цього часу, проте хотілося б зазначити, наскільки ви були поруч, addInventoryDataToCollection()але, схоже, ви неправильно написали конфігураційний файл або ми використовуємо зовсім інші версії magento. Моя копія CatalogInventory/etc/config.xmlмає інший метод, для якого вимагається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() називається в <sales_quote_item_collection_products_after_load>

Джерело для 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;
}

Ви можете або встановити прапор require_stock_itemsколекції перед завантаженням, можливо, це не так просто для блоку, що знаходиться за списком категорій, або ви можете зателефонувати Mage::getModel('cataloginventory/stock')->addItemsToProducts($productCollection)в колекцію вручну після його завантаження. addItemsToProducts()отримує всі StockItems для вас і додає їх до вашого 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

Ви взагалі використовуєте лак або FPC або плануєте в майбутньому?

Ми виявили, що кількість списку пробоїн / ESI-запитів, необхідних у списках продуктів, майже не варто мати кешування на місці, тому обрали інший підхід.

Ми реалізували рішення на одному веб-сайті, який використовує запит AJAX на користувальницький контролер для отримання даних про запаси продуктів, а javascript обробляє оновлення DOM. Додатковий запит на дані про запаси займає ~ 100 мс, що взагалі не впливає на загальний (видимий) час завантаження сторінки. У поєднанні з тим, що за допомогою грунтованого FPC знижується запит на сторінку до менш ніж 100 мс, у вас є один швидкий сайт із низькими накладними накладними витратами для відображення даних про запаси у списках товарів.

Все, що вам потрібно зробити, - це додати productId до кожної HTML-обгортки продуктів, щоб ваш JavaScript знав, які дані про запаси потрібно застосувати до кожного продукту.

Якщо ви подивитесь на додаткові методи кешування фактичних даних про запаси, ви можете опуститись набагато нижче 100 мс, не запускаючи Mage / натискаючи базу даних при кожному запиті.

Вибачте, якщо це не відповідає принципам того, що ви шукаєте, але ми виявили, що це найкращий підхід для масштабованості та продуктивності відповідно до наших вимог.


2
Розгорніть мавпу хаосу і подивіться, що станеться, коли ваше сховище кешу перестане. Питання конкретно зазначає не покладатися на кеш. Такі відповіді дають нам змогу переконати клієнта, що FPC не збирається виправити їх шаблон BuiltIn / MomsBasement набагато складніше.
Мельвін

Ви дійсно неправильно розумієте мою відповідь, якщо думаєте, що я пропоную FPC / Varnish для продуктивності та як рішення. Я заявив, що якщо вони використовують або розглядають FPC / Vanish, вони повинні вивчити такий підхід для зменшення пробивання дірок / запитів ESI. Ми отримуємо необхідні нам дані про запаси менше ніж 100 мс, не залишаючи кеш-пам'яті.
john-jh

І ви отримаєте ті самі дані за один і той же час за допомогою ESI. Ваш підхід до ESI просто принципово інший. Отже, без будь-якого кешу ви все одно можете отримати ті самі дані за менший час. Замість того, щоб пройти ще один маршрутизатор, щоб відправити назад JSON, ви пишете запас масиву в JavaScript, який оновлює DOM і т. Д. Пекло, помістіть його в JSON, якщо хочете, просто виріжте маршрутизатор.
Мельвін

1
Кешування буде використовуватися, але питання більше узгоджується з оптимізаціями продуктивності. Деякі відомості: magento.stackexchange.com/questions/13957/… (немає кешу / майже не існує) Дякую за відповідь, проте, вдячний за вклад!
B00MER

Не впевнений, чи існує спосіб «найкращої практики» зробити це в M2, але ваше рішення полягає в тому, як ми це завжди робили з Magento 1.x.
thoan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.