Ефективна колекція дзвінків, фільтрування та завантаження


15

Зараз я повторно використовую безліч колекцій, які вкладені в петлі foreach. Чи можливо перемістити ці речі на кілька рівнів? В даний час я змушений перезавантажувати колекції, які містять 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();
    ...
}

EDIT: це поточний функціонал, який я придумав, я все ще думаю, що є кращий спосіб поводження з цими колекціями.


Яка колекція передається functionOne($collection)? У якому порядку буде розмір / кількість предметів? Чи потрібно провести цикл, щоб отримати SKU?
оч.

@ 7ochem Це власна колекція, побудована з нових даних про запаси, які ми отримуємо з нашої системи управління запасами. він містить назву предмета, кількість предмета, що знаходиться в руці, і номер предмета. Він потенційно може містити 60 к + елементів.
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

Кілька невеликих помилок, але це набагато краще побудовано, ніж моя власна.
easymoden00b

5

Я хотів додати це як коментар, але у мене ще недостатньо представників. Подивіться, як основні сітки Magento приєднуються до продукту qty до каталогу / колекції продуктів тут: https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml /Block/Catalog/Product/Grid.php#L65

Якщо ви приєднаєтесь до таблиці, щоб отримати qty, вам не потрібно називати це в циклі: 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 має хороший приклад здійснення того ж підходу:

Список літератури :

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.