It turns out that when saving an EAV entity, the
afterSave methods of the backend models of all attributes are executed, even when they are not part of the collection: the method
Mage_Eav_Model_Entity_Abstract::save calls the methods
_afterSave, which call the method
backend/afterSave as the first arguments.
Mage_Eav_Model_Entity_Attribute_Backend_Abstract::beforeSave, which is used by most attributes, sets the default value of the attribute on the supplied object in the following circumstances:
- The object does not have an entry in its internal
- The default value of the attribute is non-empty.
The first condition applies to most attributes when loading via a collection. The second condition only applies to the
include_in_menu attribute (in a standard Magento setup).
To work around this issue, simply add the
include_in_menu attribute to the collection query:
Another issue that the
afterLoad methods of the attribute backend models are not called (thanks to Marius for the warning). On a default Magento installation, this causes issues with the following attributes:
created_at: the value is not converted from UTC to the current store time zone on load. On save, the value is converted back to UTC (which it already was), leading to incorrect
available_sort_by (if it is part of the collection and its value is not
NULL): the comma-separated value is not replaced by an array. On save, the value is reset to
To resolve this, you can trigger the
afterLoad methods by adding the following to the loop body:
BTW This does not seem to harm performance that much: all category attributes are also loaded when saving the category, and Magento caches the attribute instances in the
Mage_Eav_Model_Config singleton. So it seems that when loading and saving multiple categories, each attribute is only loaded once.
Similar to the issue that the
beforeLoad method is not called for any attribute, the method
Mage_Eav_Model_Entity_Abstract::_loadModelAttributes is not called. This method does two things for every attribute:
$object->setData($attributeCode, $attributeValue) where
$attributeValue is loaded from the database.
$attribute->getBackend()->setEntityValueId($object, $valueId) where
$valueId is the primary key of the attribute value in the corresponding attribute table.
When loading EAV collections, the similarly named method
Mage_Eav_Model_Entity_Collection_Abstract::_loadAttributes is called, which does the former but not the latter. As a result, the corresponding
getEntityValueId method always returns an empty value. This only causes issues when deleting attribute values from the backend table. For categories (and also products) this only happens when setting an the value of an attribute with global scope to
To avoid the issues with not being able to delete values of attributes with global scope, do not call
$object->setData($attributeCode, false) for such an attribute. When it is necessary to clear an attribute, you can use
null instead of
false, which results in an
UPDATE query on the attribute backend table instead of a