7

In my resource model I have defined a field as serializable (see property $_serializableFields in Mage_Core_Model_Resource_Db_Abstract): Magento handles automatically serialization/unserialization of the contents when loading data from the database and before saving the object.

This works perfectly, but when I iterate over a collection of those objects, the field contents are not unserialized automatically. I tried with $collection->walk('afterLoad') but soon realized that unserializeFields() is triggered in the resource model load(), and not in afterLoad() like I first thought. What is the best practice in this case? How do I get this field unserialized automatically? I can of course reload the object in my loop, doing something like $object = $object->load($object->getId()), but I was wondering if there is a smarter way to achieve this.

4 Answers 4

9

I can of course reload the object in my loop, doing something like $object = $object->load($object->getId())

This is not a reload - this is a load. Items (model instances) in collections are not loaded. They merely have had an associative array of result row data applied to them. It's an important and slightly confusing difference between these collection-owned model instances and those which are self-load data through their resource model.

In your collection's _afterLoad(), iterate over the _items and unserialize() the appropriate field:

protected function _afterLoad()
{
    parent::_afterLoad();
    foreach ($this->getItems() as $item) {
        $item->setData('field',unserialize($item->getData('field')));
        $item->setDataChanges(false);
        //The above sets items as not dirty.
        //Value will be serialized on save via resource model.
    }
    return $this;
}
3
  • I know they are not loaded, wrong wording, but thanks for pointing that out! Neat solution indeed, thought there might be an easier way, but this looks just right.
    – fmrng
    Commented Jun 28, 2013 at 12:24
  • It would be nice if Mage_Core_Model_Resource_Db_Abstract defined a getter for _serializableFields that the collection abstract would use to perform the same logic. That said, it would be easy enough for you to create one in your resource model.
    – benmarks
    Commented Jun 28, 2013 at 12:28
  • Also a good idea, to keep the logic inside the resource model. Like that, the collection doesn't need to know which fields need to be unserialized. Even if I only have one field and I'm not planning to add more, it is definitely more consistent.
    – fmrng
    Commented Jun 28, 2013 at 12:37
4

Or more simply, in your resource model collection:-

class Collection 
extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
    protected function _afterLoad()
    {
        parent::_afterLoad();
        foreach ($this->getItems() as $item) {
            $this->getResource()->unserializeFields($item);
            $item->setDataChanges(false);
        }
        return $this;
   }
}
3
  • Not working in M2 (2.2.6)
    – Priyank
    Commented Feb 15, 2019 at 9:02
  • 1
    Nice idea. Works great in Magento 2.3. I prefer this solution over the accepted answer, because it uses Magento unserialization utility instead of PHP's unserialize. This way it respects Magento's serialization mode.
    – stollr
    Commented Dec 15, 2020 at 11:35
  • Agree about preferring to pass off the work of unserialization to the Magento\Framework\Model\ResourceModel\Db\AbstractDb::unserializeFields($object) method. Thanks for this answer.
    – sdaugherty
    Commented Mar 25, 2021 at 5:58
0

To do this for product collections I added an event listener to catalog_product_collection_load_after and to iterate over all products.

/**
 * Apply backend manipulations of attributes to collection items.
 * Listens to catalog_product_collection_load_after.
 *
 * @param Varien_Event_Observer $observer
 * @return Mage_Catalog_Model_Observer
 */
public function loadCollectionAttributes(Varien_Event_Observer $observer)
{
    $collection = $observer->getCollection();
    /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */
    foreach ($collection as $product) {
        /* @var $product Limora_Catalog_Model_Product */
        foreach ($product->getAttributes() as $attribute) {
            /* @var $attribute Mage_Eav_Model_Attribute */
            $attribute->getBackend()->afterLoad($product);
        }
    }
}
0

You can use \Magento\Framework\Model\ResourceModel\Db\AbstractDb::$_serializableFields with \Magento\Framework\Data\Collection\AbstractDb::beforeAddLoadedItem

Add this code to yor custom collection

// Model/ResourceModel/SomeModel/Collection.php    

/**
 * @inheritDoc
 */
protected function beforeAddLoadedItem(\Magento\Framework\DataObject $item)
{
    $this->getResource()->unserializeFields($item);
    return parent::beforeAddLoadedItem($item);
}

For grid collection:

You should add method to resource model:

// Model/ResourceModel/SomeModel.php

public function unserializeDataProviderDocumentFields(Document $object)
{
    foreach ($this->_serializableFields as $field => list($serializeDefault, $unserializeDefault)) {
        $this->_unserializeField($object, $field, $unserializeDefault);
    }
}

And use this method in grid collection instead unserializeFields:

// Model/ResourceModel/SomeModel/Grid/Collection.php

protected function beforeAddLoadedItem(DataObject $item)
{
    $this->getResource()->unserializeDataProviderDocumentFields($item);
    return parent::beforeAddLoadedItem($item);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.