zen-cart开发教程 - 通知者/观察者模式
通过重写机制和自动加载机制, Zen-Cart使得二次开发人员能够很方便的向核心代码中增加自己的功能, 然而开发者并不能在不破坏原始代码的基础上随意向Zen-Cart核心代码中的不同地方增加自己的代码. 因此, Zen-Cart引入了通知者/观察者模式(observer/notifier system, ONS), 这给开发人员提供了访问Zen-Cart核心代码, 而不需要破坏其原始文件内容的方式.第1节 基类base
为了实现通知者/观察者模式, 首先引入一个重要的类: base类(class.base.php).
base类中包含了实现通知者/观察者模式的代码, 因此为了使通知者/观察者模式生效, Zen-Cart中的所有类都被定义为base类的子类, 任意打开一个类文件, 例如language.php,你讲可以看到如所示的代码. 如果你希望你自己的类也能够实现通知者/观察这模式, 请将你的类从base类派生.
class language extends base { … .
第2节 通知者
ONS的意图是, 开发者可以写代码, 来等待特定的事件发生, 一旦事件发生, 就执行自己的代码. 那么这些事件如何定义, 什么时候触发呢?
事件可以通过如下面所示的代码来进行触发.
$this -> notify( ' EVENT_NAME ' );
第3节 观察者
事件已经触发了, 可是这对开发者而言又有何用呢? 为了利用通知者的事件, 我们必须编写代码来监听这些事件. 一般而言, 我们需要将观察者编写成一个类, 并放到目录” includes/classes/observers”下面. 下面, 我们就来看一下如何写一个监听事件的观察者类.
<? php class myObserver extends base { function myObserver() { $this -> attach( $this , array ( ' NOTIFIER_CART_ADD_CART_END ' )); } function update( & $callingClass , $notifier , $paramsArray ) { ... do some stuff } }
从代码中, 可以看到, 我们定义了一个名为myObserver的类, 并且在构造函数中将myObserver类和事件NOTIFIER_CART_ADD_CARD_END(shopping_cart.php)进行了绑定.
当某个事件被触发时, base类将检测是否有观察者类在监听这个事件, 如果有, base类将执行观察者类中的一个方法, 在这里当NOTIFIER_CART_ADD_CARD_END事件被触发后, update方法将会被执行.
参数说明:
attach方法.
&$observer - 观察者类的引用, 为新的观察者生成一个唯一的ID
$eventIDArray - 要监听的事件数组
update 方法
&$callingClass – 触发事件的类的引用, 你可以通过该参数访问类中的变量
$notifier – 触发update通知者名称, 因为可能会监听到多个通知者触发事件
$paramsArray – 传递的参数, 通知这可能利用该参数向观察者提供一些参数
第4节 notifier类
ONS是面向对象的思想而设计, 观察者期望将自己绑定到一个可以在自己方法中进行通知的类, 然而在Zen-Cart中的很多程序事实上并不包含在类中, 例如页面中的代码. 为了使在一般的程序中也能够进行事件的通知, Zen-Cart设计了一个notifier类, 作为全局的一个通知者. 如果你想创建一个类来监听在程序中(例如页面头部文件中的代码)触发的事件, 就应该将notifier加入myObserver类, 如下面代码所示.
class myObserver extends base { function myObserver() { global $zco_notifier ; $zco_notifier -> attach( $this , array ( ' NOTIFY_HEADER_END_CHECKOUT_CONFIRMATION ' )); }
第5节 包含观察者类文件
值得注意的是”includes/classes/observersdirectory”目录中的文件并不会自动加载, 所以你需要让application_top.php文件能够自动加载myObserver类.在目录” includes/auto_loaders”中增加一个文件”config.freeProduct.php”,文件内容如表格 3‑4 代码所示.
$autoLoadConfig [ 10 ][] = array ( ' autoType ' => ' class ' , ' loadFile ' => ' observers/class.freeProduct.php ' ); $autoLoadConfig [ 90 ][] = array ( ' autoType ' => ' classInstantiate ' , ' className ' => ' freeProduct ' , ' objectName ' => ' freeProduct ' );
第6节 通知者/观察者模式编程实例
有时会遇到的一种需求是, 当客户购买了一定数额的商品后, 如果购买的总金额超过指定的数量, 我们希望给客户一个免费的小赠品.
通过分析后, 可以知道, 当客户增加了商品到购物车后, 会检测是否购买的数量超过了指定的最低消费数量, 则应该自动将该礼品加到客户的购物车, 而如果客户删掉了购物车中的商品, 同样也需要检测, 如果消费总数量已经小于了最低消费数量了, 则应该自动将该礼品从购物车中删除. 按照传统的编程方式, 要实现这个功能也不难, 但是这将在多处对Zen-Cart的核心代码产生破坏. 现在通过ONS, 我们可以通过一个很简单的自定义类来完成这个功能, 并且不会破坏Zen-Cart的核心代码.
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
<? php /* * * Observer class used to add a free product to the cart if the user spends more than $x * */ class freeProduct extends base { /* * * The threshold amount the customer needs to spend. * 客户需要消费的总数 * Note this is defined in the shops base currency, and so works with multi currency shops * * @var decimal */ var $freeAmount = 50 ; /* * * The id of the free product. * 赠品的ID * Note. This must be a true free product. e.g. price = 0 Also make sure that if you don't want the customer * to be charged shipping on this, that you have it set correctly. * * @var integer */ var $freeProductID = 57 ; /* * * constructor method * 构造方法 * Attaches our class to the $_SESSION['cart'] class and watches for 2 notifier events. */ function freeProduct() { $this -> attach( $this , array ( ' NOTIFIER_CART_ADD_CART_END ' , ' NOTIFIER_CART_REMOVE_END ' )); } /* * * Update Method * * Called by observed class when any of our notifiable events occur * * @param object $class * @param string $eventID */ function update( & $class , $eventID ) { if ( $_SESSION [ ' cart ' ] -> show_total() >= $this -> freeAmount && ! $_SESSION [ ' cart ' ] -> in_cart( $this -> freeProductID) ) { $_SESSION [ ' cart ' ] -> add_cart( $this -> freeProductID); } if ( $_SESSION [ ' cart ' ] -> show_total() < $this -> freeAmount && $_SESSION [ ' cart ' ] -> in_cart( $this -> freeProductID) ) { $_SESSION [ ' cart ' ] -> remove( $this -> freeProductID); } if ( $_SESSION [ ' cart ' ] -> in_cart( $this -> freeProductID)) { $_SESSION [ ' cart ' ] -> contents[ $this -> freeProductID][ ' qty ' ] = 1 ; } } } ?>
当然,也许由于开发者也不可能考虑的很周全,有时候在代码中不一定在我们想要的地方都进行了事件通知。遇到这种情况,还是有可能对核心代码做少量修改的。但是通过使用观察者模式,还是可以尽量的避免修改其核心代码。