Magento is designed with a very modular architecture. To follow best practices for changing any of the core
architecture, or adding new architecture, it is best to understand how to properly write configuration xml.

Keep in mind that when extending a class, to override just one function you need only declare that method and populate it with new code.

 

Download Our Right Way To Approach Magento Development Guide PDF

The real advantage in working with Magento this way is creating small, light-weight overrides that create a clear path for future developers to follow. Practicing development in Magento with these techniques also creates the ability to turn each module off. This is very useful during the upgrade & development process, if Magento functionality seems broken, disabling a module’s output ( essentially isolating it from being a part of the application ) is a powerful technique for isolating the source of any given problem.

Prerequisite Knowledge

The first thing that Magento does when Mage::app() is instantiated is load all files with an .xml extension in /app/etc & app/etc/modules. /app/etc is reserved for application configurations, well app/etc/modules is used for module loading. Drilling down into app/etc/modules reveals many .xml files shipped with Magento. All Magento Core modules are prefixed with Magento_. There exists an .xml file for each module of Magento, and there is a matching folder with the same name in /app/code/core/Mage.

Additionally, there is a Mage_All.xml which contains any modules that do not have their own .xml file.

Enterprise ships with more core code than community edition, and enterprise config xml in /app/etc/modules are prefixed Enterprise_.

When coding customizations to Magento there are two approaches, both of which will organize the projects customizations into a clearly defined group.

Method A is to create a project_all.xml file which declares all modules specific to the project

Method B is to create a project_module.xml file for each modules specific to the project

To Declare a module in /app/etc/modules, the following must be included in the .xml document:

<config>

<modules>

<Project_Module>

<active>true</active>

<codePool>local</codePool>

</Project_Module>

</modules>

</config>

In the above .xml project_module represents the path in /app/code/local to the module. i.e. the above code references a module located at /app/code/local/Project/Module

Following the autoloading of .xml that happens in /etc/modules, once your configuration .xml declares your module Magento looks next for configuration .xml in your Project/Module/etc folder. This is typically named config.xml, but again any file ending in .xml will be loaded at run-time.

What do I declare in the module’s confirgutation.xml? This is where any project specific blocks, controllers, models, resource models, resource entities and database tables are declared. There are many more specifics that can be declared in this configuration .xml and we’ll try to cover how to use configuration .xml in-depth

The three common uses of a module’s configuration .xml are two declare the module’s resources & structure, to override & extend core classes, and to create event observer hooks.

Overriding and Extending Core Classes

There are many circumstances where extending or overriding magenta’s core classes is necessary to achieve integrations, implement features specific to a projects business rules or enhance or modify functionality

What’s the difference between extending and overriding? Well when you create class A that extends class B and class B contains a function such as {public function doSomething()} and the project requires adding code to this method, or changing it, declaring it in class A and using config.xml to tell the application to use class A instead of class B will override just one function while maintaining all of the remaining functionality in class B.

Extending happens when methods need to be added to an existing class, and follows the same idea as overriding to extend the class that needs additions.

Okay, that makes sense, so lets look at how to implement this on specific areas of Magento.

Blocks

Blocks are the simplest type of class in Magento to override

<blocks>

<project_module>

<class>Project_Module_Block</class>

</project_module>

<magentomodule> <!– i.e. shipping, or catalog, etc.. ->

<rewrite>

<magento_blockfile>Project_Module_Block_MagentoModule_Folder_File</magento_blockfile>

</rewrite>

</magentomodule>

</blocks>

In the above, <magento_blockfile> is the name, with an underscore instead of a / that leads to the file you are over writing.

The part that reads: Project_Module_Block_MagentoModule_Folder_File tells Magento to load /Project/Module/Block/MagentoModule/Folder/File.php whenever the code would normally call the original block

Helpers

Helpers are fairly easy to rewrite as well, and the following code should reveal how to do so

Configuration xml:

<helpers>

<module>

<class>Project_Module_Helper</class>

</module>

<checkout>

<rewrite>

<data>Project_Module_Helper_Data</data>

</rewrite>

</checkout>

</helpers>

<?php

class Project_Module_Helper_Data extends Mage_Checkout_Helper_Data

{

public function isAllowedGuestCheckout(Mage_Sales_Model_Quote $quote, $store = null)

{

return false;

}

}

Models

Keep in mind that this is a little different than overwriting resource models. Overwriting models is very similar to overwriting blocks.

<models>

<project_module>

<class>Project_Module_Model</class>

</project_module>

<sales>

<rewrite>

<quote_address>Projecy_Module_Model_Sales_Quote_Address</quote_address>

</rewrite>

</sales>

</models>

Resource Models

Resource Models, as controllers have a unique way to be overwritten. This can be a little frustrating at times. The concept is the same, but the syntax is a little different.

There are two types of resource folders in Magento’s module folder. One folder named Resource, the other folder names Mysql4. Mysql4 is deprecated in favor of Resource, but hasn’t completely been removed from the code base. The following configuration with a little adjustment  in syntax will work for either resource convention.

Keep in mind in each module, Models and Resource Models are declared slightly differently.

i.e.:

<models>

<tag>

<class>Mage_Tag_Model</class>

<resourceModel>tag_resource</resourceModel>

</tag>

<tag_customer>

<class>Mage_Tag_Model_Customer</class>

<!– <resourceModel>tag_customer_mysql4</resourceModel> –>

<resourceModel>tag_customer_resource</resourceModel>

</tag_customer>

Notive in the above the deprecated mysql4 resource which in this case has been deactivated, but the underlying code has not been removed yet.

Here is the technique for overwriting resource models:

<models>

<project_module>

<class>Project_Module_Model</class>

</project_module>

<tag_resource>

<rewrite>

<customer>Project_Module_Model_Tag_Resource_Customer</customer>

</rewrite>

</tag_resource>

</models>

Here is a technique for working with a specific mysql4 resource model. Notice this is an eav resource model as well.

<models>

<project_module>

<class>Project_Module_Model</class>

</project_module>

<catalog_resource_eav_mysql4>

<rewrite>

<attribute>Project_Module_Model_Catalog_Resource_Eav_Mysql4_Attribute</attribute>

</rewrite>

</catalog_resource_eav_mysql4>

</models>

The above two examples are fairly different, in the cad of the tag resource rewrite calling Mage::getResourceModel(‘tag/customer’) will load Collection.php which leverages Magento’s ORM (Object Relational Mapping) system to grab data from the defined table in the config.xml

The second model again relates to Magento’s ORM functionality, but does not reference a Collection.php file directly, instead the Attribute.php file it calls extends:

class Mage_Catalog_Model_Resource_Attribute extends Mage_Eav_Model_Resource_Entity_Attribute

which in turn extends class

Mage_Eav_Model_Resource_Entity_Attribute extends Mage_Core_Model_Resource_Db_Abstract

which in turn extends

abstract class Mage_Core_Model_Resource_Db_Abstract extends Mage_Core_Model_Resource_Abstract

which implements the abstract class

abstract class Mage_Core_Model_Resource_Abstract

It can be nerve racking and brain frying to follow the inheritance chain all the way back, but the faster and more frequently you do so, the better understanding you’ll have of the system as a whole and its Architecture.

A little confusing, yes? We’ll save the next topic for understanding Magento’s design patterns, including the abstract factory pattern, and a few other topics as well as highlighting some of Magento’s main uses of Zend’s functionality and framework patterns

It is important to understand what collections are in Magento and when / where to use them, which again we will highlight in a separate document.

Controllers

Controllers are a little more frustrating until the original controller is included at the top of the class declaration, such as the following over ride of the Customer AccountController.php

<?php

include_once(‘Mage/Customer/controllers/AccountController.php’);

class Project_Module_Checkout_AccountController extends Mage_Checkout_AccountController

{

Whoa what the heck, I didn’t reference the controllers path in the controller class declaration? Again this is a little confusing Magentoism to be aware of.

The configuration .xml is also a little more specific and the exact controller being overridden has to be declared as follows:

<config>

<frontend>

<routers>

<customer>

<args>

<modules>

<Project_Module before=”Mage_Customer”>Project_Module_Customer</Project_Module>

</modules>

</args>

</customer>

</routers>

</frontend>

</config>

In this example let me highlight a key distinction in the class declaration Project_Module_Checkout_AccountController

Notice I included Checkout after the module name just to help make certain that the syntax stood out. In practice the Module name would most likely be Checkout and the class declaration would actually be:

Project_Checkout_AccountController

This would also mean refactoring the above configuration xml at the following line, from:

<Project_Module before=”Mage_Customer”>Project_Module_Customer</Project_Module>

To:

<Project_Module before=”Mage_Customer”>Project_Customer</Project_Module>

Another Magentoism worth pointing out is that any controller is named in camel case NameController.php This is specific to how Magento loads and manages controllers, and when controllers are routed on the front end through the URI such as http://yourdomain.com/customer/account/ It is important to recognize Magento by default calls the method defined as indexAction in the defined controller. Lets say your URI read as follows:http://yourdomain.com/customer/account/login Magento’s front-router design pattern will then look for a method defined in the AccountController named loginAction and execute this method.

Creating Custom Module Classes Following Magento's Design Patterns

Before we start, it is important to understand the arbitrary, or opportunity to give references to implemented classes meaningful names in the configuration xml and how it related to Magento’s application architecture.

<models>

    <arbitraryname>

<class>Project_Module_Model</class>

</arbitraryname>

</models>

 

Ok how does this relate to the application? Anywhere that Mage::getModel(‘arbitraryname/folder_folder_file’) or something similar is called, Magento’s defined autoloader class will essentially perform the following pseudo code:

variable name = new returnedClass();

The other distinction to be aware of is the use of underscore in the reference ‘arbitraryname/folder_folder_file’

The part before the “/” references the name defined in the xml tag <arbitraryname>

The part after the “/” begins looking for a class to autoload in /Project/Module/Module/Model as referenced in: <class>Project_Module_Model</class>

Then the autoloader looks at the last part of ‘folder_folder_file’ i.e. file as a file declaration and looks for Filer.php, if there is anything before that uses an underscore Magento looks for Folder/Folder/File.php

In the following examples I have given the arbitrary name module where the above concept applies.

Also keep in mind that all of the following configuration .xml is nested inside <global></global> to properly register it with Magento’s application

It is important that the chosen name tag is unique, and as such a lot of people prefix it in the following way: project_module

If there is an underscore in this part of the tag it is referenced as such:

Mage::getModel(‘project_module/folder_folder_file’)

Module Declaration

Step back to the beginning where Magento’s parsing of all .xml files in /app/etc/modules, and then in the defined /app/code/local/project/module/etc to process configuration .xml is explained before proceeding.

The following declarations are used in the Modules /etc configuration .xml declarations

Defining the Module’s version

The first part of defining the module is declaring it as follows:

<config>

<modules>

<Project_Module>

<version>0.1.0</version>

</Project_Module>

</modules>

After all the other module configuration .xml is written, of course the closing </config> tag needs to be implemented

A note about version and how it related to the Magento application architecture, and any sql install scripts defined in a particular module.

In the database exists a table called core_resource

In the table are row’s for all loaded modules, there are two fields, one for version number, one for data version number. Should the above configuration .xml version number be higher than the number represented in the core_resource table, Magento will execute any setup scripts to update the version’s sql, or data structure, is database setup is defined. If the core_resource version number matches the configuration version number, Magento will proceed as if it is already up to date.

Blocks 

<blocks>

<module>

<class>Project_Module_Block</class>

</module>

</blocks>

Models  

<models>

<module>

<class>Moo_Catalog_Model</class>

</module>

</models>

Helpers 

<helpers>

<module>

<class>Moo_Catalog_Helper</class>

</module>

</helpers>

Resource Models 

<models>

<module>

<resourceModel>eav_resource</resourceModel>

</module>

</models>

A note about resource models, when referencing Mage::getResourceModel(‘arbitraryname/file’)

This can be the same as calling Mage::getModel(‘arbitraryname/file’)->getCollection()

This is because all Model files in Magento reference a resource model somewhere following Magento’s ORM ( Object Relational Mapping ) design pattern

Controllers 

<frontend>

<routers>

<module>

<use>standard</use>

<args>

<module>Project_Module</module>

<frontName>module</frontName>

</args>

</module>

</routers>

</frontend>

A note about controllers, the <frontName> tag above directly defines the name the URI uses to call the router

And example, when navigating to http://yourdomain.com/customer/account

The /customer/ part of the URI routes the application to look for an AccountController in the above defined location, and must follow the pattern that controllers are kept in the /controllers/ folder in the above defined configuration, which is why another magic Magentoism is that in configuration when defining controllers the /controllers/ folder is not referenced.

Additionally the class declaration of a controller does not contain _controllers_ in the declaration

sql setup declarations 

<module>

<class>Project_Module_Model_Resource</class>

<entities>

<arbitraryname> <!– Matching custom module arbitrary name defining resource model –>

<table>database_table_name</table>

</arbitraryname>

</entities>

</module>

Defining the table that this resource module will use is important, most custom database tables ought to be prefixed with something like project_module_  or project_  or module_ depending on the standard the team chooses for a specific project’s implementation to create a clear to follow convention for future developers

<resources>

<module_setup>

<setup>

<module>Project_Module</module>

<class>Project_Module_Model_Resource_Mysql4_Setup</class>

</setup>

<connection>

<use>core_setup</use>

</connection>

</module_setup>

<module_write>

<connection>

<use>core_write</use>

</connection>

</module_write>

<module_read>

<connection>

<use>core_read</use>

</connection>

</module_read>

</resources>

The are differences of opinion on the about <class> declaration in the part

Resource_Mysql4

Since Mysql4 is deprecated, some prefer just using Resource, and other prefer Resource_Mysql4 for areas that install scripts are running to interact with the database

The part that reads:

<connection>

<use>core_setup</use>

</connection>

Is important and in your install.php files referencing the following:

$installer = $this;

/* @var $installer Mage_Core_Model_Resource_Setup */

$installer->startSetup();

Instantiates the core resource setup object which ultimately instantiates Varian library and Zend library database transaction classes that are very important.

In the second part in the read & write declarations <module> should be named your unique module name.

Installation scripts first should follow the folder structure convention that is commonplace in Magento as follows: /Project/Module/sql/module_setup/

Installation scripts follow a specific naming convention:

module-install-0.1.0.php

Where the first script in the folder’s number matches the version number

Upgrade scripts also follow a specific naming convention

module-upgrade-0.1.0-0.2.0.php

Where the first number is the previous version and the second number is the next version

Magento will run all install scripts and upgrade scripts sequentially until it runs an upgrade script whose second number matches the current <version>0.1.0</version> number defined in the configuration .xml

A Note About Declaration

<entities>

<arbitraryname> <!– Matching custom module arbitrary name defining resource model –>

<table>database_table_name</table>

</arbitraryname>

</entities>

Sometimes a resource model simply extends some other resource model at some level to extend functionality. However, somewhere an entity is declared that helps the resource model map directly to a database table following the Object Relational Mapping pattern as it functions in Magento’s application framework.

Event Listeners & Observers

The Magento Event Architecture is a basic implementation of an Event architecture, and should ring a familiar cord if you’ve seen event architecture in Javascript, Adobe’s Flex, Zend, .NET, or just about any other advanced modern programming language or framework. Of course syntax always varies from language to language, and some of the aforementioned languages have more advanced bubbling systems, or features implemented in their Event Architecture

The Event Architecture follows the Observer Design Pattern.

Magento dispatches events as follows:

Mage::dispatchEvent(‘customer_login’, array(‘user’=>$user));

The first parameter being the event name, the second parameter being data attached to the event that is made available to any event listeners

Magento dispatches many events that are presumed commonplace to eCommerce Architecture and it’s application architecture. For a complete list of Magento Events, reference the Magento Events Cheat Sheet .pdf

Thats great! how do I use the Observer pattern via configuration xml and class implementation ?

First let me provide you with a piece of Mage_Core_Model_Abstract

public function save()

{

$this->_getResource()->beginTransaction();

try {

$this->_beforeSave();

if ($this->_dataSaveAllowed) {

$this->_getResource()->save($this);

$this->_afterSave();

}

$this->_getResource()->commit();

}

catch (Exception $e){

$this->_getResource()->rollBack();

throw $e;

}

return $this;

}

Notice the _beforeSave  & _afterSave

They execute the following events

protected function _beforeSave()

{

Mage::dispatchEvent(‘model_save_before’, array(‘object’=>$this));

Mage::dispatchEvent($this->_eventPrefix.’_save_before’, array($this->_eventObject=>$this));

You can see that each event is actually constructed based on the object being saved. So if we were saving a Mage_Catalog_Model_Product then the event will have:

protected $_eventPrefix      = ‘catalog_product’;

protected $_eventObject      = ‘product’;

This is an example of a core event handler declared in config.xml for study of how binding to an event is executed:

<frontend>

<secure_url>

<checkout_onepage>/checkout/onepage</checkout_onepage>

<checkout_multishipping>/checkout/multishipping</checkout_multishipping>

</secure_url>

<events>

<customer_login>

<observers>

<loadCustomerQuote>

<class>checkout/observer</class>

<method>loadCustomerQuote</method>

</loadCustomerQuote>

</observers>

</customer_login>

<customer_logout>

<observers>

<unsetAll>

<class>checkout/observer</class>

<method>unsetAll</method>

</unsetAll>

</observers>

</customer_logout>

<sales_quote_save_after>

<observers>

<set_checkout_quote_id>

<class>checkout/observer</class>

<method>salesQuoteSaveAfter</method>

</set_checkout_quote_id>

</observers>

</sales_quote_save_after>

</events>

Observer classes typically go in Project/Model/Observer.php

Notice the above configuration .xml defines the method called @ the point of event observation

Here is a more generic version for study of event binding referenced from:http://www.aschroder.com/2010/01/magento-events-explained-and-a-few-gotchas-avoided/

<events>

<admin_session_user_login_success>

<observers>

<some_descriptive_phrase_for_your_listener>

<type>singleton</type>

<class>model_base/class_name</class>

<method>function_name</method>

</some_descriptive_phrase_for_your_listener>

</observers>

</admin_session_user_login_success>

</events>

Implementing The Observer

Right so if you get this far you just need to actually implement the function that will get called, and correctly access any data that has been passed along within the event. That’s what I’ll show you in this next snippet.

public function salesOrderShipmentTrackSaveAfter(Varien_Event_Observer $observer) {

$track = $observer->getEvent()->getTrack();

$order = $track->getShipment()->getOrder();

$order->getShippingMethod();

// …

}

The $observer object get’s populated with variables that can be accessed via get’s and set’s courtesy of Magento’s use of the magic __get and __set – a feature which for me, the jury is still out on. The names of the variables are the keys of the associative array passed in when the event was fired (you were paying attention to that part right?) so the getTrack() function here can be called on the event because the array passed to the event looked like array(‘track’=>$track).