Book Image

Magento PHP Developer????s Guide, 2nd Edition

By : Allan MacGregor
Book Image

Magento PHP Developer????s Guide, 2nd Edition

By: Allan MacGregor

Overview of this book

Table of Contents (16 chapters)
Magento PHP Developer's Guide Second Edition
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Events and observers


The event and observer pattern is probably one of Magento's more interesting features, as it allows developers to extend Magento in critical parts of the application flow.

In order to provide more flexibility and facilitate interaction between the different modules, Magento implements an Event/Observer pattern. This pattern allows modules to be loosely coupled.

There are two parts to this system, an Event dispatch with the object and event information and an Observer listening to a particular event:

Event dispatch

Events are created or dispatched using the Mage::dispatchEvent() function. The core team has already created several events on critical parts of the core. For example, the Model abstract class Mage_Core_Model_Abstract calls two protected functions every time a model is saved: _beforeSave() and _afterSave() on each of these methods two event are fired.

protected function _beforeSave()
{
    if (!$this->getId()) {
        $this->isObjectNew(true);
    }
    Mage::dispatchEvent('model_save_before', array('object'=>$this));
    Mage::dispatchEvent($this->_eventPrefix.'_save_before', $this->_getEventData());
    return $this;
}

protected function _afterSave()
{
    $this->cleanModelCache();
    Mage::dispatchEvent('model_save_after', array('object'=>$this));
    Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData());
    return $this;
}

Each function fires a generic model_save_after event, and then a dynamic version based on the type of object being saved. This gives us a wide range of possibilities to manipulate objects through observers.

The Mage::dispatchEvent() method takes two parameters, the first is the event name and the second is an array of data that is received by the observer. We can pass values or objects in this array. This comes in handy if we want to manipulate the objects.

In order to understand the details of the event system, let's take a look at the dispatchEvent() method:

public static function dispatchEvent($name, array $data = array())
{
    $result = self::app()->dispatchEvent($name, $data);
    return $result;
}

This function is actually an alias to the dispatchEvent() function inside the App core class, located in Mage_Core_Model_App:

public function dispatchEvent($eventName, $args)
{
    foreach ($this->_events as $area=>$events) {
        if (!isset($events[$eventName])) {
            $eventConfig = $this->getConfig()->getEventConfig($area, $eventName);
            if (!$eventConfig) {
                $this->_events[$area][$eventName] = false;
                continue;
            }
            $observers = array();
            foreach ($eventConfig->observers->children() as $obsName=>$obsConfig) {
                $observers[$obsName] = array(
                    'type'  => (string)$obsConfig->type,
                    'model' => $obsConfig->class ? (string)$obsConfig->class : $obsConfig->getClassName(),
                    'method'=> (string)$obsConfig->method,
                    'args'  => (array)$obsConfig->args,
                );
            }
            $events[$eventName]['observers'] = $observers;
            $this->_events[$area][$eventName]['observers'] = $observers;
        }
        if (false===$events[$eventName]) {
            continue;
        } else {
            $event = new Varien_Event($args);
            $event->setName($eventName);
            $observer = new Varien_Event_Observer();
        }

        foreach ($events[$eventName]['observers'] as $obsName=>$obs) {
            $observer->setData(array('event'=>$event));
            Varien_Profiler::start('OBSERVER: '.$obsName);
            switch ($obs['type']) {
                case 'disabled':
                    break;
                case 'object':
                case 'model':
                    $method = $obs['method'];
                    $observer->addData($args);
                    $object = Mage::getModel($obs['model']);
                    $this->_callObserverMethod($object, $method, $observer);
                    break;
                default:
                    $method = $obs['method'];
                    $observer->addData($args);
                    $object = Mage::getSingleton($obs['model']);
                    $this->_callObserverMethod($object, $method, $observer);
                    break;
            }
            Varien_Profiler::stop('OBSERVER: '.$obsName);
        }
    }
    return $this;
}

The dispatchEvent() method actually does all the work on the Event/Observer model:

  1. It gets the Magento configuration object.

  2. Then, it walks through the observer's node children, checking if the defined observer is listening to the current event.

  3. For each of the available observers, the dispatch event tries to instantiate the observer object.

  4. Lastly, Magento tries to call the corresponding observer function mapped to this particular event.

Observer bindings

Now, dispatching an event is only part of the equation. We also need to tell Magento which observer is listening to each event. Not to our surprise, observers are specified through the config.xml file. As we saw before, the dispatchEvent() function queries the configuration object for available observers. Let's take a look at an example config.xml file:

<events>
    <event_name>
        <observers>
            <observer_identifier>
                <class>module_name/observer</class>
                <method>function_name</method>
            </observer_identifier>
        </observers>
    </event_name>
</events>

The event node can be specified in each of the configuration sections (admin, global, frontend, and so on) and we can specify multiple event_name children nodes. The event_name node has to match the event name used in the dispatchEvent() function.

Inside each event_name node, we have a single observer node that can contain multiple observers, each with a unique identifier.

Observer nodes have two properties, <class>, which points to our observer model class, and <method>, which points to the actual method inside the observer class. Let's analyze an example observer class definition:

class Namespace_Modulename_Model_Observer
{
    public function methodName(Varien_Event_Observer $observer)
    {
        //some code
    }
}

Note

One interesting thing about observer models is that they don't extend to any other Magento class.