Magento – Add Inventory Stock Levels on Product Listings for Performance

categoryinventorylist.phtmlperformance

TL;DR, The requirement is having a stock level of inventory be displayed on the category product listing page with as little additional queries/memory with performance in mind that adheres to Magento's framework.


After reading Vinai Kopp's article on preloading for scalability.

What is the best way to include the inventory stock levels on the category product listing pages (list.phtml) with as few additional queries/loads for performance sakes?

I am aware of a few approaches:

afterLoad() seems to work well with media_gallery inclusion without additional queries, however I've not been successful implementing the same approach with inventory.

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

Direct SQL to gather the required data in parallel to the collection with a product_id key for instance. But looking for more of a means through the framework.

Currently I am simply loading the stock_item object via:

$_product->load('stock_item')->getTotalQty(); Which works, but I notice the addition of more queries to get the inventory stock totals of all products in the collection.

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

strangely, this works. The magic happens in Mage_Eav_Model_Entity_Abstract->load($object, $entityId, $attributes). If $attributes is empty it will call loadAllAttribute($object). So $product->load('blah') will load all missing attributes, including 'media_gallery' – William Tran Nov 19 '14 at 4:45

Add needed values to the already loaded collection.

The obvious simple approach of adding the needed data to the top level production collection in the layer/filter, would seem to be the best approach.

I did notice an observer addInventoryDataToCollection() in Mage_CatalogInventory_Model_Observer that sounds like it would achieve such, but adding the method to a custom modules observer doesn't seem to be compatible.

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

Which results in:

Warning: Invalid argument supplied for foreach() in
/app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php
on line 71

Best Answer

The real issue here isn't preloading, it's accuracy. It's relatively easy to obtain the stock amount for a collection of products:

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

Now with two queries, you have all the info you need. They're just hard to relate to one another, which can be fixed by using an associative array 'product_id' => 'stock' and writing a getter. Also, addProductsFilter can be optimized:

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

This saves you the type check and array cloning.

The problem now is Block HTML cache. This category page needs to be purged when any stock is updated on a product contained in it. As far as I know, this isn't standard, since only a stock status change purges a category page containing a product (or more accurately, a visibility change). So you'll need to observe at least cataloginventory_stock_item_before_save and maybe a few others and purge block html cache (and FPC cache) for that category page.

Related Topic