Customize Magento using Event/Observer

Last modified by DeBaas on Wed, August 18, 2010 08:00
Source|Old Revisions  

This is an old revision of the document!


NOTICE:

Before you start, make sure the compiler is turned OFF

Since version 1.3+, Magento has a compiler mechanism. If that compiler is active, Magento will try to find the files in the folder includes/src.

There are 2 ways to check if the compiler is off:

1. Backoffice → Tools → Compilation → In the top right corner there are 2 buttons, make sure the first one is labeled ‘Disabled’. If it’s enabled click it.

2. Open file includes/config.php, and comment out both lines ( eg: put a # character in front of the php-codelines )

Once your plugin is tested and finished, you can turn the compiler back on if you wish so.

Overview

Apart from the powerful OOP way of customizing Magento which is overriding methods by subclassing Magento’s core Blocks and Models, there is another way to plug in customizations in key flow areas of your Magento eCommerce shop. Referred to as the Event-Observer methodology, Magento has been programmed to raise events in crucial areas of the flow and handling these events for customizations would keep upgradation a simple task that does not require fiddling around with Magento’s core source code. An example would be the event ‘catalog_product_save_after’ which will be raised by Magento immediately after a product is saved.

Terminology

Event Event is something that occurs in a certain place during a particular sequence flow. Say once a customer completes an order, the flow sequence would be to i. Save the order details ii. Send e-mail confirmation to customer Events may be seeded before or after each of these flow points to introduce custom logic.

Observer Observer is the handler of an event. This listens to any event it is attached to and accordingly handles the event.

Customization - Event Vs Overriding

Simply put, think about overriding existing core logic if you need to completely change or if you need to extend core logic and your new logic is going to be reused elsewhere. Use events if you are fine with existing logic provided by Magento and need to add to the core logic.

Example Usage

This example tries to use the Event-Observer methodology to introduce a percentage discount for each product. Currently Magento supports special price functionality without a % discount. So we would use this opportunity to customize magento to introduce %discount at a product level.

Before starting, the aim is to ensure that the percentage discount is considered for a simple product when a product is displayed. The event is raised in the class Mage_Catalog_Model_Product_Price→getFinalPrice() (Magento 1.3.0 file: app/code/core/Mage/Catalog/Model/Product/Type/Price.php). The event is raised by the line Mage::dispatchEvent(’catalog_product_get_final_price’,array(’product’⇒$product));

The event that we are about to handle is catalog_product_get_final_price which is going to help us add logic to consider the percentage discount.

Step 1

Create a new attribute ‘percent_discount‘.

Attrib Identifier –percent_discount , Scope – Store View , Catalog I/p – Text , Unique Value – No , Values Required – NoInput , Validation –Decimal , Apply to Configurable/All Product Types - Yes

Use in quick search – No , Advanced Search – No , Comparable – No , Visibile on Frontend – Yes , Attribute Label – % Discount

Step 2

Add this new attribute to your attributeset. If your product’s attributeset is ‘default‘, add the new ‘percent_discount’ attribute to this attributeset under “prices” attribute group.

Step 3

Register a new custom local module under name ‘Xyz’. For this create file ‘Xyz.xml’ under directory ‘app/etc/modules/’. File contents are -

  1. <?xml version="1.0"?>
  2. <config>
  3.   <modules>
  4.     <Xyz_Catalog>
  5.       <codePool>local</codePool>
  6.       <active>true</active>
  7.     </Xyz_Catalog>
  8.   </modules>
  9. </config>

Step 4

Register the event with its Observer. Create file ‘config.xml’ under directory ‘app/code/local/Xyz/Catalog/etc/’ with contents as -

  1. <?xml version="1.0"?>
  2. <config>
  3.   <global>
  4.     <models>
  5.         <xyzcatalog>
  6.              <class>Xyz_Catalog_Model</class>
  7.         </xyzcatalog>
  8.     </models>
  9.     <events>
  10.       <catalog_product_get_final_price>
  11.         <observers>
  12.           <xyz_catalog_price_observer>
  13.             <type>singleton</type>
  14.             <class>catalog/price_observer</class>
  15.             <method>apply_discount_percent</method>
  16.           </xyz_catalog_price_observer>
  17.         </observers>
  18.       </catalog_product_get_final_price>     
  19.     </events>
  20.   </global>
  21. </config>

Step 5

Creating the Observer. Create the directory structure - app/code/local/Xyz/Catalog/Model/Price/. Place the php code below in a file by name ‘Observer.php’ in the directory just created.

  1. <?php
  2. class Xyz_Catalog_Model_Price_Observer
  3. {
  4.     public function __construct()
  5.     {
  6.     }
  7.     /**
  8.             * Applies the special price percentage discount
  9.             * @param   Varien_Event_Observer $observer
  10.             * @return  Xyz_Catalog_Model_Price_Observer
  11.             */
  12.     public function apply_discount_percent($observer)
  13.     {
  14.       $event = $observer->getEvent();
  15.       $product = $event->getProduct();   
  16.       // process percentage discounts only for simple products     
  17.       if($product->getSuperProduct() && $product->getSuperProduct()->isConfigurable()) {
  18.       } else {
  19.         $percentDiscount = $product->getPercentDiscount();
  20.  
  21.         if (is_numeric($percentDiscount)) {
  22.           $today = floor(time()/86400)*86400;
  23.           $from = floor(strtotime($product->getSpecialFromDate())/86400)*86400;
  24.           $to = floor(strtotime($product->getSpecialToDate())/86400)*86400;
  25.  
  26.           if ($product->getSpecialFromDate() && $today < $from) {
  27.           } elseif ($product->getSpecialToDate() && $today > $to) {
  28.           } else {
  29.             $price = $product->getPrice();
  30.             $finalPriceNow = $product->getData('final_price');
  31.  
  32.             $specialPrice = $price - $price * $percentDiscount / 100;
  33.  
  34.             // if special price is negative - negate the discount - this may be a mistake in data
  35.             if( $specialPrice < 0) $specialPrice = $finalPriceNow;
  36.                  
  37.             if ($specialPrice < $finalPriceNow)
  38.               $product->setFinalPrice($specialPrice); // set the product final price
  39.           }
  40.         }   
  41.       }         
  42.       return $this;
  43.     }
  44. }

Step 6

NOTE: To get Magento to load the changed local code configuration, one needs to (temporary) disable caching of code configuration. In admin panel, go to System > Cache management and uncheck the Configuration option.

Set the discount on the product. Navigate to the catalog product on the admin login and edit a product. Set the percentage discount for this product (under prices subtab).

Step 7

Navigate to the product details page on the front end and observe that the new discount has taken effect. To be noted here is that, on all other screens where discounted price is required. An example here is the search results screen, where you would need to add this new attribute to the select query search attributes in method Mage_CatalogSearch_Block_Result→_getProductCollection()

  1. $_productCollection= $_productCollection->addAttributeToSelect('percent_discount');

Events

The events list is continually expanding in the Magento core (over 223 in v1.3.2.4 compare to 140 in v1.2.0.2) and extensions can easily add more. 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 app/code/core folder and type

  1. grep -r "dispatchEvent" . > ../events.txt

This will create a file app/code/events.txt containing all the events located in the core.

For a nice shell script check out this blog post on http://www.edmondscommerce.co.uk/blog/magento/magento-events-cheat-sheet-grep-works-for-any-version/

At time of editing the wiki, the script is as follows:


#!/bin/bash

# Find all Magento Events, include file names, line numbers and the preceding 6 lines of code

#Please define these two

ABSOLUTE_PATH_TO_MAGENTO_ROOT=/home/joseph/Projects/Magento/

ABSOLUTE_PATH_TO_OUTPUT_FILE_INC_FILE_NAME= /home/joseph/magentoEvents.txt

#here is the command

find $ABSOLUTE_PATH_TO_MAGENTO_ROOT -name “*.php” | xargs -L10 grep -n -B 6 “dispatchEvent” .> $ABSOLUTE_PATH_TO_OUTPUT_FILE_INC_FILE_NAME

# save this file as magentoevents.sh

# then do command: chmod +x magentoevents.sh

# then to run, do command: ./magentoevents.sh


Below is a list of events from earlier releases.

Event List:

Magento v 1.1.6 Event list :

http://www.magentocommerce.com/wiki/_media/magento_events_1.1.6.xls

Magento v 1.2.0.2 Event list :

http://www.magentocommerce.com/wiki/_media/magento_events_v1.2.0.2.xls

Other Notes

I found that step 4 was wrong, here is how I got it to work

Created: local/Company/Module/Model/Observer.php as class Company_Module_Model_Observer

then I played around with the XML and watched the file it was trying to include ( which with the above instructions was looking for classes called Mage_Company ) Then I used this XML exactly, change EVENT_TO_HOOK and ‘Company’ and ‘company’ and ‘module’, pay attention to casing, bleh

<events>
  <EVENT_TO_HOOK>
    <observers>
      <module>
        <type>singleton</type>
        <class>company_module_model_observer</class>
        <method>methodToCall</method>
      </module>
    </observers>
  </EVENT_TO_HOOK>     
</events>

Fix for Step 4

I’ve just found only one error:

change:

<class>catalog/price_observer</class>

with:

<class>Xyz_Catalog_Model_Price_Observer</class>

it will work fine!




 

Magento 2 GitHub Repository

Magento Job Board - Some sort of tag line goes here

Latest Posts| View all Jobs