2

I have added custom datetime and decimal attributes to the customer address entity using update scripts such that they can be edited via the admin. Although it is possible to add or update the attribute values, it is not possible to clear them.

When adding similar attributes to the customer entity, it is possible to clear those attributes.

I am wondering what could the cause of this issue. Looks like a Magento bug to me.

3

It turns out that the backend models of customer address attributes are not fully loaded by the method Mage_Customer_Model_Customer::getAddressesCollection: the value_id from the corresponding backend tables are not loaded into the backend models, which makes it impossible to delete attribute values when saving an entity.

The root cause of the problem is that the method Mage_Eav_Model_Entity_Attribute_Backend_Abstract::setEntityValueId is not called on the backend models. This leads to the following issue when calling Mage_Eav_Model_Entity_Abstract::save($object):

  • First _collectSaveData is called on $object, which divides the attribute values that are changed into three arrays: $insert, $update and $delete. Each $update and $delete entry is an array containing a value_id element; its value is determined by calling $attribute->getBackend()->getEntityValueId($newObject)... but this is always empty since the corresponding setter was not called.
  • Second _processSaveData is called on the previously determined arrays. The $insert and $update arrays are used to fill the _attributeValuesToSave instance variable, and while doing this the $value_id element from the elements of the $update array is discarded. The $delete array is used to fill the _attributeValuesToDelete instance variable... but this does not contain anything useful, because only empty values are added. At the end of the method, _processAttributeValues is called to perform the actual database changes; for the _attributeValuesToDelete array, the statement $adapter->delete($table, array('value_id IN (?)' => $valueIds) is executed, which is useless, since $valueIds only contains empty values.

One way of solving this issue, is to use Mage_Eav_Model_Entity_Abstract::load method on each customer address that needs to be save. This method calls _loadModelAttributes, which in turn calls the _setAttributeValue method for each attribute, which makes sure the current value_id is set on the corresponding backend model by calling Mage_Eav_Model_Entity_Attribute_Backend_Abstract::setEntityValueId.

The easiest way to achieve this is by replacing the method Mage_Customer_Model_Customer::getAddressesCollection with the following implementation:

public function getAddressesCollection()
{
    if ($this->_addressesCollection === null) {
        $this->_addressesCollection = $this->getAddressCollection()
            ->setCustomerFilter($this);
        /** @var Mage_Customer_Model_Address $address */
        foreach ($this->_addressesCollection as $address) {
            /** @var Mage_Customer_Model_Resource_Address $addressResource */
            $addressResource = $address->getResource();
            $addressResource->load($address, $address->getId());
            $address->setCustomer($this);
        }
    }
    return $this->_addressesCollection;

Some other points of interest:

  • Clearing customer address attribute values of type varchar and text from the admin does not cause any issues, but only because the corresponding records in the database are updated with the empty string instead of deleted. The reason for this is that Magento uses the Mage_Eav_Model_Entity_Attribute_Abstract::isValueEmpty method to determine whether an existing attribute value should be deleted (true) or updated (false). This method only considers an empty string to be empty when the backend type is int, decimal or datetime (and not when it is varchar or text).
  • The use of Mage_Eav_Model_Entity_Abstract::load also calls the _afterLoad method, which calls afterLoad on all backend models. On default Magento installations, this is implemented only for the created_at attribute: the value is converted from UTC to the current store time zone. Since the (converted) value is converted back to UTC on save, absence of the afterLoad call leads to incorrect created_at dates.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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