Friday, 13 July 2012

MCD: The Process and Configuration of Class Overrides in Magento.Register an Observer

In the previous post we have described the following factory methods, used in Magento.

• for blocks Mage_Core_Model_Layout :: createBlock(…)
• for models Mage :: getModel(…) and getResourceModel(…)
• for helpers Mage :: helper(…)

What is it and how can we make use of it?

Let’s take the example of a model. As we mentioned, due to the getModel(‘catalog/product’) method, Magento returns an object of a certain class the way as follows:

1) It defines class name.
2) It creates an object for this class and calls autoloader.
a. It turns class name into Mage/Catalog/Model/Product.php path.
b. Server searches this file automatically among 3 codepools (local, community, core) and in the lib directory.

As a result, Magento loads the first found class according to the order, defined in include_path.

If we want to change basic Magento functionality, here are the options.

To do poorly. We can simply duplicate the file and its path (see the ‘a’ step above) into local codepool. Our class will be loaded from the local folder due to the priority of this codepool in the list, mentioned in include_path. Now make the necessary changes in the copied file.

So why is it bad? Because after copying the entire file, we will most likely miss all changes, made in a new version of the file, when Magento update is released. This method is better to be used for fast and dirty fixes.

To act smart. Inherit this class and describe in it changes only. There is a huge plus, compared to the previous method – readability. After all, we immediately see what was changed. Rewrite hurries to our rescue here. It allows us on the very first define class name stage to replace the name of the existing class with the name of our class (inherited from the original one).

How to describe it

Let’s create a module. In config.xml inside globals -> models, blocks and helpers we can specify our rewrites. For example, to override the standard class of the Mage_Catalog_Model_Product product, you need to use:

            <product> Namespace_Modulename_Model_Myproduct </product>

Principle is the same as in Magento path.

– specify module namespace (catalog – node name can be viewed in module configuration in the section for modules);
– mark that next the list of module rewrites will be described;
– part of the classname following the group classname (Mage_Catalog_Model). A new class name is specified inside, and the class will be used instead of the standard one.

To override the Mage_Adminhtml_Sales_Order_Create class, we will add the following lines in configuration (models section):

        <sales_order_create> Namespace_Modulename_Model_Adminhtml_Order_Create

The substitution is made here:
Mage :: getModel($modelClass, $arguments){
 Mage_Core_Model_Config :: getModelInstance($modelClass, $arguments){
  $className = $this->getModelClassName($modelClass){
   return  $this->getGroupedClassName('model', $modelClass)
  $obj = new $className($arguments);

getGroupedClassName makes search in rewrites, described in the layout. What will happen if we describe several rewrites for a single class? The first found will be executed. Peep inside a previous post Module Conflicts to find out more about such cases.

Creating rewrite for Blocks and Helpers is similar. We also have another integral part of Magento MVC – controllers. How can we change the functionality of a standard controller?


To override the controller, we add a new router to config.xml of the module (expect to read more about router in the future posts). In brief, the pattern below helps establish the connection between router (used in the url) and a particular module.

Creating modules, we normally set the connection between our router and a module. In this case we override the connection of existing router with our module.
                        <namespace_modulename before="Prevnamespace_Prevmodulename">

Now we create a controller with the same name in our module like this:
require_once 'Prevnamespace/Prevmodulename/controllers/FilenameController.php';
class Namespace_Modulename_FilenameController extends Prevnamespace_Prevmodulename_FilenameController

Prevnamespace, Prevmodulename – name space and modul name, where we need to override the controller.
Filename – the name of an overridable controller
Namespace, Modulename- name space and module name with an extended controller.

Rewrites may certainly be inevitable. But they cause modules conflicts and problems with updates. How do we pull ourselves through this? Let’s have a look at a very classy way to affect Magento.

Events in Magento

Observer pattern, implemented in Magento, allows inserting additional actions at given moments of code execution.

This pattern helps developing independent modules that embed their functionality with the help of events. It means it will be way more preferable to find an “observer” instead of rewriting the class. This observer will allow executing events at a certain time, influencing some kind of data. How is it realized?

Look for the mentioned notes in the code
Mage::dispatchEvent(‘event_name’, $event_arguments);

This note is an “observer” that allows executing methods, attached to it, transferring $event_arguments data array into methods.

For example, there is a great “observer” in the Mage_Core_Model_Abstract class, which allows impacting any object after it has been saved in database as far as models inherit Mage_Core_Model_Abstract.
protected function _afterSave()
 Mage::dispatchEvent($this->_eventPrefix.'_save_after', $this->_getEventData()); ...

Let’s have a look at the Mage_Catalog_Model_Product model. After saving the product, the described method will be called.
$this->_eventPrefix = 'catalog_product';
$this->_getEventData()= array(
 'data_object' => $this,
 ‘product’     => $this,

Here is what we get:
Mage::dispatchEvent(‘catalog_product_save_after’, array(…, ‘product’ => $this));

Events that will be called are described in config.xml via layout. In our case:

(In this case I announce the event for area adminhtml, but events may also be described both by global and, of course, frontend).

class Namespace_Modulename_Model_Observer
    public function methodName(Varien_Event_Observer $observer)

We can get the data of the array, transferred in dispatchEvent method, via $observer object. In our example array(…, ‘product’ => $this) is transferred. To receive the element of the product array, we should use $observer->getEvent()->getProduct().

Together with other events we are free to use the ones, formed automatically and individually for your module and for models and controllers, created in it.

• Models



* – protected $_eventPrefix = ‘…’
Specified individually in a created model

• Controllers


(described in a controller, it mostly concerns blocks)

* – RouteName
** – RouteName_ControllerName_ActionName

Using Observers, described in Magento core, is a very good habit. Remember to create Observers in the main locations of your modules. It will help developers use and customize extensions in their projects easily.


No comments :

Post a Comment