Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

PDF and EPUB Versions of Magento Product Types: Developer’s Guide

I am happy to announce that my book Magento Product Types: Developer’s Guide is now available as PDF and EPUB on Leanpub! I have long been promising a version other than Kindle, and now there it is:


Leanpub offers a .mobi (Kindle) version as well. I still recommend getting the Kindle-formatted book from Amazon, which I have carefully tested. The Amazon links are in this post: Magento Product Types: Developer’s Guide

Your comments and Magento Questions

One of the most rewarding parts of blogging is getting comments and being asked questions on my work. Unfortunately recently I’ve been unable to quickly respond to your comments and give advice on your Magento problems. Currently I am deeply engaged in a startup and this takes all my time. For this reason I must recommend you to post questions on Magento StackExchange – the community there will help you faster and better than I ever could. I’m sorry, I’m not as available to my readers as I wished I would.

Adding Custom Options to Products in Magento

In my post A Magento File Custom Option Type Primer I’ve talked about how file custom product options are handled in Magento. One of the readers posted a comment there asking if there was an alternative to manually adding custom options to existing products. For such a task I can recommend MAGMI, Magento Mass Importer, which was used in one of my projects to import product data including custom options. Another solution would be writing a script which could dynamically add custom options to a set of existing products. In this post I will show you how to create such script.

First, let’s see how custom option data are written to database when a product is saved. The product model class Mage_Catalog_Model_Product has a protected method _afterSave which is called, as the name suggests, after a product model is saved.

Continue reading

Quick Tip: Magento Catalog Price Rules Don’t Work

In this post you’ll find a list of likely reasons to why your catalog price rules do not have any effect on prices of products they are supposed to control. The problems listed below are most likely to occur without being caused by a custom module, i.e. it is assumed that catalog price rule functionality has not been changed. The reasons are not mutually exclusive and it is possible that more than one applies to your case. If you, however, find a reason not listed here, please post it in the comment section. This would be much appreciated. So, to the list:

Continue reading

Magento Global Functions normally includes the names of a controller and its action (command). The path can be first checked for a URL-rewrite, for example, in web applications that use human- or SEO-friendly URLs. In this case the routing information is extracted from a resource, which contains URLs matched to controller and action names. Once the controller is found, the command dispatcher instantiates it and calls the matched action.

Magento also features a front controller. If you take a look into the root folder of your application you find only a handful of *.php files and among them index.php.
In Magento index.php is the entry point for every incoming request. This is ensured by Apache’s module mod_rewrite and rewrite rules in the root folder’s .htaccess file:

## 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]

These rules mean that unless a physical file or directory can be located, the request will be sent to the index.php file.

However, index.php is not a front controller. Magento’s front controller is implemented in a class that is instantiated in the early stages of the request processing. Before that the index.php instantiates the main Magento hub class, Mage. The application is started with the calling of the Mage::run() function. After loading configuration, establishing a database connection, and performing other common routines, the front controller is launched. The front controller is implemented in class Mage_Core_Controller_Varien_Front. Its centerpiece is the Mage_Core_Controller_Varien_Front::dispatch function, which is responsible for matching a URL and dispatching the request. But first, the front controller has to be initialized.

The Mage_Core_Controller_Varien_Front::init function does the following

  • Dispatches an event controller_front_init_before.
  • Gets the current store’s router configuration (under the node stores/default/web/routers, details). By this time Magento has already loaded all the module config XML files (more on extending the Magento router configuration). Once loaded the $routerInfo variable contains an array of two routers: admin and standard. Having this information, the system begins iterating through these routers and collect information from the module configurations, which extend them, by looking up the frontName nodes. This is achieved by calling Mage_Core_Controller_Varien_Router_Standard::collectRouters. The admin router will contain routing configurations for the administration back end part of the website, while the standard will be comprised of configurations for the front end.
  • Once the router configuration is collected, the system dispatches an event controller_front_init_routers. This event is observed by Mage_Cms_Controller_Router::initControllerRouters function, which adds a cms router to the front controller.
  • Finally, the front controller adds a default router of type Mage_Core_Controller_Varien_Router_Default. This router will be matched last.

Thus, the front controller initialization includes reading the router configuration from the system configuration objects and arranging the module routing extensions into four routers: admin, standard, cms, and default. This means that whatever router rewrites you have specified in your modules are now available to the front controller. The dispatch() function is now ready to go. It does the following:

  • Checks if the request URL matches the base URL, which is set in the shop configuration. If not, the system performs a redirect to the base URL. The redirect code is set in the configuration node web/url/redirect_to_base and can be null (in which case the further check is not performed), or 301, or 302.

  • Performs URL rewrites by searching for a URL in the core_url_rewrite table. This is done by function Mage_Core_Model_Url_Rewrite::rewrite. This function loads all entries from table core_url_rewrite that match the path info string (the URI path, the part between the host name and the query string, e.g. if the URL is then the path info would be sony-vaio-notebook-pc.html). The found entries contain the target_path (e.g. catalog/product/view/id/144) that is used to locate the controller and action to which the request must be be dispatched. If no match is found, the rewrite function will return false and the router matching will use the original path info. Otherwise, the found target_path will be used as a new path info. It will be amended with a query string, if such was provided with the request.

  • Processes configuration rewrites. The front controller uses its function rewrite to retrieve configuration nodes under path global/rewrite. If such configuration entries are found, the front controller rewrites the path info property of the request object accordingly.

  • Finds a router match. As we have established, there are already 4 routers known to the front controller: admin, standard, cms, and default. These will be iterated through to match the controller and action parameters parsed from the URL. The loop, in which it is done, is nested.
    • The upper loop will iterate until either a request is matched or the loop count exceeds 100. It is done because some routers can be rewritten multiple times. In this case the inner loop will break and fall back to the external loop to match a rewritten request again. The limit of 100 iteration is set to avoid erroneous recursive rewrites, which otherwise would lead to an infinite matching circle. Upon reaching the iteration limit the system throws the “Front controller reached 100 router match iterations” exception.
    • The inner loop will go through all 4 routers. If a match is found, i.e. there is a valid controller class that has a callable action, the request is dispatched – the request is processed by the found controller. If no match is found in the first 3 routers, the request is transferred to the last, default, router that generates a noRoute output that is a 404 error page.
  • The matched request is now dispatched. Before outputting the generated HTML (or any other form of output: XML, JSON) the front controller sends a controller_front_send_response_before event. After the response is sent, a controller_front_send_response_after is dispatched.

As you can see the Magento’s front controller encapsulates the request processing from the moment after the application is initialized and until the output is ready to be sent back to the client. The router matching and request dispatching to a controller is a demonstration of a command-based pattern: a command is recognized from the request and executed. You can add new commands (create new controllers) without altering the front controller. Compared to their complexity the front controller is relatively simple, and its tasks are narrow: routing and dispatching.

Quickly Create Magento Module Structure with a Powershell Script

Your work on a new module usually begins with a tedious task of creating the basic folder structure. You go to your app folder, cd to code, then to local, then to you working package directory. You add module folder and then etc, Block and Helper. Before you actually get to programming you logic you have to add and edit config.xml, helper.php. And when you are done and ready to fire the first test you remember you forgot to add an xml module file to app/etc/modules.

Sounds familiar? But you are a programmer; so why not try to solve this problem like a programmer?

Its fairly easy to write a shell script for Linux that would do all this dull work for you. But for those who work in Windows (I do) automating this task would mean diving into decades old DOS scripting to create a bat file. Fortunately there is a better alternative: PowerShell. This object oriented scripting framework was first introduced in 2006 and since then had evolved into an incredibly nifty tool for every advanced Windows user.

After some tinkering I was able to produce a script that offers GUI to enter destination app folder, package and module names and creates the basic folder structure and essential files in no time. No installation required: download, unzip, and double-click!

Download it now, its free to use, modify and distribute:


Symbolic Links

It is surprising how many people don’t know about symbolic links and do not realize how useful they can be. For instance you are running a test server with multiple instances of out shop configured as separate installations. You can easily update the source code by pulling it from a common repository. But what if you need to update you media files, which normally are not under version control? And what if there are tens of gigabyte of them? You don’t want to copy the files to each installation, instead copy them into one folder and set up symbolic links for the media folders of your test sites:

Creating a symbolic link (syntax):


For example:

ln -s /var/media /var/www/vhosts/test01/media

ln -s /var/media /var/www/vhosts/test02/media

and so on…



Using Macros for Apache Virtual Hosts Configuration for Magento Multistore Environment

When working with a multistore environment it is often necessary to setup several virtual hosts whose configuration is essentially the same apart from one or two settings. Instead of copying and pasting each configuration twice (do not forget about the SSL) one can use a third party extension for Apache web server called mod_macro. This module allows you setting up templates (macros) of your configuration. Your actual vhosts entries will contain references to these templates providing values for variables you defined.

Installing mod_macro on a Ubuntu server is simple by running the apt-get utility:

sudo apt-get install libapache2-mod-macro

Alternatively, download the module and perform a direct installation as described here.

Sample usage

Consider you have to configure two shops running from the same installation. You want them to have two different top level domains ( and and keep logs separate.
Create a new virtual host configuration file under /etc/apache2/sites-available/ and create your template as follows:

<Macro MyTld $tld>
    ServerName www.myserver.$tld
    ServerAlias myserver.$tld
    ErrorLog ${APACHE_LOG_DIR}/error_www.myserver.$tld.log
    LogLevel warn
    CustomLog ${APACHE_LOG_DIR}/access_www.myserver.$tld.log vhost_combined
    DocumentRoot /var/www/

    <Directory /var/www/>
        SetEnv MAGE_RUN_CODE "$tld"
        Options  FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all

Also set up an SSL macro:

<Macro MySSL>
    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/
    SSLCertificateKeyFile /etc/ssl/private/
    <FilesMatch "\.(cgi|shtml|phtml|php)$">
       SSLOptions +StdEnvVars
    BrowserMatch "MSIE [2-6]" \
        nokeepalive ssl-unclean-shutdown \
        downgrade-1.0 force-response-1.0
   # MSIE 7 and newer should be able to use keepalive
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

Finally add your virtual hosts:

NameVirtualHost *:80
NameVirtualHost *:443

 <VirtualHost *:80>
     Use MyTld com

 <VirtualHost *:443>
     Use MyTld com
     Use MySSL

 <VirtualHost *:80>
     Use MyTld de

 <VirtualHost *:443>
     Use MyTld de
     Use MySSL

Advantages of using mod_macro are self-evident:

  • smaller configuration files to maintain
  • less bugs due to copy-pastes only partially updated
  • better readability

More information on mod_macro can be found here.

Theme: Esquire by Matthew Buchanan.