효율적인 컬렉션 호출, 필터링 및로드


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

답변:


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 2016 년

몇 가지 작은 오류가 있지만 내 오류보다 훨씬 더 좋습니다.
easymoden00b 2016 년

5

이것을 주석으로 추가하고 싶었지만 아직 충분한 담당자가 없습니다. Magento 코어 그리드가 제품 수량을 카탈로그 / 제품 컬렉션에 어떻게 결합시키는 지 살펴보십시오 : https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml / 블록 / 카탈로그 / 제품 /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

필드에 참여하는 방법에 대한 예제를 추가했지만 이는 작은 최적화 일뿐입니다. 예 : 결과를 클래스 변수로 저장하고 다른 곳에서 호출 할 수 있습니다. 그러나 필터링을 변경할 때마다 새로운 모델을 인스턴스화해야 한다고 생각 합니다 (목적을
잃을

감사합니다. 제가 두려워한 것처럼 보입니다. 이 최적화 예제에 감사드립니다. 당신이 생각할 수있는 다른 사람이 있습니까? 위의 코드 예제에서 각 컬렉션의 사용법에 대해 조금 설명하겠습니다.
easymoden00b

3

Mage::getModel()자원 모델이 어떻게 설정되어 있는지 기억하지 못하는 참조만으로 모델을 계속해서 다시로드 할 필요가 없습니다 . 메모리와 매번 루프에서 매번 다시 초기화되는 경우 말하기가 어렵습니다. 디스크 스왑이 발생할 수있는 메모리.

그들을 모두 다스리는 하나의 컬렉션. 하나의 컬렉션 만 참조하도록 함수를 리팩토링합니다. 이것은 표준 SQL 및 절차 적 프로그래밍과 동일합니다. SQL에서 필요한 모든 데이터를 한 번, 두 번 얻은 다음 충분한 메모리를 확보하고 표시 / 조작을 위해 반복 할 데이터를 참조하는 방법에 대한 콜렉션 및 자원 모델을 조사하는 데 약간의 시간이 더 걸립니다. 하나의 결과를 캐시에 저장하는 것이 더 쉽습니다. 이는 MySQL의 내장 캐싱 메커니즘에서도 마찬가지입니다. 요청이 충분히 많으면 동일한 디스크 스왑 문제가 발생할 수 있습니다.

I / O 저장

Vinai는 동일한 접근법을 구현하는 좋은 예를 가지고 있습니다.

참고 문헌 :

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.