Magento Event Driven Programming Tips & Tricks

event_observer

One of the cool things about Magento from the application architectural point of view is its support for the event driven programming. That is, event – observer system. The whole idea is pretty simple. On one side you have an events getting fired, and on another side you have observers listening for specific events and executing certain logic when specific event is fired. What’s great about event driven programming is that it enables clear separation of your custom code from the core code.

It allows you to hook into certain parts of the workflow and possibly change its behaviour or at the very least inject your additional custom logic alongside. Given the role Magento tries to fill in, the super modular and extendible eCommerce platform for growth, events play a significant role in its architecture. Not so surprisingly, this event – observer pattern is mostly used among extension developers, because it enables them clean injection of extensions functionality into Magento. For example, lets say you have a specific requirement of notifying a 3rd party system about each new order that gets created. In Magento, this is really easy, you would simply implement observer that observes sales_order_place_after event and place your logic within it.

There are several “types” or better to say kinds of event getting fired in Magento. For one, we can differentiate what I call:

  • static events
  • dynamic events

First of all, this is just my terminology. There is no special difference between the two, its just how I like to call them, so bare with me.

Let me explain what I mean by static event. Static events are all those events defined with full event name like:

  • Mage::dispatchEvent(‘admin_session_user_login_failed’, array(‘user_name’ => $username, ‘exception’ => $e));
  • Mage::dispatchEvent(‘cms_page_prepare_save’, array(‘page’ => $model, ‘request’ => $this->getRequest()));
  • Mage::dispatchEvent(‘catalog_product_get_final_price’, array(‘product’ => $product, ‘qty’ => $qty));
  • Mage::dispatchEvent(‘catalog_product_flat_prepare_columns’, array(‘columns’ => $columnsObject));
  • Mage::dispatchEvent(‘catalog_prepare_price_select’, $eventArgs);

Dynamic events are all those events defined with dynamically, at runtime, fetched event name like:

  • Mage::dispatchEvent($this->_eventPrefix.’_load_before’, $params);
  • Mage::dispatchEvent($this->_eventPrefix.’_load_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_save_before’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_save_after’, $this->_getEventData());
  • Mage::dispatchEvent(‘controller_action_layout_render_before_’.$this->getFullActionName());

Once more, both type of events are absolutely the same, they function the same, this is just my terminology. If you are an extension developer, then chances are that most of the time you will be interested in dynamic events. For example, each time you wish to intercept a certain parameters passed to a controller action you could simply create an event observer that would observe “controller_action_predispatch_*” event triggered within Mage_Core_Controller_Varien_Action class file:

Mage::dispatchEvent(‘controller_action_predispatch_’ . $this->getFullActionName(), array(‘controller_action’ => $this));

Now, let us see how exactly do we define event observer and place some of our code to be executed upon certain event. First we need to create an entry within our modules config.xml file. Lets say we want to “examine” all of the parameters passed to controller action during customer registration process. When customer fills in the required registration fields and clicks “Submit”, form POST’s the data to http://{{shop.domain}}/index.php/customer/account/createpost/ url. Now, if you look at the previously mentioned “controller_action_predispatch_*” event, expression “$this->getFullActionName()” would return “customer_account_createpost” string. You can find that out easily by placing the “var_dump($this->getFullActionName()); exit;” expression right there under the “Mage::dispatchEvent(‘controller_action_predispatch_…” expression. So, now that we now that, we can safely conclude that the full event name we need to observe in this case is “controller_action_predispatch_customer_account_createpost”.

Now let us create a required entry within our modules config.xml file:

    <frontend>
        <events>
            <controller_action_predispatch_customer_account_createpost>
                <observers>
                    <inspectCustomerRegistrationData>
                        <class>Inchoo_Test_Model_Observer</class>
                        <method>inspectCustomerRegistrationData</method>
                    </inspectCustomerRegistrationData>
                </observers>                
            </controller_action_predispatch_customer_account_createpost>
        </events>

Value of <class> implies on using Inchoo_Test_Model_Observer class file and value of implies on using the “inspectCustomerRegistrationData” method within that class. The can be written slightly different, for example “<class>inchoo_test/observer” where “inchoo_test” is your model class group. Its actually a better, more modular, approach to use class group (“inchoo_test”) for <class> definition here. Method “inspectCustomerRegistrationData” is where we will put our custom logic. In this case we will simply try to fetch the POST parametars passed to the controller action, so our inspectCustomerRegistrationData class file looks like:

<?php
 
class Inchoo_Test_Model_Observer
{
    public function inspectCustomerRegistrationData($observer = null)
    {
        $event = $observer->getEvent();
 
        $controllerAction = $event->getControllerAction();
 
        Zend_Debug::dump($controllerAction->getRequest()->getParams(), 'PARAMS');
        exit;
    }

Line “$event = $observer->getEvent();” is a standard way to get the “event” object which then holds the data passed to the event. If you look above at the example where we outlined the dispatch of ‘controller_action_predispatch_*’ event you will see that that event takes one parameter called “’controller_action’” with the value of “$this”. Since “$this” in that context came from Mage_Core_Controller_Varien_Action class file that means that our $this is actually an instance of Mage_Core_Controller_Varien_Action and that we can do stuff like “$this->getRequest()->getParams();” to get the parameters passed to controller action. Calling Zend_Debug as in example above would then output something like shown below to your browser:

PARAMS array(7) {
  ["success_url"] => string(0) ""
  ["error_url"] => string(0) ""
  ["firstname"] => string(6) "Branko"
  ["lastname"] => string(6) "Ajzele"
  ["email"] => string(16) "user@mail.com"
  ["password"] => string(8) "test123"
  ["confirmation"] => string(8) "test123"
}

The above is simple example of how to use event observer. The most important thing in the whole process is to simply “discover” the event you need, and then observer it. Sometimes the right event might not be there, so you might need to look for the second best. For example, if we did not had the “controller_action_predispatch_customer_account_createpost” event dispatched then the next best event would probably be:

Mage::dispatchEvent(‘controller_action_predispatch’, array(‘controller_action’ => $this));

However, event “’controller_action_predispatch’” is pretty generic, which means it will get triggered for every controller action predispatch. In which case you would have to do a little IF logic within your event observer code.

Usually, next to the controller related events are model related events. If you open a class file like Mage_Catalog_Model_Product, you can see property definitions like “protected $_eventPrefix = ‘catalog_product’;” and “protected $_eventObject = ‘product’;”. Now, if you trace the code a little bit down to Mage_Core_Model_Abstract class file, you will see that properties $_eventPrefix and $_eventObject are used for “dynamic” events like (alongside with the generic events for the same action):

  • Mage::dispatchEvent($this->_eventPrefix.’_load_before’, $params);
  • Mage::dispatchEvent($this->_eventPrefix.’_load_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_save_commit_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_save_before’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_save_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_delete_before’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_delete_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_delete_commit_after’, $this->_getEventData());
  • Mage::dispatchEvent($this->_eventPrefix.’_clear’, $this->_getEventData());

Knowing this is extremely important, as it enables you to create all sort of event observers for specific models and their actions, like “Customer entity create/update/delete”, “Order entity create/update/delete”, “Invoice entity create/update/delete”, etc. As a matter of fact, defining $_eventPrefix and $_eventObject properties on your custom model classes is something you should adopt as a sign of good coding practice. Doing so enables the other guy to hook into your extension code in a clean way.

To conclude, writing observers is pretty easy. Most of the time issues pop out when you do not have an event dispatched for certain situations. In such cases, try looking around for “dynamic” events or the next best event you can hook into.

Hope this helps.
Cheers.


3 comments

  1. Your explanation is written in pretty well format, My suggestion is to write a series as some part in the writing may require further explore to show how it can and how it does.

    I am aiming to write articles on Magento and came to best reference than any other have on Events. Indeed the article explain alot to pros. 🙂

  2. but what do if we don’t know that what event should to choose because magento haves a lot of events but which one of them is best match for my module requirement .. how can we determine it..

    1. If I need to find correct event for my custom observer, I usually do such steps:
      1. Determine, what object is going to be modified (customer, CMS page, product, search results collection etc)
      2. Locate module which handles that object
      3 a. If I know when I need to modify the object , I go to controllers, blocks or models, and search for ‘Mage::dispatchEvent’ calls, and use corresponding event.
      3 b. If I don’t know what exact event I need to observe – I just search for ‘Mage::dispatchEvent’ calls in that extension, and look for most suitable event there.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.