Magento中的事件(event)和观察者模式(observer pattern)相当的有意思,它使得开发者在应用流中对Magento的重要部分进行扩展。为了提供高灵活性以及促进不同模块之间的交互,Magento添加了事件/观察者模式,这一模式可以让模块之间产生松散耦合。
这一系统分为两部分,处理对象和事件信息的事件以及监听特定事件的观察者。
事件处理
事件是通过Mage::dispatchEvent()函数来创建和处理的。core内部已在core的主要部分创建了一些事件,例如,模型抽象类Mage_Core_Model_Abstract在每次模型被保存时调用_beforeSave()和_afterSave()这两个被保护的方法(protected),每个方法将会应用两个事件:
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;
}
每个方法应用一个常规事件mode_save_after,然后基于对象类型的动态版会被保存。这使得我们可以通过观察者来操作对象成为可能。Mage::dispatchEvent()中有两个参数,第一个是事件名,第二个是通过observer接收到的一个数组,这让我们可以轻易地对对象进行操作。
为方便大家了解事件系统的详细内容,让我们来看看Mage::dispatchEvent()方法:
public static function dispatchEvent($name, array $data = array())
{
$result = self::app()->dispatchEvent($name, $data);
return $result;
}
事实上这个函数是Mage_Core_Model_App中app core类内部dispatchEvent()的一个别名函数(/app/code/core/Mage/Core/Model/App.php):
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;
}
dispatchEvent()方法实际上完成了事件/观察者模型的所有工作:
- 获取Magento配置对象
- 遍历观察者的子节点,检查所定义的观察者是否在监听当前事件
- 对于每个现有observer,dispatch事件会试图去实际化观察者对象
- 最后,Magento会尝试调用映射特定事件的相应观察者方法
观察者绑定
现在,分派事件是方程中的唯一部分,我们还需要告诉Magento用哪个observer来监听各事件。您可能已经猜到,观察者通过config.xml来进行指定。正如前面我们所看到的,dispatchEvent()方法在配置对象查看哪些观察者可用。下面让我们来看看config.xml文件:
<events>
<event_name>
<observers>
<observer_identifier>
<class>module_name/observer</class>
<method>function_name</method>
</observer_identifier>
</observers>
</event_name>
</events>
event节点可通过各配置部分来进行指定(如admin, global, frontend等等),我们也可以指定多个event_name子节点,event_name需要与dispatchEvent()中用到的事件名相匹配。在每个dispatchEvent()节点中,都有一个可包含多个observer的单个观察者节点,这些observer都有单独的标识符。
观察者节点有两个属性,如<class>,指向我们的观察者模型类,另外还有<method>,它指向observer类内部的一个方法。让我们一起来分析一个定义观察者类的例子:
class Namespace_Modulename_Model_Observer
{
public function methodName(Varien_Event_Observer $observer)
{
//some code
}
}
*有意思的是观察者模型不继承Magento中的任何类