性能:在所有产品类型的产品列表list.phtml上添加库存库存水平


12

TL; DR,要求在类别产品列表页面上显示库存的库存水平,同时考虑到性能符合Magento框架的情况,同时减少查询/存储量。


在阅读Vinai Kopp关于预加载以实现可伸缩性的文章之后。

为了降低性能,在类别产品列表页面(list.phtml)上包含库存库存水平且没有额外的查询/负载的最佳方法是什么?

我知道一些方法:

afterLoad()似乎可以很好地与media_gallery包含项一起使用,而无需其他查询,但是我没有成功实现与库存相同的方法。

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

指示SQLproduct_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_Abstract-> load($ object,$ entityId,$ attributes)中。如果$ attributes为空,它将调用loadAllAttribute($ object)。因此$ product-> load('blah')将加载所有缺少的属性,包括'media_gallery'– William Tran 2014年11月19日,4:45

将所需的值添加到已加载的集合中。

将所需数据添加到图层/过滤器的顶级生产集合中的简单方法显然是最好的方法。

我确实注意到在Mage_CatalogInventory_Model_Observer中有一个观察者addInventoryDataToCollection(),听起来好像可以实现,但是将方法添加到自定义模块观察者中似乎并不兼容。

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

结果是:

警告:第71行的/app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.php中为foreach()提供的参数无效


1
好问题的婴儿潮一代
阿米特·贝拉

Answers:


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 2015年

如果使用与现在相同的方法来实现库存数据,则使用JavaScript数据来更新DOM,则可以使用具有自己的缓存键的块来编写该数据,而只会使库存数据无效。这就是我针对您的情况采取的方式,而不使用基于ESI的FPC,而是可以在请求处理器中打孔的模块。不仅用于路由器,而且每个页面仅需要一个php解释器。当机器由于任何原因受到压力时,您的php解释器是Cpu上最耗费资源的资源。这听起来越来越像一个不错的周末项目;-)
Melvyn 2015年

3
这是使工作变得非常有趣的东西,您必须真正考虑实施以及潜在的问题和瓶颈。我挑衅地看到您来自单个PHP流程的源头,这对我们来说不是问题,但可以看到它在您扩展时会产生怎样的影响。页面上显示的库存值不是关键功能,因此我们在加载后将其作为辅助资源进行加载,而不会影响用户。这是产品清单上的可耻库存,不是默认功能。
john-jh 2015年

1
是的,我现在正在考虑从Redis直接获取并切出php的想法。张春在《开放的Resty》上有一些非常有趣的工作。
梅尔文2015年

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()为您获取所有StockItem并将其附加到您的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

您是完全使用Varnish还是FPC,还是计划将来使用?

我们发现,由于产品清单上需要打孔/ ESI的数量众多,因此几乎不值得进行缓存,因此选择了其他方法。

我们在一个网站上实施了一种解决方案,该解决方案使用对自定义控制器的AJAX请求来检索产品的库存数据,而javascript处理DOM更新。额外的库存数据请求大约需要100毫秒,这完全不会影响整个(可见)页面加载时间。再加上准备好的FPC将页面请求降低到100毫秒以下,您将拥有一个快速站点,其性能开销较低,无法在产品列表上显示库存数据。

您需要做的明智的模板就是将productId添加到每个产品包装HTML中,以便您的javascript知道要应用于每个产品的库存数据。

如果您查看用于实际库存数据的其他缓存技术,则可以不必在每个请求上初始化Mage / hit数据库,而将其降至100ms以下。

很抱歉,如果这与您要查找的内容不符,但我们发现它是根据我们的要求实现可伸缩性和性能的最佳方法。


2
部署混乱的猴子,看看当缓存存储崩溃时会发生什么。这个问题专门提到不依赖缓存。这样的回答使我们的工作说服客户FPC不会更加困难地修复BuiltIn / MomsBasement模板。
梅尔文

如果您认为我建议FPC / Varnish提供性能和解决方案,那么您真的会误解我的答案。我说过,如果他们正在使用或考虑使用FPC / Vanish,则应研究此方法以减少打孔/ ESI要求。我们可以在不到100ms的时间内获得所需的库存数据,而不会浪费缓存。
john-jh 2015年

使用ESI,您将在同一时间获得相同的数据。您使用ESI的方法根本不同。因此,没有任何缓存,您仍然可以在更少的时间内获得相同的数据。您无需通过另一个路由器来发送回JSON,而是使用JavaScript编写了一个库存数组来更新DOM等。该死,如果需要,可以将其放入JSON中,只需切断路由器即可。
Melvyn 2015年

1
将会使用缓存,但问题更多是性能优化。一些背景:magento.stackexchange.com/questions/13957/…(没有缓存/几乎没有)感谢您的答复,感谢您的输入!
B00MER

不确定在M2中是否有“最佳实践”方法,但是您的解决方案是自Magento 1.x以来我们一直都这样做。
thdoan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.