- A router
- A controller
- An action
Friday, 9 May 2014
Magento Design Patterns
2) Front Controller
7) Object Pool
9) Lazy Loading
10) Service Locator
Magento its architecture is sometimes deemed overly engineered. If we look at it from a helicopter view, commonly used design patterns are easily spotted.
A software design pattern is a reusable solution to an often occurring problem. This doesn’t mean that software is better if it has more design patterns. Instead, a good software engineer should be able to spot the problem and implement the pattern instead of introducing implementations without purpose. The earlier behavior is leadingly noticeable in Magento, where most if not all design pattern-implementations have a purpose.
Magento represents a PHP system that is built on well thought-out coding principles - reusable design patterns being one of them. In terms of an example of a PHP system, I think it can be considered pretty cutting edge and therefore worth considering from an architectural point of view.
As I understand it, there are many design patterns that are available to the OOP developer. Seeing such patterns being put to use in an open-source system such as Magento allows a developer to view examples of such patterns in real use and in situ, rather than in examples that can sometimes be rather achedemic, and even a little misleading.
As such, I am wondering what patterns, other than the ones I have listed below, Magento programmers have used when developing for Magento.
As a note, I understand that some of these patterns are in place as a consequence of being built on the Zend Framework, MVC / Front Controller being a couple of them,
Magento utilizes a unique MVC pattern, utilizing a DOM based configuration layer. It leverages xml to drive the configuration and actions of the application on top of the regular Model-View-Controller architecture.
Model View Controller, MVC for short, is a design pattern where business, presentation and coupling logic are separated. Magento heavily utilizes XML as templating-logic and HTML mixed with PHP files for its views. Models are backed by Varien’s ORM. Most business logic happens in the models whereas the controllers map the model-data to the views.
Because Magento its views are “fat” – they often contain a lot of logic – it’s not rare that views have an additional PHP class (the Block system) which will help with rendering.
The front controller pattern makes sure that there is one and only one point of entry. All requests are investigated, routed to the designated controller and then processed accordingly to the specification. The front controller is responsible of initializing the environment and routing requests to designated controllers.
Magento has only one point of entry (index.php) which will initialize the application environment (Mage::app()) and route the request to the correct controller.
Let's see how it all works.
Magento leverages the class Mage_Core_Controller_Front_Action. A module's controllers usually extend this base class.
Magento handles requests through URLS in the browser. The front controller helps to process these requests.
A url is broken up into multiple sections. A basic url has the following elements:
A router is defined in your Magento module configuration xml. This is an alias for your module. It is the first section after your base URL.
A controller is a class used to help process your request. It is the second section after your base URL.
An action is the actual method called within your defined class. It is the third section after your base URL.
Here is a basic example of Magento's Front Controller process.
http://www.mymagentosite.com/ - Base URL
/customer/ - Router for the Customer module
/account/ - Account controller class. This is located in the controllers folder of the Customer module and is the file AccountController.php and calls the class Mage_Customer_AccountController.
/login/ - The actual method called within Mage_Customer_AccountController. Whenever a method is called, standard Magento practice is to append the word 'Action' at the end, to differentiate between methods that parse files. The method called here is loginAction().
Anything after the standard router/controller/method call is parsed any additional parameters are parsed as key=>value pairs to be handled with the request.
The Factory Method is used to instantiate classes in Magento. You instantiate a class in Magento by calling an appropriate method passing an abstract name representing a class group followed by a class name. Class groups and their appropriate abstractions are declared in your configuration XML files in your module's /etc/ folder.
$product = Mage::getModel('catalog/product');
Another way to retrieve an instance of a class, is to call Mage::getSingleton(). It accepts a class alias and before returning an instance, it checks the internal registry whether this class has already been instantiated before – this results in a shared instance.
Much like factory class abstraction and class groups in Magento, the Singleton pattern is instantiated for Blocks and Classes just the same.
An example of where this is mandatory is the session storage which should be shared through the code base instead of creating it anew every time.
$category = Mage::getSingleton('catalog/session');
The registry pattern is basically a pattern that allows any object or data to be available in a public global scope for any resource to use.
The registry is often used for transferring data between scopes when they cannot be passed on, otherwise.
This is especially helpful transferring data between Models and Blocks without having to instantiate an entire class and load data.
You can register an object or data with:
After it is registered, you can call it with:
You can also unregister an object at any time with:
$currentCategory = Mage::registry('current_category');
The Prototype pattern in Magento is used as an extension of the Abstract Factory pattern. It ensures that an appropriate subclass is instantiated via appropriate types that are assigned to an object. What does this mean? Basically, it means that whenever you need to get a specific class that is defined via its parent type, the prototype pattern ensures you get the right class that can handle what you need.
A notable example is the Mage_Catalog_Model_Product class which has a getTypeInstance method to retrieve the specific Mage_Catalog_Model_Product_Type with a specific subset of methods and properties not applicable to all products.
For example, the Mage_Downloadable_Model_Product_Type ultimately extends the Mage_Catalog_Model_Product_Type. If you are iterating over an order and want to call a specific method of a downloadable product, you will need to factorize it first with the getTypeInstance method of the original product.
The object pool pattern is simply a box with objects so that they do not have to be allocated and destroyed over and over again. It is a great way to save on memory consumption and compute cycles.
It’s not used a lot in Magento other than for heavy tasks where resources can get limited soon, like importing products. The object pool (managed by Varien_Object_Cache) can be accessed with Mage::objects().
$id = Mage::objects()->save($object);
$object = Mage::objects($id);
The iterator pattern defines that there is a shared way to iterate over a container with objects. In Magento, this is handled by the Varien_Data_Collection which on its turn uses various baked-in PHP classes (like ArrayIterator) for having a more OO-interface to arrays. This ensures that model-collections will always have a common API to iterate over without being dependent of the actual models.
The Iterator Pattern is a design pattern that allows an object traverse through the elements of another class. This allows you to specify an iterator and allow for multiple different sets of data to be passed without changing the underlying structure that allows the iteration.
Lazy Loading is a design pattern that delays the loading of an object until the time that the object is called upon. This results in less resources being used. With Magento, they don't utilize this with objects, but data.
One of the lazy loading behaviors of Magento is that of collections.
If you were to retrieve a collection of products with Mage::getModel('catalog/product')->getCollection(), the database will only be touched when you actually access the collection by, for example, iterating over it or retrieving the count of models found.
Lazy Loading, which means that database access only occurs when strictly necessary. For example:
$productCollection = Mage::getModel('catalog/product')->getCollection();
The database query will not be made until you attempt to access an item in the Collection.
The service locator is a design pattern that allows a user to get a service by encapsulating the process inside an abstraction layer. This allows the user to retrieve the appropriate or best service without knowing what that service is at runtime.
Allows overrides or renamed physical resources (e.g. Classes, DB tables, etc)
The Module Design Pattern is a form of modular programming that emphasizes the grouping of functionality of a program into independent, interchangeable modules.
Anyone familiar with Magento development has stumbled upon the module pattern. It basically defines that different domains are grouped into separate modules which function independent of each other and can be plugged-in to the main system as deemed appropriate. In an ideal situation, an implementation of the module pattern would make sure that each element can be removed or swapped. One of the protagonists of the module pattern in PHP is the Composer package manager.
Though Magento heavily relies on a modular architecture, it’s not modular to the bone. Certain functionality is heavily tied to the core and cannot be easily changed. There is also the heavy usage of the super-global Mage core-class which introduces all sorts of system-wide dependencies not easily overseen.
The observer pattern is where an event listener is set at a certain point during an application's execution. Other components of the application can "hook" into this event listener and execute their code during this point.
Magento its event-driven architecture is a result of an implementation of the observer pattern. By defining observers (or listeners), extra code can be hooked which will be called upon as the observed event fires. Magento uses its XML-data storage to define observers. If an event is fired with Mage::dispatchEvent($eventName, $data), the data storage will be consulted and the appropriate observers for $event will be fired.
Hopefully these 12 design patterns, give you a bit better understanding of the architectural decisions made in Magento. Not all design patterns found in Magento are implemented as defined by the original concepts. This is perfectly fine, because design patterns are there to help with solving common problems as seen fit, instead of deploying exact implementations.
Let me know in the comments if this has helped you........... Enjoy........Happy coding :)...