User:Wilt

From Zen Cart(tm) Wiki
Revision as of 23:06, 15 March 2006 by Wilt (talk | contribs)
Jump to: navigation, search

dump of phpdoc docbooc for init system tutorial


DOCS FOR OBSERVER/NOTIFIER CLASSES

Introduction

One of the many goals of the Zen-Cart project has always been to make it easier to for 3rd party developers to add functionality to the core code in an easy and unobtrusive manner. To do this we have in the past relied on the override and auto inclusion systems. However these still do not give developers an easy method of hooking into many areas of core code, without 'hacking' core files themselves.

The observer/notifier system was introduced to give developers unprecendented access to core code, with out the need to touch any core files at all. Although ostensibly written for an object oriented code base, we will see later how it can also be used with general procdural code as well.

Extending all Classes

In order to implement the observer/notifier system, some structural changes have been made to Zen Cart. Firstly two new classes have been introduced, the base class (class.base.php) and the notifier class (class.notifier.php).

The base class contains the code that is used to implement the observer/notifier system. However to make it effective all other Zen Cart classes now have to be declared as children of the base class. You will see this if you look at the source of any of the Zen Cart classes.

  class currencies extends base {


The notifier class will be discussed later, when we look at extending the obsever/notifier system(ONS) into procedural code.

Notifiers, Big Brother is watching

So, what is all the fuss about. The point of the ONS is that developers can write code that wait for certain events to happen, and then when they do, have their own code executed.

So, how are events defined, where are they triggered.

Events are triggered by code added to the core for v1.3 (with more to be added over time). In any class that wants to notify of an event happening we have added.

 $this->notify('EVENT_NAME');

an example would probably help here.

In the shopping cart class after an item has been added to the cart this event is triggered

 $this->notify('NOTIFIER_CART_ADD_CART_END');


There are many other events that have notifiers in Zen Cart v1.3.0, for a list see ++here++

All of this notifying is all well and good, but how does this help developers.

Observe and Prosper

To take advantage of notifiers, developers need to write some code to watch for them. Observers need to be written as a class. Theres even a nice directory includes/classes/observers where developers can put these classes.

Lets take an example. Using the notifier mentioned above (NOTIFIER_CART_ADD_CART_END), how would I write a class that watched for that event.

 <?php
 class myObserver extends base {
   function myObserver() {
     $this->attach('NOTIFIER_CART_ADD_CART_END');
   }
...
 }

As you can see we have defined a new class called myObserver and in the constructor function for that class (function myObserver) have attached this myObserver class to the event NOTIFIER_CART_ADD_CART_END.

Fine, I here you saying, but how do I actually doing any thing useful.

Ok, good question. Whenever an event occurs, the base class looks to see if any other observer class is whatching that event, if it is the base class executes a method in that observer class. Remember the $this->notify('EVENT_NAME') from above. Well when that event occurs the base class calls the update method of all observers. Lets see some more code.

 <?php
 class myObserver extends base {
   function myObserver() {
     $this->attach('NOTIFIER_CART_ADD_CART_END');
   }
   function update(&$callingClass. $notifier, $paramsArray) {
     ... do some stuff
   }
 }

Now, whenever the NOTIFIER_CART_ADD_CART_END occurs, our myObserver::update method will be executed.

some notes about the parameters. As you can see the update method is passed 3 parameters. These are

  • &$callingClass - This is a reference to the class in which the event occurred. allowing you access to that classes variables
  • $notifier - the name of the notifier that triggered the update (it is quite possible to observer more than one notifier.)
  • $paramsArray - Not Used (for future)

To tie this all together, lets look at a real world example.

A Real World Example

One of the often requested features, is the abilty for the store to automatically add a free gift to the Shopping Cart if the customers spends over a certain amount. The code has to be intelligent. It not only has to add the free gift, when the shopper spends over a certain amount, but also remove the gift if the users changes the contents of the shopping cart such that the total falls below the threshold.

Traditionally, although the code for this is not particularly difficult, it would have meant 'hacking' the core shoppingCart class in a number of places. With the ONS, this can be achieved with one very small custom class, and absolutley no hacking whatsoever.

Heres the code.

<?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.
   * 
   * Note. This must be a true free product. e.g. price = 0 also make sure tht 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() {
    $_SESSION['cart']->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;
    }
  }
}
?>

First, I have set the options for the system in the class itself. This is obviously a bad idea, and it would be much better to have an admin module to set these options.

Second, notice that we are actually watching for 2 events in the one class.

   $_SESSION['cart']->attach($this, array('NOTIFIER_CART_ADD_CART_END', 'NOTIFIER_CART_REMOVE_END'));

so we are watching for the NOTIFIER_CART_ADD_CART_END and NOTIFIER_CART_REMOVE_END of the shopping cart class.

The update class is extemely simple, but in its simplicity, manages to do all the work wwe require of it. It first tests to see if the total in the cart is over the threshold, and if it hasn't already, adds the free product to the cart. It then tests to see if the cart total has dropped below the threshold, and if the free product in the cart, removes it.

Now that was cool, how about something a little more difficult.

Another Real World Example

Again we return to the Shopping Cart and promotions. Another oft requested feature is the BOGOF promotion, or Buy One Get One Free. This is a little more difficut to achieve than our previous example, as there is some manipulation needed of the cart totals. However as you will see it is still pretty much a breeze.


<?php
/**
 * Observer class used apply a Buy One Get One Free(bogof) algorithim to the cart
 *
 */
class myBogof extends base {
  /**
   * an array of ids of products that can be BOGOF.
   *
   * @var array
   */
  var $bogofsArray = array(10,4); //Under Siege2-Dark Territory & The replacement Killers
  /**
   * Integer number of bogofs allowed per product
   * 
   * For example if I add 4 items of product 10, that would suggest that I pay for 2 and get the other 2 free.
   * however you may want to restrict the customer to only getting 1 free regardless of the actual quantity
   *
   * @var integer
   */
  var $bogofsAllowed = 1;
  /**
   * constructor method
   * 
   * Attaches our class to the $_SESSION['cart'] class and watches for 1 notifier event.
   */
  function myBogof() {
    $_SESSION['cart']->attach($this, array('NOTIFIER_CART_SHOW_TOTAL_END'));
  }
  /**
   * Update Method
   * 
   * Called by observed class when any of our notifiable events occur
   * 
   * This is a bit of a hack, but it works. 
   * First we loop thru each product in the bogof Array and see if that product is in the cart.
   * Then we calcualte the number of free items. As it is buy one get one free, the number of free items
   * is equal to the total quantity of an item/2.
   * Then we have to hack a bit(would be nice if their was a single cart method to return a products in cart price)
   * we loop thru the cart till we find the bogof item, and get its final price
   * calculate the saving, and adjust the cart total accordingly.
   *
   * @param object $class
   * @param string $eventID
   */
  function update(&$class, $eventID) {
    $cost_saving = 0;
    $products = $_SESSION['cart']->get_products();
    foreach ($this->bogofsArray as $bogofItem) {
      if ($_SESSION['cart']->in_cart($bogofItem)) {
        if (isset($_SESSION['cart']->contents[$bogofItem]['qty']) && $_SESSION['cart']->contents[$bogofItem]['qty'] > 1) {
          $numBogofs = floor($_SESSION['cart']->contents[$bogofItem]['qty'] / 2);
          if ($numBogofs > $this->bogofsAllowed) $numBogofs = $this->bogofsAllowed;
          if ($numBogofs > 0) {
            for ($i=0, $n=sizeof($products); $i<$n; $i++) {
              if ($products[$i]['id'] = $bogofItem) {
                $final_price = $products[$i]['final_price'];
                break;
              }
            }
            $cost_saving .= $final_price * $numBogofs;
          }
        }
      }
    }
    $_SESSION['cart']->total -= $cost_saving;
  }
}
?>

NB. There are still some weaknesses here. First although the adjust total is correctly showm on the shopping cart page and sidebox. the line total is not adjusted. Secondly this will probably producee a confusling output at checkout. Have not tested for tax compliance yet @TODO


Notifiers currently set in v1.3.0

Notifier points for Zen Cart 1.3.0

Shopping Cart class

  • NOTIFIER_CART_INSTANTIATE_START
  • NOTIFIER_CART_INSTANTIATE_END
  • NOTIFIER_CART_RESTORE_CONTENTS_START
  • NOTIFIER_CART_RESTORE_CONTENTS_END
  • NOTIFIER_CART_RESET_START
  • NOTIFIER_CART_RESET_END
  • NOTIFIER_CART_ADD_CART_START
  • NOTIFIER_CART_ADD_CART_END
  • NOTIFIER_CART_UPDATE_QUANTITY_START
  • NOTIFIER_CART_UPDATE_QUANTITY_END
  • NOTIFIER_CART_CLEANUP_START
  • NOTIFIER_CART_CLEANUP_END
  • NOTIFIER_CART_COUNT_CONTENTS_START
  • NOTIFIER_CART_COUNT_CONTENTS_END
  • NOTIFIER_CART_GET_QUANTITY_START
  • NOTIFIER_CART_GET_QUANTITY_END_QTY
  • NOTIFIER_CART_GET_QUANTITY_END_FALSE
  • NOTIFIER_CART_IN_CART_START
  • NOTIFIER_CART_IN_CART_END_TRUE
  • NOTIFIER_CART_IN_CART_END_FALSE
  • NOTIFIER_CART_REMOVE_START
  • NOTIFIER_CART_REMOVE_END
  • NOTIFIER_CART_REMOVE_ALL_START
  • NOTIFIER_CART_REMOVE_ALL_END
  • NOTIFIER_CART_GET_PRODUCTS_START
  • NOTIFIER_CART_GET_PRODUCTS_END
  • NOTIFIER_CART_SHOW_TOTAL_START
  • NOTIFIER_CART_SHOW_TOTAL_END
  • NOTIFY_CART_USER_ACTION
  • NOTIFY_HEADER_START_SHOPPING_CART
  • NOTIFY_HEADER_END_SHOPPING_CART

Order Class:

  • NOTIFY_ORDER_PROCESSING_STOCK_DECREMENT_BEGIN
  • NOTIFY_ORDER_PROCESSING_STOCK_DECREMENT_END
  • NOTIFY_ORDER_PROCESSING_CREDIT_ACCOUNT_UPDATE_BEGIN
  • NOTIFY_ORDER_PROCESSING_ATTRIBUTES_BEGIN
  • NOTIFY_ORDER_PROCESSING_ONE_TIME_CHARGES_BEGIN


Email:

  • NOTIFY_EMAIL_AFTER_EMAIL_FORMAT_DETERMINED
  • NOTIFY_EMAIL_BEFORE_PROCESS_ATTACHMENTS
  • NOTIFY_EMAIL_AFTER_PROCESS_ATTACHMENTS
  • NOTIFY_EMAIL_AFTER_SEND (individual email)
  • NOTIFY_EMAIL_AFTER_SEND_ALL_SPECIFIED_ADDRESSES (full batch)


Modules:

  • NOTIFY_MODULE_START_META_TAGS
  • NOTIFY_MODULE_END_META_TAGS

Individual Pages (Header scripts):

  • NOTIFY_HEADER_START_ACCOUNT_HISTORY
  • NOTIFY_HEADER_END_ACCOUNT_HISTORY
  • NOTIFY_HEADER_START_CHECKOUT_CONFIRMATION
  • NOTIFY_HEADER_END_CHECKOUT_CONFIRMATION
  • NOTIFY_HEADER_START_CHECKOUT_PAYMENT
  • NOTIFY_HEADER_END_CHECKOUT_PAYMENT
  • NOTIFY_HEADER_START_CHECKOUT_SHIPPING_ADDRESS
  • NOTIFY_HEADER_END_CHECKOUT_SHIPPING_ADDRESS
  • NOTIFY_HEADER_START_CHECKOUT_SUCCESS
  • NOTIFY_HEADER_END_CHECKOUT_SUCCESS
  • NOTIFY_HEADER_START_CREATE_ACCOUNT
  • NOTIFY_FAILURE_DURING_CREATE_ACCOUNT
  • NOTIFY_LOGIN_SUCCESS_VIA_CREATE_ACCOUNT
  • NOTIFY_HEADER_END_CREATE_ACCOUNT
  • NOTIFY_MAIN_TEMPLATE_VARS_START_DOCUMENT_GENERAL_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_PRODUCT_TYPE_VARS_DOCUMENT_GENERAL_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_EXTRA_DOCUMENT_GENERAL_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_END_DOCUMENT_GENERAL_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_START_DOCUMENT_GENERAL_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_END_DOCUMENT_GENERAL_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_START_DOCUMENT_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_PRODUCT_TYPE_VARS_DOCUMENT_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_EXTRA_DOCUMENT_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_END_DOCUMENT_PRODUCT_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_START_DOCUMENT_PRODUCT_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_END_DOCUMENT_PRODUCT_INFO
  • NOTIFY_HEADER_START_DOWNLOAD
  • NOTIFY_DOWNLOAD_VIA_SYMLINK___BEGINS
  • NOTIFY_DOWNLOAD_WITHOUT_REDIRECT___COMPLETED
  • NOTIFY_DOWNLOAD_WITHOUT_REDIRECT_VIA_CHUNKS___COMPLETED
  • NOTIFY_HEADER_END_DOWNLOAD
  • NOTIFY_HEADER_START_GV_FAQ
  • NOTIFY_HEADER_END_GV_FAQ
  • NOTIFY_HEADER_START_GV_SEND
  • NOTIFY_HEADER_END_GV_SEND
  • NOTIFY_HEADER_START_INDEX
  • NOTIFY_HEADER_END_INDEX
  • NOTIFY_HEADER_START_INDEX_MAIN_TEMPLATE_VARS
  • NOTIFY_HEADER_INDEX_MAIN_TEMPLATE_VARS_RELEASE_PRODUCT_TYPE_VARS
  • NOTIFY_HEADER_END_INDEX_MAIN_TEMPLATE_VARS
  • NOTIFY_HEADER_START_LOGIN
  • NOTIFY_LOGIN_SUCCESS
  • NOTIFY_LOGIN_FAILURE
  • NOTIFY_HEADER_END_LOGIN
  • NOTIFY_HEADER_START_LOGOFF
  • NOTIFY_HEADER_END_LOGOFF
  • NOTIFY_HEADER_START_EZPAGE
  • NOTIFY_HEADER_END_EZPAGE
  • NOTIFY_HEADER_START_PAGE_NOT_FOUND
  • NOTIFY_HEADER_END_PAGE_NOT_FOUND
  • NOTIFY_HEADER_START_PASSWORD_FORGOTTEN
  • NOTIFY_HEADER_END_PASSWORD_FORGOTTEN
  • NOTIFY_MAIN_TEMPLATE_VARS_START_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_PRODUCT_TYPE_VARS_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_EXTRA_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_END_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_START_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_END_PRODUCT_FREE_SHIPPING_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_START_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_PRODUCT_TYPE_VARS_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_EXTRA_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_END_PRODUCT_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_START_PRODUCT_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_END_PRODUCT_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_START_PRODUCT_MUSIC_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_PRODUCT_TYPE_VARS_PRODUCT_MUSIC_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_EXTRA_PRODUCT_MUSIC_INFO
  • NOTIFY_MAIN_TEMPLATE_VARS_END_PRODUCT_MUSIC_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_START_PRODUCT_MUSIC_INFO
  • NOTIFY_PRODUCT_TYPE_VARS_END_PRODUCT_MUSIC_INFO
  • NOTIFY_HEADER_START_SITE_MAP
  • NOTIFY_HEADER_END_SITE_MAP
  • NOTIFY_HEADER_START_UNSUBSCRIBE
  • NOTIFY_HEADER_END_UNSUBSCRIBE


PayPal:

  • NOTIFY_PAYMENT_PAYPAL_RETURN_TO_STORE
  • NOTIFY_PAYMENT_PAYPAL_CANCELLED_DURING_CHECKOUT
  • NOTIFY_PAYMENT_PAYPAL_INSTALLED
  • NOTIFY_PAYMENT_PAYPAL_UNINSTALLED

Sideboxes:

  • NOTIFY_SIDEBOX_START_EZPAGES_SIDEBOX
  • NOTIFY_SIDEBOX_END_EZPAGES_SIDEBOX
  • NOTIFY_HEADER_START_EZPAGES_HEADER
  • NOTIFY_HEADER_END_EZPAGES_HEADER
  • NOTIFY_FOOTER_START_EZPAGES_FOOTER
  • NOTIFY_FOOTER_END_EZPAGES_FOOTER


Search:

  • NOTIFY_HEADER_START_ADVANCED_SEARCH_RESULTS
  • NOTIFY_HEADER_END_ADVANCED_SEARCH_RESULTS
  • NOTIFY_SEARCH_COLUMNLIST_STRING
  • NOTIFY_SEARCH_SELECT_STRING
  • NOTIFY_SEARCH_FROM_STRING
  • NOTIFY_SEARCH_WHERE_STRING
  • NOTIFY_SEARCH_ORDERBY_STRING

DOCS FOR EXTENSIBLE SHOPPING CART ACTIONS

Explain here the init_cart-handler.php code for abstracting cart actions and the extra_cart_actions override directory.

Note also the new methods in the shopping cart class

  • actionUpdateProduct
  • actionAddProduct
  • actionBuyNow
  • actionMultipleAddProduct
  • actionNotify
  • actionNotifyRemove
  • actionCustomerOrder
  • actionRemoveProduct