高效的集合调用,筛选和加载


15

现在,我正在重用foreach循环中嵌套的许多集合。是否可以将这些内容上移几级?目前,Im不得不一遍又一遍地重新加载具有51k +个实体的集合,这大大降低了运行速度。特别是kitinventory集合。

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

Answers:


9

您可以做一些事情。

  • 不通过引用传递,因此使用额外的内存可以传递对象,但是默认情况下不能通过引用传递数组。或者&在函数参数声明中添加一个function hello(array &$world)
  • 无效的检查,如果不存在,则立即返回。不要试图找到不存在的东西
  • 有时可读性可能很难
    • 添加一些文档(这样您就可以了解是否可以在几天,一月,几年内看到它)
    • 更智能的if语句以减少缩进
  • 函数应该仅具有一个目的,即更新库存或与更新相关,而不是两者兼有,因此甚至可以将某些函数切成更小的函数。尝试在您的脑海中创建这样的逻辑,然后从那里进行工作。
  • 看一眼 ->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 2015年

有一些小错误,但是比我自己的错误要好得多。
easymoden00b 2015年

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.
}

另一种选择是查看是否可以缓存此系统密集型过程的结果。也许您可以制作第二个数据库表来存储结果,并像magento索引那样刷新它。


希望分享如何以类似于上面的代码的方式调用此信息?另外,如果我生成产品集合并将其分配给类变量,我是否可以在整个代码中调用该集合?过滤(如代码中所示)会影响类变量吗,或者如果我将此过滤后的集合分配给另一个变量,它会保持不变吗?
easymoden00b 2015年

我添加了一个有关如何加入该领域的示例,但这只是一个小的优化。是:您可以将结果另存为类变量,然后在其他地方调用它们。但是:我想您每次想更改过滤条件时都必须实例化一个新模型(可能会
破坏

谢谢,这似乎是我所担心的。感谢您的优化示例。您还能想到其他吗?在上面的代码示例中,我将详细说明每个集合的用法。
easymoden00b 2015年

3

您不需要一遍又一遍地重新加载模型,Mage::getModel()只要知道一个资源模型是如何设置的,引用就足够了,很难说是否每次在内存中都对其进行了初始化,并且在这些循环中最终导致泄漏/用完了可能导致发生磁盘交换的内存。

一个集合来统治他们。重构功能以仅引用一个集合。这与标准SQL和过程编程相同。花更多的时间研究您的集合和资源模型,以了解如何从SQL中一次,两次获取所需的所有数据,然后有足够的内存以及引用数据进行循环以进行显示/操作。与将结果存储在缓存中相比,将结果存储在缓存中要容易得多,这对于MySQL的内置缓存机制也是如此,因为频繁的请求足够大会导致相同的磁盘交换问题。

保存I / O

Vinai有一个实施相同方法的很好的例子:

参考文献

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.