Magento Basics, Request Flow, Standards and Best Practices

Adrian Morutan

Nowadays, companies are migrating their businesses online more and more. They’ve chosen to direct their attention to online communication in order to stay closer to their potential buyers, to make use of social websites to inform people about their products/services and, finally, to find a workaround that provides people with a buying process directly from home, work office or any place with an Internet connection.

One of the solutions for e-commerce that gained trust from lots of companies is Magento. It supports small businesses, but also scales to larger sizes.

Alongside the expansion of e-commerce came across the need for experts to maintain and customize functionalities for their specific shop infrastructure.

In this article we will discuss the initial step that needs to be accomplished by a developer in order to get familiar with Magento’s structure and be ready to start adding custom functionality.

Magento’s Basics

A free version of Magento Community Edition can be downloaded from here, the official Magento website.

Assuming we have already configured a virtual host in a local environment and extracted Magento in the desired folder, before firing the installer, file permissions needs to be set correctly. Here are the steps:

  • set all directories and subdirectories from the application to 775

  • set all files to 644

  • set directory app/etc/ and all its files and subdirectories to 777

  • set directory var/ and all its files and subdirectories to 777

  • set directory media and all its files and subdirectories to 777

In a linux system you can achieve all this by typing the following commands in your Magento folder:

find . -type d -exec chmod 775 {} \;
find . -type f – exec chmod 644 {} \;
chmod 777 -R app/etc/
chmod 777 -R var/
chmod 777 -R media/`

After the installer is done working, file permissions from app/etc should be changed back – directories to 775 and files to 644, for security reasons.

Code pools

Modules are located in app/code/ in the existing code pools: core, community (deprecated) and local.

Each module has a corresponding configuration in an .xml file in app/etc/modules/ and has defined a syntax like <codePool>local</codePool> from which Mage_Core_Model_ConfiggetModuleDir() will point to the module’s path.

Module structure

Block

The files in this folder inherit or load data and transfer it to the templates from your theme (.phtml files).

controllers

Controllers represent the business logic, containing specific actions that are matched for a given request (dispatch(), preDispatch(), postDispatch() methods) and delegate commands to the other parts of the system.

Helper

In the Helper directory you can create class files with utility methods, parsing methods and other generally helpful methods for your store which are commonly used by the whole system. Methods declared in helpers can be called from any template file or block, model or controller. Magento is returning a singleton instance for helper classes.

Controller

In this directory routes can be defined, used to match module-controller-action based on the request, template layers for controller classes, etc. For an example, see the “Paypal” module.

Model

Usually contains classes that correspond to database tables. Models can be used as regular models that use two resource classes (one for reading and one for writing) to communicate with the database, resource models used to prepare query data for database communication, service models used as standalone classes that define a process flow, helper models used as standalone classes with individual methods for computing more complex algorithms than regular Helpers, and so on.

etc

Contains module behavior configurations. Each module must have at least a config.xml file and declare paths for models, blocks, helpers, routers, etc.

sql

Each module can come along with sql installers to prepare database entities. Installers have a group name defined in each module/etc/config.xml and each sql installer has a version for order iteration purposes.

data

Almost the same functional statement as sql installers, but with a different scope: will not create/update table schemes and attributes, instead it will update existing records or populate database tables with default records.

doc

A folder dedicated to documentation and module explanation.

Templates and Layout

The layout in Magento is kept in app/design/ and has a well defined structure for default and custom theming. Loading hierarchy within its priority and file existence starts with the custom theme. If not found, it will search the .phtml file in default theme and if still not found, the last search path will be the base theme.

Magento establishes the theming structure in high-level areas such as adminhtml (system administration templates), frontend (the visible template for an end-user), installer (templates for the helper system that will automatically configure the shop).

Each theme has a folder ‘layout’ with .xml files (usually each module has its own referred layout configuration file) that defines a block of content for a controller action.

The custom theme may also contain a folder called ‘locales’ that will keep a file ‘translate.csv’ where all the custom translations are kept or even override existing ones already defined in standard Magento.

Skin and Javascript

The CSS files, images, and custom JS are located in the directory skin/ and implement the same theming structure and loading priority as for Templates and Layout (skin/area_name/package_name – a custom namespace can be defined or base or default/theme_name).

Class naming conventions

Magento applies the basic, albeit outdated Zend class naming convention and uses Varien_Autoload::register() to autoload classes by replacing the ‘_’ in the name with a directory separator to find the inclusion path to the file. This is very primitive, but due to backwards compatibility could not be solved sooner – Magento 2 will use ZF2, modern PHP and namespaces.

Request Flow

Let’s see how a Magento application handles a request starting from the main index.php file.

Application Initialization

1) If you use Nginx, you should follow the configuration guide. If you’re on Apache, these are the rewrite rules for .htaccess from the main application directory that shouldn’t be missing.

Rewrite rules for skin, media, JS directories to allow loading files and, if not found, to return 404. Everything else is rewritten to index.php:

<IfModule mod_rewrite.c>

############################################
## enable rewrites

    Options +FollowSymLinks
    RewriteEngine on

############################################
## always send 404 on missing files in these folders

    RewriteCond %{REQUEST_URI} !^/(media|skin|js)/

############################################
## never rewrite for existing files, directories and links

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-l

############################################
## rewrite everything else to index.php

    RewriteRule .* index.php [L]

</IfModule>

2) These are the steps the application follows when initialized:

  • run file index.php where app/Mage.php is included

  • call Mage::run($mageRunCode, $mageRunType)

  • in ‘run’, we load Varien Events Collection, the class Mage_Core_Model_Config and the class Mage_Core_Model_App

  • Mage_Core_Model_App::run() is triggered

  • load configuration files from app/etc/modules

  • load configuration xml files inside modules app/code/{code pool}/{module name}/etc/config.xml

  • initialize current store

  • initialize request object

  • run sql and data installers

  • update/create versions of modules in core_resource

  • Front Controller dispatch – Mage_Core_Controller_Varien_Front::dispatch();

  • collect routers from config by Area and Route Code

  • use routes to match Controller

  • if a controller and action will be matched, the controller file will be included, instantiated and action method will be called

  • action method will instantiate and call methods on models, depending on the request

  • the Varien Action Controller will call a Layout Object

  • this Layout Object will create a list of Block objects that are valid for this request, based on some request variables and system properties (also known as “handles”)

  • template files (.phtml) are assigned in the layout xml file for each block.

  • _prepareLayout() method is called where, based on action layout xml structure, specific blocks are instantiated within associated .phtml files

  • _toHtml() method is triggered to generate html

  • output html

Front controller

1) the properties and role of the Front Controller:

  • directory path: app/code/core/Mage/Core/Controller/Varien/Front.php

  • base role: receive all requests from the browser and return HTML code

  • Front controller uses routes of the system to match a controller class and its action method

  • default Magento routers are: Standard (class Mage_Core_Controller_Varien_Router_Standard), Admin (class Mage_Core_Controller_Varien_Router_Admin), Default (class Mage_Core_Controller_Varien_Router_Default)

Here’s is an example of a defined route in the Customer module’s /etc/config.xml:

<routers>
    <customer>
        <use>standard</use>
        <args>
            <module>Mage_Customer</module>
            <frontName>Customer</frontName>
        </args>
    </customer>
</routers>

2) events that Front Controller triggers:

  • controller_front_init_before

  • controller_front_init_routers

  • controller_front_send_response_before

  • controller_front_send_response_after

URL rewrites

URL structure/processing in Magento.

A link in Magento has the below structure:

https://user:password@host:443/{base_path}/{base_script}/{storeview_path}/{module_front_name}/{controller_name}/{action_name}/{param1}/{value1}?{
query_param=query_value#fragment}

  • user:password@host:443/{base_path}/{base_script}: the path to the Script file which runs Magento. Usually, it is an index.php file or a path that uses access rewrite rules to index.php

  • {storeview_path}: store view code will be used if the website has a multi-store-view model (e.g. multi-language support that is marked in the url)

  • {module_front_name}/{controller_name}/{action_name}: the path to match a module controller and action method

  • {param1}/{value1}: name and value of a parameter sent by url

  • ?query_param=query_value#fragment: parameters query sent via the standard GET request

The above link will traverse through the following files, each contributing to match a schema for url and call the correct module-controller-action to output content:

  • app/Mage.php (Mage::app()->run())

  • app/code/core/Mage/Core/Model/App.php

  • Init and Dispatch controller $this->getFrontController()->dispatch();

  • app/code/core/Mage/Core/Controller/Varien/Front.php

  • choose the defined router from config.xml to match module-controller-action $router->match($this->getRequest())

    • app/code/core/Mage/Core/Controller/Varien/Router/Admin.php
    • app/code/core/Mage/Core/Controller/Varien/Router/Standard.php
    • app/code/core/Mage/Core/Controller/Varien/Router/Default.php
  • app/code/core/Mage/Core/Controller/Varien/Action.php

  • call Action method (example: indexAction())

URL rewrite process

Url is rewritten in the following cases:

  • core url rewrite: having priority over routers, will try to translate a custom request path to standard module/controller/action path based on the defined rows in database (core_url_rewrite table)

  • module name rewrite: a front name will be defined in a module’s config.xml to rewrite the actual path and name (example: Mage_Customer will be matched by url through using its defined front name ‘customer’)

  • custom routers will rewrite the url (mostly used for SEO purposes): for the default Magento an example will be the CMS module that will match a url-identifier like homepage.html to module ‘Mage_Cms’, controller ‘Page’, action ‘View’ and a parameter id that will match the cms entity row in the database.

Standards and Best Practices

Sample configuration xml

This is a sample of module config.xml (path: {base_app_folder}/app/local/{module_name}/etc/config.xml) that includes the main configuration:

<?xml version="1.0"?>
<config>
    <modules>
        <{{localnamespaceC}}_{{modulenameC}}>
            <version>0.1.0</version>
        </{{localnamespaceC}}_{{modulenameC}}>
    </modules>
    <global>
        <models>
            <{{localnamespaceL}}_{{modulenameL}}>
                <class>{{localnamespaceC}}_{{modulenameC}}_Model</class>
            </{{localnamespaceL}}_{{modulenameL}}>
        </models>
        <resources>
            <{{localnamespaceL}}_{{modulenameL}}_setup>
                <setup>
                    <module>{{localnamespaceC}}_{{modulenameC}}</module>
                </setup>
                <connection>
                    <use>core_setup</use>
                </connection>
            </{{localnamespaceL}}_{{modulenameL}}_setup>
        </resources>

        <blocks>
            <{{localnamespaceL}}_{{modulenameL}}>
                <class>{{localnamespaceC}}_{{modulenameC}}_Block</class>
            </{{localnamespaceL}}_{{modulenameL}}>
        </blocks>

        <helpers>
            <{{localnamespaceL}}_{{modulenameL}}>
                <class>{{localnamespaceC}}_{{modulenameC}}_Helper</class>
            </{{localnamespaceL}}_{{modulenameL}}>
        </helpers>
    </global>
    <frontend>
        <routers>
            <{{localnamespaceL}}_{{modulenameL}}>
                <use>standard</use>
                <args>
                    <module>{{localnamespaceC}}_{{modulenameC}}</module>
                    <frontName>{{modulenameL}}</frontName>
                </args>
            </{{localnamespaceL}}_{{modulenameL}}>
        </routers>
        <layout>
            <updates>
                <{{localnamespaceL}}_{{modulenameL}} module="{{localnamespaceC}}_{{modulenameC}}">
                   <file>{{modulenameL}}.xml</file>
                </{{localnamespaceL}}_{{modulenameL}}>
            </updates>
        </layout>
    </frontend>
</config>

Legend:

  • localnamespaceC and localnamespaceL is the name of your chosen namespace directory in app/local/{namespace} (C is for uppercase first letter of the word, and L for lower case)

  • modulenameC and modulenameL is the name of your module in app/local/{namespace}/{modulename}

Magento’s factory methods

Magento makes use of factory methods to instantiate models, helpers and blocks. The methods are:

  • Mage::getModel('{module}/{path to file in model directory}') – returns an instance of a class in the Model directory

  • Mage::helper('{module}/{path to file in helper directory}') – returns a singleton instance of a class in the Helper directory

  • Mage::getSingleton('{module}/path to file in model directory') – returns a singleton instance of a class in the Model directory

  • Mage::getResourceModel('{module}/{path to file in model/resource directory}') – returns an instance of a class in the Model/Resource directory.

  • Mage::getBlockSingleton('{module}/{path to file in block directory}') – returns a singleton instance of a class in the Block directory after the layout for a controller action was initialized.

Rewriting the core

Magento’s architecture offers several ways to rewrite core files. Let’s see what they are and list some pros and cons:

a) rewrite a file by creating a similar directory path and file name in the ‘local’ code pool

  • you need to copy entire class with all its properties even if one line of code needs to be changed (downside)

  • if the file is also overridden in a custom module, the tracking for applied change previously may be lost due to the fact that it doesn’t respect the standard developers are used to.

b) rewrite the file in a Custom Module (local/MyNamespace/MyCustomModule/) and create a subdirectory with the core module name and add the original subdirectory tree (e.g. local/MyNamespace/MyCustomModule/Model/Core/Rewrite.php).

  • to override a file in a custom module name and not using Magento’s module name for this purpose will also confuse developers and the tracking of those changes may be lost by adding another rewrite in a different place. However, by adding module configuration (in app/etc/modules/MyCustomModule.xml) to mark dependency to the Mage_Core module you can give developers a heads up that a file rewrite was made.

c) rewrite a file in a Custom Module with Magento’s module name and preserve path (e.g. local/MyNamespace/Core/Model/Rewrite.php)

  • this rewrite method is the most clear as developers can keep track of changes due to the naming convention.

  • will encourage other developers working on the same side to extend already changed functionality and not override it

  • a small disadvantage for this rewrite method is that the number of modules in a chosen namespace will grow along with each file that is overriden from different Magento modules.

Dependency between modules

a) when extending a Magento core module, dependency between modules needs to be configured

e.g. app/etc/modules/MyNamespace_Customer.xml:

<MyNamespace_Customer>
    <active>true</active>
    <codePool>local</codePool>
    <priority>1</priority>
    <depends>
        <Mage_Customer/>
    </depends>
</MyNamespace_Customer>

b) when creating an sql installer or data installer in the Custom Module that updates the Magento core module entity, a dependency needs to be created

e.g. installer in MyCustomModule:

$installer = $this;
$installer->startSetup();

$sqlQuote = 'ALTER TABLE ' . $this->getTable('sales_flat_quote') .
    ' ADD `is_urgent` TINYINT UNSIGNED NOT NULL DEFAULT 0';

$installer->run($sqlOrder);
$installer->run($sqlQuote);

$installer->endSetup();

e.g. dependency config (sales quote entity is altered, dependency with ‘Sales’ module needs to be added):

<MyNamespace_MyCustomModule>
    <active>true</active>
    <codePool>local</codePool>
    <priority>1</priority>
    <depends>
        <Mage_Sales/>
    </depends>
</MyNamespace_MyCustomModule>

c) Extending/Rewriting a file (model, helper, block, controller) from the Magento core module requires dependency between modules, otherwise the last changed version will be arbitrary and you will lose control over the rewrite.

Module dependencies are important to prioritize order of sql statements, and on a fresh install they will avoid conflicts, crashes or wrong final data.

Conclusion

In this article, I’ve covered some of Magento’s fundamentals that will help developers understand the basic architecture and have a starting point in building their custom functionalities. If you’d like to see specific upgrades to Magento covered, or have use cases you’re curious about, let us know. Stay tuned for some Magento 2 tutorials and explanations as well!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • alanstorm

    Nice article — a few clarifications/expansions off the top of my head (inevitable, given how complicated e-commerce/Magento is)

    1.
    The community code pool isn’t depreciated, but it’s generally reserved
    for modules distributed via Magento Connect, commercial module makers,
    and free form modules swapped around the Magento community.

    2.
    The Block definition here isn’t necessarily wrong, but a better way to
    think about it is Magento layouts are a tree of nested block objects,
    each block rendering many sub-blocks. Block objects are created from the
    block class files in the Block folder, and many blocks (but not all)
    are rendered with phtml templates

    3. The layout XML files are
    actually a “domain specific programming language” (DSL) for
    adding/modifying blocks to/in the Magento Layout mentioned in #2

    4.
    Re: the autoload, it’s important to note that Magneto jigger’s PHP’s
    include_path a bit such that it will first look for a class in local,
    then in community, then core, and then in the root lib/ folder. This
    gives developers the ability to replace class files from core in one of
    the code pools

    5. Re: URL rewrites, there’s also a depreciated
    (but still widely used) ability to do URL rewrites in a module’s
    `config.xml` file. This is similar to the `core_url_rewrite` table, but
    many developers prefer it because it’s easier to distribute rewrites in
    this form

    6. Re: URL rewrites, it’s also possible to configure
    things such that **multiple** modules claim a particular frontname.
    This is most commonly used to add a new page/section to the Magento
    admin that still has the admin frontname

    • http://www.bitfalls.com/ Bruno Skvorc

      Great feedback, thanks!

    • http://www.skymagento.com/ Farhan Islam

      Great and explanatory feedback.

  • http://www.bitfalls.com/ Bruno Skvorc

    If you look carefully, you’ll see the permissions are reverted immediately after that paragraph.

  • http://www.northcutt.com/ Ben Ustick

    Hey Adrian,
    Great article! I just wanted to reach out and let you know that I included your post in the roundup of May’s best Magento content. http://blog.nexcess.net/2014/06/05/roundup-of-mays-best-expressionengine-wordpress-and-magento-content/ Thanks for the nice post.

    Ben

  • Dan Homorodean

    Congrats for a great article, Adi!