効率的なコレクションの呼び出し、フィルタリング、読み込み


15

現在、foreachループ内にネストされている多くのコレクションを再利用しています。これらのことをいくつかのレベルに上げることは可能ですか?現在Imは、51k +エンティティを持つコレクションを何度もリロードすることを余儀なくされており、これにより物事が非常に遅くなります。具体的には、キットインベントリコレクション。

<?php
class Codespace_Module_Helper_Item extends other_one{

function functionOne($collection){
    ...
    $data = $collection->getData();
    foreach($data as $item){
        $this->_functionTwo($item);
    }
    ...
}

function _functionTwo($item){
    $model = Mage::getModel('catalog/product');
    $id = $model->getIdBySku($item['sku']);
    $inventoryStatus = Mage::getResourceSingleton('catalog/product')->getAttributeRawValue($id, 'product_inventory_status', 1);
    $invStatus = $model->getResource()->getAttribute('product_inventory_status')->getSource()->getOptionText($inventoryStatus);
    if ($invStatus && $id) {
        if ($invStatus !== 'Z') {
            $stockItem = Mage::getModel('cataloginventory/stock_item');
            $stockItem->setData(array());
            $stockItem->loadByProduct($id);
            if ($stockItem->getQty() != $item['quantity']) {
                $stockItem->setQty(item['quantity']);
                $stockItem->save();
                $this->functionThree($item['sku']);
            }
        }
    }
}

function functionThree($sku){
    $collectionOfKits = Mage::getModel('kitinventory/kitinventory')->getCollection()->addFieldToFilter('related_sku',$sku);
    if($collectionOfKits->getSize()){
        foreach($collectionOfKits as $kit){
            $kitSku = $kit->getSku();
            $kitCollection = Mage::getModel('kitinventory/kitinventory')->getCollection()->addFieldToFilter('kit_sku',$kitSku)->setOrder('related_sku','ASC');
            ...
            foreach($kitCollection as $component){
                $componentSkus[] = $component->getRelatedSku();
                $componentRequiredQuantity[] = $component->getRequiredQuantity();
            }
            $componentProductCollection = Mage::getModel('catalog/product')->getCollection();
            $componentProductCollection->joinField('qty',
                'cataloginventory/stock_item',
                'qty',
                'product_id=entity_id',
                '{{table}}.stock_id=1',
                'left');
            $componentProductCollection->addAttributeToFilter('sku', array('in' => $componentSkus));
            foreach($componentProductCollection as $component){
                $quantity = $component->getQty();
                ...
            }
            $kitId= Mage::getModel('catalog/product')->getIdBySku($kitSku)
            $kitStockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($kitId);
            $this->functionFour($kitStockItem,$kitSku,$amountOfKitsPossible);
        }
    }
}

function functionFour($kitStockItem,$kitSku,$amountOfKitsPossible){
    ...
    $kitStockItem->setQty($quantity);
    $kitStockItem->save();
    ...
}

編集:これは私が思いついた現在の機能です、私はまだこれらのコレクションを処理するためのより良い方法があると思います。


どのようなコレクションが渡されfunctionOne($collection)ますか?サイズ/アイテムの数はどの順序ですか?SKUを取得するためにループする必要がありますか?
7ochem

@ 7ochemは、在庫管理システムから取得した新しい在庫データから作成されたカスタムコレクションです。アイテムの名前、手持ちのアイテムの数量、アイテムのSKUが含まれています。潜在的に60k +要素を含むことができます。
-easymoden00b

回答:


9

作業できることがいくつかあります。

  • 参照渡しではないため、追加のメモリを使用してオブジェクトを渡すことができますが、デフォルトでは配列を参照渡しできません。または&、関数パラメータ宣言にfunction hello(array &$world)
  • 何かが存在しない場合、無効なチェックはすぐに戻ります。そこにないものを見つけようとしないでください
  • 読みやすさがときどき難しい
    • いくつかのドキュメントを追加します(数日、数ヶ月、数年後に見れば理解できます)
    • ifインデントを減らすためのよりスマートなステートメント
  • 関数の目的は1つのみで、在庫の更新または関連の更新の両方ではなく、一部の関数をより小さな関数にカットすることもできます。あなたの心の中でそのようなロジックを作成し、そこからやり直してみてください。
  • いくつかのオブジェクトの基礎となるデータをクリアするために->cleanModelCache()->clearInstance()から見て、Mage_Core_Model_Model_Abstract物事をスピードアップできます。
  • すでに言われた他のすべての粗いもの。

現在のコードにいくつかのインラインの推奨事項を含むコードの更新バージョンを追加しました。少し先に進むことができますが、現時点では追加されません。

機能1:目的はコレクションを歩くこと

    /**
     * Walk collection
     * 
     * @param Mage_Core_Model_Resource_Db_Collection_Abstract $collection
     * @return void
     */
    public function functionOne($collection)
    {
        // ...

        // Walk collection, create references instead of passing array data
        foreach ($collection as $item) {

            // Update stock for product
            if (!$this->_functionTwo($item)) {
                // Not updated, continue next
                continue;
            }

            // Update related products
            $this->_functionThree($item); // Use same object again, no extra memory is used
        }

        // ...
    }

機能2:変更された場合、目的は在庫を更新する

    /**
     * Update stock item if changed, returns true if updated
     * 
     * @param Mage_Core_Model_Model_Abstract $item
     * @return bool
     */
    function _functionTwo($item)
    {
        $model = Mage::getModel('catalog/product');
        /** @var $model Mage_Catalog_Model_Product */

        $id = $model->getIdBySku($item->getData('sku'));

        if (!$id) {
            // no id found, so stop looking nothing up
            return false;
        }

        // Get option value for store 1
        $inventoryStatus = $model->getResource()
                ->getAttributeRawValue($id, 'product_inventory_status', 1);

        if (!$inventoryStatus) {
            // No need for another lookup in db, because the status isn't set
            return false;
        }

        $invStatus = $model->getResource()
                ->getAttribute('product_inventory_status')
                ->setStoreId(0) // Get admin value
                ->getSource()
                ->getOptionText($inventoryStatus);

        if (!$invStatus) {
            // No need for another lookup in db, because the status has no text
            return false;
        }

        if ($invStatus === 'Z') {
            // Inventory status to not change something
            return false;
        }

        $stockItem = Mage::getModel('cataloginventory/stock_item');
        /** @var $stockItem Mage_CatalogInventory_Model_Stock_Item */

        // $stockItem->setData(array()); // unneeded piece of code
        $stockItem->loadByProduct($id);

        if ($stockItem->getQty() == $item->getData('quantity')) {
            // Valid stock
            return false;
        }

        // Update stock
        $stockItem->setQty($item->getData('quantity'));
        $stockItem->save();

        // End function and call function three separately, does something else
        return true;
    }

機能3:関連する在庫品目を更新する目的

    /**
     * Update related stock items, return false if no related items are found
     * 
     * @param Mage_Core_Model_Model_Abstract $item
     * @return bool
     */
    function functionThree($item)
    {

        $collectionOfKits = Mage::getModel('kitinventory/kitinventory')
                ->getCollection()
                ->addFieldToFilter('related_sku', $item->getData('sku')); // Check if your indexes are set on these columns

        if (!$collectionOfKits->getSize()) {
            // Nothing found to relate to
            return false;
        }

        $connection = Mage::getSingleton('core/resource')
                ->getConnection('core_write');

        // Walk kits
        foreach ($collectionOfKits as $kit) {

            // getData is slightly faster then getSku(unless you've implemented it in your model)
            // getSku -> __call('getSku') -> get -> lowercase('sku') -> getData('sku') | note, Magento has some internal caching in this 
            $kitSku = $kit->getData('sku');

            $kitCollection = Mage::getModel('kitinventory/kitinventory')
                    ->getCollection()
                    ->addFieldToFilter('kit_sku', $kitSku)
                    ->setOrder('related_sku', 'ASC');

            // Use just a fetchAll to create a fast db query
            $select = $kitCollection->getSelect();

            $select->reset(Zend_Db_Select::COLUMNS)
                    ->distinct()
                    ->columns('related_sku')
                    ->columns('required_quantity');

            // Fetch component sku
            $componentSkus = $connection->fetchAll($select, 0);

            // Fetch required quantity
            $componentRequiredQuantity = $connection->fetchCol($select, 1);

            // ...

            $componentProductCollection = Mage::getModel('catalog/product')
                    ->getCollection()
                    ->joinField('qty',
                    'cataloginventory/stock_item',
                    'qty',
                    'product_id = entity_id',
                    '{{table}}.stock_id = 1',
                    'left');
            $componentProductCollection->addAttributeToFilter('sku', array('in' => $componentSkus));

            // Next line will invoke a load on the product collection
            foreach ($componentProductCollection as $component) {
                $quantity = $component->getQty();

                // ...

            }
            // You could choose to do a fetchAll here instead to get just the data you need
            $connection = $componentProductCollection->getConnection();

            foreach ($connection->fetchAll($componentProductCollection->getSelect()) as $row) {
                // Will have a array here
                $quantity = $row['quantity'];

                // ... -- do not not which funky magic happens here
            }


            $kitId = Mage::getModel('catalog/product')
                    ->getIdBySku($kitSku);
            if (!$kitId) {
                // No id
                continue;
            }

            // You could also take a look if you can sum the stock and do a single update instead
            $kitStockItem = Mage::getModel('cataloginventory/stock_item')
                    ->loadByProduct($kitId);
            $this->functionFour($kitStockItem, $kitSku, $amountOfKitsPossible);

            // Or something like this, update single field
            $connection->update($kitStockItem->getResource()->getMainTable(), array('qty' => $quantity), 'item_id = ' . $kitStockItem->getId());
        }

        return true;
    }

機能4:いくつかの幸運な(または不運な)推測を行う必要がありました。今のところは役に立たない機能であり、機能3のように追加できます。

    /**
     * Save stock item if changed and something else, rather not say ;-)
     * 
     * @param Mage_Catalog_Inventory_Model_Stock_Item $kitStockItem
     * @param string $kitSku
     * @param int $amountOfKitsPossible Guessed it
     */
    function functionFour($kitStockItem, $kitSku, $amountOfKitsPossible)
    {

        // ...

        // Do not know the rest of the code, so I wouldn't know which I could optimize here
        // If it isn't to serious, you could look at a single query and not hitting extra functions

        // Check if changed
        if ($quantity !=$kitStockItem->getData('qty')) {
            $kitStockItem->setQty($quantity);
            $kitStockItem->save();
        }        

        // ...

    }
}

あなたは男です。私はこれがうまくいくことを比較的確信しており、これが処理時間の明確な改善を示している場合、コレクションを操作するための私の参考になるかもしれません!
easymoden00b

いくつかの小さなエラーがありますが、それは私自身のものよりはるかに優れています。
-easymoden00b

5

これをコメントとして追加したかったのですが、まだ十分な担当者がいません。Magentoのコアグリッドはこちらカタログ/製品回収への製品の数量に参加する方法を見てみましょう:https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml /Block/Catalog/Product/Grid.php#L65

テーブルを結合して数量を取得する場合、ループでこれを呼び出す必要はありません。 Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();

$productCollection = Mage::getModel('catalog/product')->getCollection();
$productCollection->joinField('qty',
    'cataloginventory/stock_item',
    'qty',
    'product_id=entity_id',
    '{{table}}.stock_id=1',
    'left');
$productCollection->addAttributeToFilter('sku',array('in' => $relatedSkus));
foreach($productCollection as $product){
    $quantity = $product->getQty();
    ...// now you have your qty without having to load the product model.
}

もう1つの方法は、このシステム集中型プロセスの結果をキャッシュできるかどうかを確認することです。結果を保存するために2番目のデータベーステーブルを作成し、magentoインデックスのように更新することもできます。


上記のコードと同様の方法でこの情報を呼び出す方法を共有してください。また、製品コレクションを生成してクラス変数に割り当てると、コード全体でそのコレクションを呼び出すことができますか?(コードに示されているように)フィルタリングはクラス変数に影響しますか、またはこのフィルタリングされたコレクションを別の変数に割り当てても変更されませんか?
easymoden00b

フィールドに参加する方法の例を追加しましたが、これはほんの小さな最適化です。はい:結果をクラス変数として保存し、他の場所で呼び出すことができます。しかし:私が思うに、あなたが新しいモデルにあなたがフィルタリングを変更するたびに、インスタンス化する必要があります(目的を倒すことがあります。)
エリック・シーストランド

私が恐れているとして、それが表示され、ありがとうございます。最適化のこの例をありがとう。他のものは、あなたが考えることができますか?上記のコード例では、各コレクションの使用について少し説明します。
easymoden00b

3

モデルを何度もリロードする必要はありませんMage::getModel()。リソースモデルがどのように設定されているかを知らなくても参照は十分です。メモリ内で毎回再初期化され、ループ内でリーク/実行が終了するかどうかはわかりませんディスクスワップを引き起こす可能性のあるメモリ。

すべてを支配する1つのコレクション。1つのコレクションのみを参照するように関数をリファクタリングします。これは、標準SQLおよび手続き型プログラミングと同じです。SQLから必要なすべてのデータを1回、場合によっては2回取得して、十分なメモリを確保し、表示/操作のためにループオーバーするためにデータを参照する方法について、コレクションとリソースモデルの調査にもう少し時間をかけます。ために同じケースで、多くの対キャッシュ内の1件の結果を格納することも簡単ですMySQLのビルトイン同じディスクスワップ問題が発生します十分な大きさをしている、頻繁な要求として、また、メカニズムをキャッシュします。

I / Oを保存する

Vinaiは同じアプローチを実装する良い例があります。

参考文献

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.