Magento Events


Magento Events

Magento “Events” are a very powerful tool that allows customization of the Magento workflow. Events allow multiple modules to be triggered when a user or system action occurs simply by using a subscription model to subscribe to the required events. Magento Events follow the popular and well known “Observer” Design Pattern and it is assumed that by reading this article, you already know enough to proceed.

In the core code of Magento there are many points where Magento fires events. Each event has a unique name and some additional data parameters associated with it. In our module, we can subscribe to these events, so that as soon as Magneto fires the event, a function in our custom module class is executed. We can then apply our business logic accordingly.

Magneto uses a function called Mage::dispatchEvent() to fire an event. You will find this method call at many places inside Magento code base. A “grep” of the core files will yield all the points and the number of dispatchEvent() method calls.

List of Events

The events list is continually expanding in the Magento core and with extensions you can easily add even more events. In v1.4 there are nearly 300 events compared to 223 in v1.3 and 140 in v1.2. This article is based on v1.7 and there are over 423 instances of dispatchEvent() calls!

On a Unix like system you can easily determine the available events in your particular build by grepping through the Local, Core and Community folders of your install eg change to the Magento root folder and type:

grep -rin  “Mage::dispatchEvent” app/* > events.txt

This will create a file events.txt containing all the events located in the app folder.

Basic Event Handling Overview

The basic process to trap events is (not in any order):

  1. Define the event(s) to observe via your config.xml file.
  2. Define the event code in your Observer.php file.
  3. Tell Magento that the configuration file (config.xml) exists so it reads it and knows what to do.

Directory Structure Required

Magento requires the XML and the event processing code to be resident in a specific directory structure. Once Magento knows which events we are interested in, it must be able to locate the observer code so that it can call the appropriate method in the appropriate class. For our example, the observer code will need to reside in:

app/code/local/<Namespace>/<Yourmodule>/Model/Observer.php

Where <Namespace> is a directory name used to contain all your code and configuration data. For the sake of the tutorial, I have left Namespace as the directory but you can change this to suit your needs. The same goes for Yourmodule,

And the configuration data will reside in:

app/code/local/<Namespace>/<Yourmodule>/etc/config.xml

Under the <Yourmodule> directory Magento requires a “Model” directory which will then contain our module called Observer.php and inside that we will declare our methods.

Basic folder structure for new Magento Extensions

All Magento custom modules have a simple structure. The structure looks like:

app/code/local/<Namespace>/<Yourmodule>/Block
app/code/local/<Namespace>/<Yourmodule>/controllers
app/code/local/<Namespace>/<Yourmodule>/etc
app/code/local/<Namespace>/<Yourmodule>/Helper
app/code/local/<Namespace>/<Yourmodule>/Model
app/code/local/<Namespace>/<Yourmodule>/sql

You will need to create this directory structure now.

Why Local?

Local pool has the highest load priority and code in this pool can’t be overridden as easy as in community or core pools. But depending on the Magento ideology, local pool was primarily designed for local changes. If you own a website and make modifications for that website, then your modifications are classified as local and your changes should be made in the app/code/local folder.

Also, when you do some custom work on the website – local code pool is for you. However, if the extension is used by many other customers, this extension is not local but shared with a (limited) community. Therefore, the extension belongs to the community pool.

Provided that you are an extension developer, you shouldn’t prevent overriding your files by other developers for the purpose of making some small changes. That can always be done by using the local pool.

Subscribing to Events

For our example, we will subscribe to the “cms_page_render” event, so we need to create the config.xml file in app/code/local/<Namespace>/<Yourmodule>/etc

<?xml version=”1.0″?>
<config>
<global>
        <modules>
                <Namespace_Yourmodule>
                        <version>1.0></version>
                </Namespace_Yourmodule>
        </modules>
        <models>
        <yourmodel>
                <class>Namespace_Yourmodule_Model</class>
        </yourmodel>
        </models>
<events>
       <cms_page_render>
            <observers>
                <test>
                    <type>singleton</type>                   <class>Namespace_Yourmodule_Model_Observer</class>                    <method>cms_page_render</method>
                </test>
            </observers>
        </cms_page_render>
    </events>
</global>
</config>

This file shows only one event, we will look at multiple events shortly.

In general, the format used to subscribe to an event is:

<events>
   <exact-name-of-event>
        <observers>
           <some_descriptive_phrase_for_your_listener>
            <type>singleton</type>
             <class>Namespace/Yourmodule</class>
             <method>function_name</method>
           </some_descriptive_phrase_for_your_listener>
      </observers>
   </exact-name-of-event>
</events>

 The key to identif

A breakdown of the tags

<global>      Standard tag for Magento wide configuration, Observers are normally put in Global but you can also put them in “frontend” or “adminhtml”.

<events>     This is the element that stores all of the events that are registered.

<observers> This is the type of event.

<descriptive phrase>

This is a unique string that defines this configuration. It can be anything, and just needs to be unique and best to make it relevant, short and descriptive.

<type>        Usually “singleton”, but other options can be “model” or “object”. The “singleton” will create the object as Mage::getSingleton() while both “object” and “model” will use Mage::getModel() when creating the observer object.

<class>       This is the observer class.

<method>   This is the function name to be called in the observer class.

Notes on setting the “type”

Observer type is a singleton by default, so if you skip the type node, your observer will be loaded as a singleton. That means, if you have many observers binded to the same action, the data, that was passed to the observers will be changed in the first observer and will be passed to another.

When you choose ‘model’ type or ‘object’ i.e. <type>model</type> that means, if data was changed in the first observer, then changed data will be NOT passed into the second. The second observer loads the data again.

Also, we have a type called ‘disabled’, which tells Magento to not run this observer.

Observer.php and Class structure

Using our XML configuration file from earlier, we now have the example class file, Observer.php, located in app/code/local/<Namespace>/<Yourmodule>/Model

The class is defined as “Namespace_Yourmodule_Model_Observer”, the method “cms_page_render()” is what Magento will call and we know Magento will pass an array to it, we will call it “$observer”.

We can now do whatever we need to when the function is executed. Below is a working example that will log a message into the Magento System log and it will log an entry into a log file in a directory (you will need to create), called “/logs”, When you create the directory make sure it has full write permissions!

  • Mkdir /logs
  • Chmod 664 /logs
  • Chown apache:apache /logs
<?php
class Namespace_Yourmodule_Model_Observer
{
     function __construct()
     {
       Mage::log("Observer code executed");
     }

    public function LogInfo($msg)
    {
         $dt=date("Y-m-d");
         $t=date("h:m:s");
         $logfile="/logs/event-triggers-$dt.log";
         $fd=fopen($logfile,"a");
         if($fd)
         {
           fwrite($fd,$dt."|".$t."|".$msg."\n");
           fclose($fd);
        }
  }

    protected function LogVars($o)
    {
        while(list($key,$val)=each($o))
         {
            $this->LogInfo("Key [$key] Value [$val]");
           if(is_array($val))
           {
           while(list($k,$v)=each($val))
             $this->LogInfo("    \------ Key [$k]  Value [$v]");
            }
        }
    }

    public function cms_page_render($observer)
    {
        $event = $observer->getEvent();
         $this->LogInfo("EVENT -> cms_page_render()");
         $this->LogInfo("Dump Observer Array");
         $this->LogVars($observer);
         $this->LogInfo("Dump Event Array");
         $this->LogVars($event);
    }
}
?>

Making it all work

So far we have put all the pieces in place to add an event observer and the code to handle the event, however Magento needs to know about our custom modules, to achieve this, Magento has a directory where each module has its own XML config file, the file registers the module with Magento. To register a new custom local module, let’s say it’s under the name ‘Xyz’, navigate to: app/etc/modules/ Create an XML file which contains the following format:

<?xml version="1.0"?>
<config>
  <modules>
    <Namespace_Yourmodule>
      <codePool>local</codePool>
      <active>true</active>
    </Namespace_Yourmodule >
  </modules>
</config>

Note that we define “Namespace_Yourmodule”, the “_” separates the directory path, so when Magento constructs the path to the Oberserver code it actually points to:

app/code/local/Namespace/Yourmodule/

Testing the Code

If you have followed the tutorial and entered the configuration and code as shown above you should now be able to exercise the event handler. Firstly you can confirm that Magento knows of your new module, it will appear in the Magento System configuration. Navigate to System -> Configuration -> Advanced ->Disable Modules Output. Your module will appear in the list, most likely at the bottom and should be “Enabled”. Now turn on the system.log file, navigate to: System -> Configuration -> Developer -> Log Settings Set the logging to “Enabled”. The best way is to create a CMS page for the home page and assign it in the configuration for the website. Navigate to System -> Configuration -> Web ->Default Pages Set the home page here, then navigate to: CMS -> Pages and select the home page, enter some content text. Now prepare to view the log files, I use “tail” to look at the log files.

localhost#watch ‘tail /logs/event*.log; tail /var/magento/var/log/system.log’

If you have coded the XML config wrong, the system.log will show any parse errors. If you have placed the Observer.php file in the wrong directory or have the wrong permissions the system.log will show this. Then open your Magento home page – observe the logs and events should start to be recorded in the log files.

Another Example – Multiple Event Support

To capture multiple events, in this case “checkout_cart_add_product_complete” and  “checkout_cart_product_add_after” which will be raised by Magento when we add products to the cart, you will need to do the following:

    1. Create the required module directory structure.
  • Craft a config.xml file
  • Write the observer PHP file.
  • Create a module-config XML file.

For this, you just need to add some event observer code in your module’s config.xml file located in:

app/code/local/<Namespace>/<Yourmodule>/etc/config.xml

And add a new observer PHP file to:

app/code/local/<Namespace>/<Yourmodule>/Model/Observer.php

First, the XML configuration in config.xml to define the event handlers:

<?xml version="1.0"?>
<config>
<global>
<modules>
    <Namespace_Yourmodule>
        <version>1.0></version>
    </Namespace_Yourmodule>
</modules>
<models>
    <yourmodel>
        <class>Namespace_Yourmodule_Model</class>
    </yourmodel>
</models>
<events>
  <checkout_cart_add_product_complete>
    <observers>
      <add_product_complete>
        <type>singleton</type>
        <class>Namespace_Yourmodule_Model_Observer</class>
         <method>checkout_cart_add_product_complete</method>
      </add_product_complete>
    </observers>
  </checkout_cart_add_product_complete>
  <checkout_cart_product_add_after>
    <observers>
      <product_add_after>
        <type>singleton</type>
        <class>Namespace_Yourmodule_Model_Observer</class>
        <method>checkout_cart_product_add_after</method>
      </product_add_after>
    </observers>
  </checkout_cart_product_add_after>
</events>
</global>
</config>

Then the PHP code contained in our file “Observer.php” located in our “Model” directory:

<?php
class Namespace_Yourmodule_Model_Observer
{
    function __construct()
    {
       Mage::log("Observer code executed");
    }
    public function LogInfo($msg)
    {
       $dt=date("Y-m-d");
       $t=date("h:m:s");
       $logfile="/logs/event-triggers-$dt.log";
       $fd=fopen($logfile,"a");
       if($fd)
       {
          fwrite($fd,$dt."|".$t."|".$msg."\n");
          fclose($fd);
       }
    }
    protected function LogVars($o)
    {
       while(list($key,$val)=each($o))
       {
          $this->LogInfo("Key [$key] Value [$val]");
          if(is_array($val))
          {
             while(list($k,$v)=each($val))
             $this->LogInfo("    \------ Key [$k]  Value [$v]");
          }
      }
    }
    public function checkout_cart_product_add_after($observer)
    {
        $event = $observer->getEvent();
        $this->LogInfo("EVENT -> add_after()");
        $this->LogInfo("Dump Observer Array");
        $this->LogVars($observer);
        $this->LogInfo("Dump Event Array");
        $this->LogVars($event);
    }
    public function checkout_cart_add_product_complete($observer)
    {
        $event = $observer->getEvent();
        $this->LogInfo("EVENT -&gt add_complete()");
        $this->LogInfo("Dump Observer Array");
        $this->LogVars($observer);
        $this->LogInfo("Dump Event Array");
        $this->LogVars($event);
    }
}
?>

Now register our Module ‘Namespace/Yourmodule’. For this create file ‘Namespace_Yourmodule.xml’ in the directory ‘app/etc/modules/’.

<?xml version=”1.0″?>
<config>
  <modules>
    <Namespace_Yourmodule>
      <codePool>local</codePool>
      <active>true</active>
    </Namespace_Yourmodule >
  </modules>
</config>

 Test your module and see what happens!

1 thought on “Magento Events”

  1. Just a quick note to say that your example config.xml has a stray > on the following line (two places in the post above):

    1.0>

    Other than that, very helpful, thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s