简体   繁体   中英

Magento: serialization error on caching Collection

I wrote an extension in which a base abstract class contains a function that will extract a product collection:

$cache = Mage::app()->getCache();
        if(!$cache->load('itserv_feed_collection')) {
            $_productCollection = Mage::getModel('catalog/product')->getCollection();
            $_productCollection->addAttributeToSelect('*');                        
            $_productCollection->addAttributeToSelect('stock_status');        
            $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/produttore'));
            $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/ean'));
            $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/mpn'));      
            $_productCollection->addAttributeToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE);                                                                       
            $cache->save(serialize($_productCollection), "itserv_feed_collection", array("itserv_feed_collection"), 120);                
        }
        else {                
            $_productCollection = unserialize($cache->load('itserv_feed_collection'));
        }
        return $_productCollection;

Each of the child classes extending from this class will use the same collection in the same runtime stack. I want to save this collection within the cache (as you can see looking at the code), so since the second time a child class will use it, the script will not need to load it again.

The problem is that it is impossible to use cache, because cache needs a serialized Collection and, in this case, i cannot do it because the collection contains the Mage_Core_Model_Config_Element that can't be serialized (it triggers the famous error "Serialization of 'Mage_Core_Model_Config_Element' is not allowed").

I tried different solutions, even json_encode/json_decode instead of serialize/unserialize, but i can't solve the problem.

Do you have some solution? Thanks!

this may be a bit late but hopefully will put someone on the right track in the future.

As you noticed, you cannot serialize the collection, but you can get the returned items as an array and cache those. This is step 1.

Then, depending on how you use the collection (for example on a product list) and count() is called, Magento will try to load the collection again, resulting in an error since the IsLoaded flag will be false, but you will have the items.

To solve this, you need to set the flag after adding the items to the collection. In order to do so, you should rewrite the product resource collection (to get access to this flag). So codewise:

The code is based on the assumption you want to use the collections on the product list page and does not support sorting / filtering etc, since only the items are cached.

Step 1: Add the rewrites for the product list and product resource collection:

<config>
    <global>
        <blocks>
            <projectName>
                <class>CompanyName_ProjectName_Block</class>
            </projectName>
            <catalog>
                <rewrite>
                    <product_list>CompanyName_ProjectName_Block_Catalog_Product_List</product_list>
                </rewrite>
            </catalog>
        </blocks>
        <models>
            <projectName>
                <class>CompanyName_ProjectName_Model</class>
            </projectName>
            <catalog_resource>
                <rewrite>
                    <product_collection>CompanyName_ProjectName_Model_Resource_Product_Collection</product_collection>
                </rewrite>
            </catalog_resource>
        </models>
    </global>
<config>

Step 2: Create the Resource class

<?php
class CompanyName_ProjectName_Model_Resource_Product_Collection extends Mage_Catalog_Model_Resource_Product_Collection
{
    public function setIsLoaded($flag)
    {
        $this->_setIsLoaded($flag);
    }
}

Step 3 : Create the product list class that does the actual caching loading

<?php
class CompanyName_ProjectName_Block_Catalog_Product_List extends Mage_Catalog_Block_Product_List
{   
    public function getCachedLoadedProductCollection()
    {
        $cache = Mage::app()->getCache();
        $cacheKey = 'category_products_' .  Mage::app()->getStore()->getId() . '_' . $this->getCategoryId() . '_' . date('d-m-Y');
        $cachedProductCollection = $cache->load($cacheKey);

        if (!$cachedProductCollection) {
            $loadedProductCollection = parent::getLoadedProductCollection();
            $cachedProductCollection = serialize($loadedProductCollection->exportToArray());
            $cache->save($cachedProductCollection, $cacheKey, array('ProjectName'), 3600);
        } else {
            $loadedProductCollection = Mage::getModel('catalog/product')->getResourceCollection();
            $loadedProductCollection->setIsLoaded(true);
            $cachedProductCollectionArray = unserialize($cachedProductCollection);   
            $loadedProductCollection->importFromArray($cachedProductCollectionArray);
        }

        return $loadedProductCollection;
    }
}

The code could be a bit more compact but like this a bit easier to understand. This is intended to be called instead of getLoadedProductCollection, to facilitate for product from multiple categoties in one page for example. Hope it helps!

Magento has its built-in collection cache, but keep in mind, collection cache does not cache collection object, it cache data that load from database base on sql executed。

In your case, you can try below code:

        $_productCollection = Mage::getModel('catalog/product')->getCollection();
        $_productCollection->addAttributeToSelect('*');                        
        $_productCollection->addAttributeToSelect('stock_status');        
        $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/produttore'));
        $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/ean'));
        $_productCollection->addAttributeToSelect(Mage::getStoreConfig('feed_options/mappa_attributi/mpn'));      
        $_productCollection->addAttributeToFilter('type_id', Mage_Catalog_Model_Product_Type::TYPE_SIMPLE);                                                                       

       if (Mage::app()->useCache('collections')) {
            $_productCollection->initCache(
               Mage::app()->getCache(),
               "defineuniquevalue",
               array("COLLECTION_DATA")
         );
     }
 return $_productCollection;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM