Programmatically change store config to avoid rewrites 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:
<?php
...
$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 the 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 does work. But there is a better solution. The solution that greatly reduces chances of conflicting class rewrites. It is especially useful for the parts of Magento that tend to be altered rather often - catalog, checkout.
The 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 the controller_action_predispatch_contacts_index_post
event, which happens before
controller’s postAction
method execution, there is some custom code in the 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:
<?php
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!