Serializable Fields in Magento Collections
-
14-04-2021 - |
سؤال
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.
المحلول
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;
}
نصائح أخرى
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; } }
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);
}
}
}
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);
}