Programmatically change store config to avoid rewrites in Magento

March 18, 2016 in #Magento

Here's a nice strategy to achieve a desired functionality in Magento while completely avoiding class rewrites  —  programmatically changing the store configuration via observers prior to core code execution.

This means altering Magento’s execution behavior by changing store configuration values instead of modifying core code. This is not applicable for any Magento functionality, instead, only for one that relies on the XML configurations.

Let's assume you need to change the recipient email address of default Magento contact form based on some custom criteria. Contact form message is sent from Mage_Contacts_IndexController controller's postAction method. The code to send the message looks like this:

...
$mailTemplate->setDesignConfig(array('area' => 'frontend'))
    ->setReplyTo($post['email'])
    ->sendTransactional(
        Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE),
        Mage::getStoreConfig(self::XML_PATH_EMAIL_SENDER),
        Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT),
        null,
        array('data' => $postObject)
    );
...

Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT) parameter of sendTransactional method defines to which email address this message should be sent. In this case, recipient email address is stored in Magento configuration that can be managed from admin panel.

Now let's say you want to send this message to different email address if customer is logged in - a feature to separate customer service emails into registered and guest users. At first glance, there is not much we can do - there are no specific events dispatched that would let us directly change the recipient email via observer. In most cases, developer would just rewrite the controller class, copy method over and change the recipient parameter.

It’s not wrong  — it works. But there’s better solution. Solution that greatly reduces chances on conflicting class rewrites. It is especially useful for the parts of Magento that tend to be altered rather often  —  catalog, checkout.


First step is to create an observer that will let us execute our code prior to the controller's action. Since this is a controller, each of its action methods have the "pre-dispatch" event available. In this case, the observer's XML configuration looks like this:

...
<frontend>  
    <events>
        <controller_action_predispatch_contacts_index_post>
            <observers>
                <vendor_modulename>
                    <type>singleton</type>
                    <method>setCustomRecipient</method>
                    <class>vendor_modulename/observer</class>
                </vendor_modulename>
            </observers>
        </controller_action_predispatch_contacts_index_post>
    </events>
</frontend>  
...

This configuration tells that on controller_action_predispatch_contacts_index_post event, which happens before controller's postAction method execution, there's some custom code in vendor_modulename/observer class's setCustomRecipient method that should be executed.

When the observer config definition is finished, create the observer class and add setCustomRecipient method with code like this:

public function setCustomRecipient(Varien_Event_Observer $observer)  
{
    // Skipping customer checks & assuming $recipient holds the email

    $path = Mage_Contacts_IndexController::XML_PATH_EMAIL_RECIPIENT;
    Mage::app()->getStore()->setConfig($path, $recipient);
}

I have skipped the part of getting recipient email based on whether current user is logged-in or not (just assume that $recipient variable holds the correct destination email address). The important part is changing the config — and it is done with Mage::app()->getStore()->setConfig($path, $recipient);

What this line essentially does is — retrieves current store instance and sets new configuration value — $recipient — in its $path configuration node.


So, since this is executed before the postAction method and the configuration value is changed by us, the email will be sent to the one we specified. This happens because Magento loads the email address from config by this line - Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT) - which will simply return the value from configuration node we changed - the path is same, but the value is the one we applied in observer.

And that's it — we have used an observer and changed the configuration node value to achieve desired functionality without any rewrites. The change of configuration value is not permanent as it is not saved. With this strategy, you can save yourself from rewriting classes & copying huge chunks of code (super upgrade-friendly). I have applied this strategy successfully multiple times and it has never failed.

Next time you have an assignment to modify Magento functionality that depends on configuration values (such as displaying products in grid or list for specific categories; charging customer when order has virtual only products & authorizing when order has physical products, etc.), think about this approach - it might really help you!