Pemanggilan, Penyaringan, dan Pemuatan Koleksi yang Efisien

15

Saat ini saya menggunakan kembali banyak koleksi yang bersarang dalam loop foreach. Apakah mungkin untuk memindahkan beberapa tingkat ini ke atas? Saat ini saya terpaksa memuat ulang koleksi yang memiliki entitas 51k + berulang-ulang yang sangat memperlambat segalanya. Khususnya koleksi koleksi kit.

<?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: ini adalah fungsi saat ini yang saya buat, saya masih berpikir ada cara yang lebih baik untuk menangani koleksi ini.

easymoden00b
sumber
Koleksi seperti apa yang diteruskan functionOne($collection)? Dalam urutan apa ukuran / jumlah barang itu? Apakah perlu untuk mengulanginya untuk mendapatkan SKU?
7ochem
@ 7ochem Ini adalah koleksi khusus yang dibangun dari data inventaris baru yang kami dapatkan dari sistem manajemen inventaris kami. itu berisi nama item, jumlah item di tangan, dan sku item. Ini berpotensi mengandung elemen 60k +.
easymoden00b

Jawaban:

9

Ada beberapa hal yang bisa Anda kerjakan;

  • tidak lewat dengan referensi, jadi menggunakan memori tambahan, Anda dapat melewati objek, tetapi array tidak dapat dilewatkan oleh referensi secara default. Atau tambahkan &dalam deklarasi parameter fungsi sepertifunction hello(array &$world)
  • cek tidak valid, jika sesuatu tidak ada segera kembali. Jangan mencoba menemukan sesuatu yang tidak ada di sana
  • keterbacaan terkadang sulit
    • tambahkan beberapa dokumen (sehingga Anda dapat mengerti jika Anda melihatnya dalam beberapa hari, monhts, tahun)
    • ifpernyataan yang lebih cerdas untuk mengurangi indentasi
  • fungsi seharusnya hanya memiliki satu tujuan, memperbarui stok atau pembaruan yang terkait, tidak keduanya, jadi mungkin bahkan memotong beberapa fungsi bahkan dalam fungsi yang lebih kecil. Cobalah untuk membuat logika seperti itu di pikiran Anda, dan ulang dari sana.
  • Lihatlah ->cleanModelCache()->clearInstance()dari Mage_Core_Model_Model_Abstractmenghapus data yang mendasari untuk beberapa objek, dapat mempercepat hal-hal.
  • dari kasar semua hal lain yang telah dikatakan.

Menambahkan versi terbaru dari kode Anda dengan beberapa rekomendasi sebaris pada kode Anda saat ini, saya bisa melanjutkan sedikit, tetapi saat ini tidak akan menambahkan lebih banyak untuk itu.

Fungsi 1: Tujuan berjalan koleksi

    /**
     * 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
        }

        // ...
    }

Fungsi 2: Tujuan memperbarui stok jika diubah

    /**
     * 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;
    }

Fungsi 3: Tujuan memperbarui item stok terkait

    /**
     * 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;
    }

Fungsi 4: Harus membuat tebakan keberuntungan (atau sial), karena sekarang ini merupakan fungsi yang tidak berguna, dapat ditambahkan seperti pada Fungsi 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();
        }        

        // ...

    }
}
Jeroen
sumber
Kamulah orangnya. Saya relatif yakin ini akan berhasil, dan jika ini menunjukkan peningkatan yang berbeda dalam waktu pemrosesan, mungkin referensi saya untuk memanipulasi koleksi!
easymoden00b
Beberapa kesalahan kecil, tapi jauh lebih baik daripada saya sendiri.
easymoden00b
5

Saya ingin menambahkan ini sebagai komentar tetapi saya belum memiliki perwakilan yang cukup. Lihatlah bagaimana grid inti Magento bergabung dengan jumlah produk ke katalog / koleksi produk di sini: https://github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/core/Mage/Adminhtml /Block/Catalog/Product/Grid.php#L65

Jika Anda bergabung dengan tabel untuk mendapatkan qty, Anda tidak harus menyebutnya dalam satu lingkaran: 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.
}

Alternatif lain adalah untuk melihat apakah Anda dapat men-cache hasil dari proses intensif sistem ini. Mungkin Anda bisa membuat tabel database kedua untuk menyimpan hasilnya, dan menyegarkannya seperti indeks magento.

Eric Seastrand
sumber
mau berbagi bagaimana saya bisa memanggil informasi ini dengan cara yang mirip dengan kode di atas? Juga, jika saya menghasilkan koleksi produk dan menugaskannya ke variabel kelas apakah saya dapat memanggil koleksi itu di seluruh kode? Akankah pemfilteran (seperti yang ditunjukkan dalam kode) mempengaruhi variabel kelas atau akan tetap tidak berubah jika saya menetapkan koleksi yang difilter ini ke variabel lain?
easymoden00b
Saya menambahkan contoh cara bergabung dengan bidang ini, tetapi ini hanya optimasi kecil. Ya: Anda dapat menyimpan hasilnya sebagai variabel kelas dan memanggilnya di tempat lain. Tapi: Saya pikir Anda harus instantiate model baru setiap kali Anda ingin mengubah pemfilteran (mungkin mengalahkan tujuan.)
Eric Seastrand
Terima kasih, ini muncul seperti yang saya takutkan. Terima kasih atas contoh pengoptimalan ini. Adakah yang bisa Anda pikirkan? Saya akan menjelaskan sedikit tentang penggunaan setiap koleksi dalam contoh kode di atas.
easymoden00b
3

Anda tidak perlu memuat ulang model berulang-ulang, Mage::getModel()dengan referensi sudah cukup tanpa mengetahui bagaimana model sumber daya Anda diatur, sulit untuk mengatakan apakah itu diinisialisasi ulang setiap kali dalam memori dan dalam loop itu Anda akhirnya bocor / kehabisan memori menyebabkan disk swap terjadi mungkin.

Satu koleksi untuk mengatur semuanya. Refactoring fungsi hanya untuk referensi satu koleksi. Ini sama dengan SQL standar dan pemrograman prosedural. Luangkan waktu lebih sedikit untuk menyelidiki koleksi dan model sumber daya Anda tentang bagaimana Anda bisa mendapatkan semua data yang Anda butuhkan dari SQL satu kali, mungkin dua kali dan kemudian memiliki memori yang cukup di tempat serta referensi data untuk perulangan untuk tampilan / manipulasi. Juga lebih mudah untuk menyimpan satu hasil dalam cache vs banyak, ini adalah kasus yang sama untuk mekanisme cache bawaan MySQL juga, karena permintaan yang sering cukup besar akan menyebabkan masalah disk swap yang sama.

Simpan I / O

Vinai memiliki contoh yang baik untuk menerapkan pendekatan yang sama:

Referensi :

B00MER
sumber