Back to blog home

Magento Core Hacks: Address Validation

Although Magento's directory structure, code pools, rewrites and observers provide myriad paths to implement custom functionality, there is no correct choice and sometimes in a certain situation it seems the only choices are poor ones. This post contains a brief introduction to each method and implements a novel solution to a frequent problem: Mage_Customer_Model_Address_Abstract::validate()

Please note that this post was originally published in 01.04.2014 and the methods are based on the 1.8.1.0 version of Magento. The following solutions might not function in the later versions as described here.

Method 1: Direct modification of core files

This is of course the most direct and expedient approach, however it is extremely brittle and will never survive upgrades. Most decent software developers are so conditioned against patching core code that it is simply out of the question.

Method 2: Copying the file to the local code pool and modifying it there

Although changes made in this manner will survive upgrades, the problem with this approach is that you have to copy the whole file, and cannot inherit from the original. Typically, you only need to change one or two lines, so this extra code duplication not only causes upgrade problems (since code that should be upgraded is not) but is also difficult for developers to follow. Code clarity should be a top priority for all developers.

Method 3: XML rewrites

Magento's XML rewrite system allows you to overwrite almost any class fetched by Magento's factory methods with a custom implementation. The advantage to this approach is that the overwritten files can inherit from the original file, bringing down the number of copied lines of code to a minimum. Less code is always better, amirite? The downsides to this method is that multiple rewriting of the same class is not supported, and that since it only works on classes loaded by node codes such as catalog/product, abstract classes cannot be edited

Method 4: Event observers

This is the holy grail of custom functionality. Multiple listeners play happily together, and event interfaces are usually preserved painlessly through upgrades. Unfortunately, you can of course only listen to an event that is fired, and then you only have access to the data supplied in the event; even if the firing class itself is passed in the event, you can call only its public methods.

Moving on to Mage_Customer_Model_Address_Abstract::validate()

We frequently need to override this class to make the telephone number an optional field; by default the address validation method will fail unless one is supplied. As an abstract class, an XML rewrite is unsatisfactory since every child class has to be overridden individually, resulting in more evil code duplication. Although an event (customer_address_validation_after) is fired, an examination of the code reveals that the collected errors are not mutable. We can circumvent this with PHP's reflection:

<?php
class Jarlssen_Customer_Observer
{
    public function validateAddress(Varien_Event_Observer $observer)
    {
        /** @var Mage_Customer_Model_Address_Abstract $address */
        $address = $observer->getEvent()->getAddress();
 
        { // reset errors by reflection
            $reflection = new ReflectionClass($address);
            $method = $reflection->getMethod('_resetErrors');
            $method->setAccessible(true);
            $method->invoke($address);
        }
        
        // now we can do our custom validation
        if (!Zend_Validate::is($address->getFirstname(), 'NotEmpty')) {
            $address->addError(Mage::helper('customer')->__('Please enter the first name.'));
        }
        [...]
        
    }
}

 

Yes, it is abusing reflection to call a private method, but the succinctness and clarity of an observer approach outweigh this given the impracticality of the other three options.