パフォーマンス:すべての製品タイプの製品リスト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);

product_idたとえば、キーを使用してコレクションと並行して必要なデータを収集するようにSQLに指示します。しかし、フレームワークを通じてより多くの手段を探しています。

現在、私は単に次の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 Nov 19 '14 at 14: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>

結果:

警告:71行目の/app/code/core/Mage/CatalogInventory/Model/Resource/Stock/Item/Collection.phpのforeach()に無効な引数が指定されました


1
良い質問ブーム
アミットベラ

回答:


4

ここでの本当の問題はプリロードではなく、正確さです。製品のコレクションの在庫量を取得するのは比較的簡単です。

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

これで、2つのクエリを使用して、必要な情報をすべて入手できました。それらは相互に関連付けるのが難しいだけで、連想配列を使用し'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を更新する現在と同じ方法でストックデータを実装する場合、独自のキャッシュキーを持つブロックを使用してそれを記述し、ストックデータのみを無効にすることができます。これは私があなたの状況でそれをする方法であり、ESIベースのFPCを使用せず、リクエストプロセッサにパンチブロックを開けることができるものです。ルータービットだけでなく、ページごとに1つのphpインタープリターのみを必要とするためにも。何らかの理由でマシンに圧力がかかった場合、PHPインタープリターは最もCPUを集中的に使用するリソースです。これはますます素敵な週末プロジェクトのように聞こえ始めています;
メルビン

3
これは、実装と潜在的な問題とボトルネックについて本当に考えなければならない場合に、仕事を非常に興味深いものにするタイプのものです。単一のPHPプロセスであなたがどこから来たのか、私は反論しますが、現時点ではこれは問題ではありませんが、スケーリングするとどのように影響するかを見ることができます。ページに表示される株価は重要な機能ではないため、ユーザーに影響を与えずに読み込み後にセカンダリリソースとして読み込みます。これはデフォルトの機能ではなく、製品リストの恥ずべき在庫です。
john-jh

1
ええ、私は今これをRedisから直接取得してphpを削除するというアイデアで遊んでいます。ここでは、Yichun Zhangによるopen Restyの興味深い作品がいくつかあります。
メルビン

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

ワニスまたはFPCを使用していますか、または今後使用する予定ですか?

製品リストに必要なホールパンチ/ ESIリクエストの量では、キャッシュを配置する価値はほとんどないため、別のアプローチを選択しました。

カスタムコントローラーへのAJAXリクエストを使用して製品の在庫データを取得し、JavaScriptがDOM更新を処理するWebサイトにソリューションを実装しました。在庫データの追加リクエストには約100ミリ秒かかりますが、全体の(表示される)ページの読み込み時間にはまったく影響しません。プライミングされたFPCがページリクエストを100ミリ秒未満に落とすと、製品リストに在庫データを表示するためのパフォーマンスオーバーヘッドの低い1つの高速サイトができます。

テンプレートごとに行う必要があるのは、各製品のラッパーhtmlにproductIdを追加して、javascriptが各製品に適用するストックデータを認識できるようにすることだけです。

実際のストックデータに対する追加のキャッシュテクニックを見ると、各リクエストでデータベースを初期化/ヒットする必要がないため、100ミリ秒を大幅に下回る可能性があります。

これがあなたが探しているものの線に沿っていない場合は申し訳ありませんが、私たちの要件に対するスケーラビリティとパフォーマンスのための最良のアプローチであることがわかりました。


2
展開カオス猿とあなたのキャッシュストレージが倒れたときに何が起こるか参照してください。この質問では、キャッシュに依存しないことが具体的に言及されています。このような答えが、FPCがBuiltIn / MomsBasementテンプレートをそれほど難しくしないことをクライアントに納得させる私たちの仕事です。
メルビン

パフォーマンスとソリューションとしてFPC /ワニスを提案していると思うなら、あなたは私の答えを本当に誤解しています。FPC / Vanishを使用または検討している場合、ホールパンチ/ ESIリクエストを減らすためにこのアプローチを調査する必要があると述べました。キャッシュを残すことなく、100ms未満で必要な株価データを取得しています。
ジョンjh

また、ESIを使用すると、同じデータを同時に取得できます。ESIでのアプローチは根本的に異なります。そのため、キャッシュがなくても同じデータを短時間で取得できます。さらに別のルーターを経由してJSONを返送する代わりに、DOMなどを更新するストック配列をJavaScriptで記述します。地獄、必要に応じてJSONに入れて、ルーターを切り取ります。
メルビン

1
キャッシングが使用される予定ですが、問題はパフォーマンスの最適化に沿ったものです。背景:magento.stackexchange.com/questions/13957/…(キャッシュなし/ほとんどありません)ただし、返信いただきありがとうございます。
B00MER

M2でこれを行うための「ベストプラクティス」の方法があるかどうかはわかりませんが、Magento 1.x以降、あなたのソリューションはこれを常に行ってきた方法です。
thdoan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.