Magento for Developers: Part 3—Magento Controller Dispatch
by Alan Storm, updated for Magento 1.12
by Alan Storm, updated for Magento 1.12
The Model-View-Controller (MVC) architecture traces its origins back to the Smalltalk Programming language and Xerox Parc. Since then, there have been many systems that describe their architecture as MVC. Each system is slightly different, but all have the goal of separating data access, business logic, and user-interface code from one another.
The architecture of most PHP MVC frameworks will looks something like this.
While this pattern was a great leap forward from the "each php file is a page" pattern established early on, for some software engineers, it's still an ugly hack. Common complaints are:
As you've probably guessed, the Magento team shares this world view and has created a more abstract MVC pattern that looks something like this:.
We'll eventually touch on each part of this request, but for now we're concerned with the Front Controller -> Routers -> Action Controller section.
Enough theory, it's time for Hello World. We're going to
First, we'll create a directory structure for this module. Our directory structure should look as follows:
app/code/local/Magentotutorial/Helloworld/Block app/code/local/Magentotutorial/Helloworld/controllers app/code/local/Magentotutorial/Helloworld/etc app/code/local/Magentotutorial/Helloworld/Helper app/code/local/Magentotutorial/Helloworld/Model app/code/local/Magentotutorial/Helloworld/sql
Then create a configuration file for the module (at path app/code/local/Magentotutorial/Helloworld/etc/config.xml):
<config> <modules> <Magentotutorial_Helloworld> <version>0.1.0</version> </Magentotutorial_Helloworld> </modules> </config>
Then create a file to activate the module (at path app/etc/modules/Magentotutorial_Helloworld.xml):
<config> <modules> <Magentotutorial_Helloworld> <active>true</active> <codePool>local</codePool> </Magentotutorial_Helloworld> </modules> </config>
Finally, we ensure the module is active:
Next, we're going to configure a route. A route will turn a URL into an Action Controller and a method. Unlike other convention based PHP MVC systems, with Magento you need to explicitly define a route in the global Magento config.
In your config.xml file(at path app/code/local/Magentotutorial/Helloworld/etc/config.xml), add the following section:
<config> ... <frontend> <routers> <helloworld> <use>standard</use> <args> <module>Magentotutorial_Helloworld</module> <frontName>helloworld</frontName> </args> </helloworld> </routers> </frontend> ... </config>
We have a lot of new terminology here, let's break it down.
The <frontend> tag refers to a Magento Area. For now, think of Areas as individual Magento applications. The "frontend" Area is the public facing Magento shopping cart application. The "admin" Area is the private administrative console application. The "install" Area is the application you use to run though installing Magento the first time.
There's a famous quote about computer science, often attributed to Phil Karlton:
"There are only two hard things in Computer Science: cache invalidation and naming things"
Magento, like all large systems, suffers from the naming problem in spades. You'll find there are many places in the global config, and the system in general, where the naming conventions seem unintuitive or even ambiguous. This is one of those places. Sometimes the <routers> tag will enclose configuration information about routers, other times it will enclose configuration information about the actual router objects that do the routing. This is going to seem counter intuitive at first, but as you start to work with Magento more and more, you'll start to understand its world view a little better. (Or, in the words of Han Solo, "Hey, trust me!").
When a router parses a URL, it gets separated as follows
http://example.com/frontName/actionControllerName/actionMethod/
So, by defining a value of "helloworld" in the <frontName> tags, we're telling Magento that we want the system to respond to URLs in the form of
http://example.com/helloworld/*
Many developers new to Magento confuse this frontName with the Front Controller object. They are not the same thing. The frontName belongs solely to routing.
This tag should be the lowercase version of you module name. Our module name is Helloworld, this tag is helloworld. Technically this tag defines our route name
You'll also notice our frontName matches our module name. It's a loose convention to have frontNames match the module names, but it's not a requirement. In your own modules, it's probably better to use a route name that's a combination of your module name and package name to avoid possible namespace collisions.
This module tag should be the full name of your module, including its package/namespace name. This will be used by the system to locate your Controller files.
One last step to go, and we'll have our Action Controller. Create a file at
app/code/local/Magentotutorial/Helloworld/controllers/IndexController.php
That contains the following
<?php class Magentotutorial_Helloworld_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { echo 'Hello World'; } }
Clear your config cache, and load the following URL
http://example.com/helloworld/index/index
You should also be able to load
http://example.com/helloworld/index/ http://example.com/helloworld/
You should see a blank page with the text "Hello World". Congratulations, you've setup your first Magento Controller!
Action Controllers should be placed in a module's controllers (lowercase c) folder. This is where the system will look for them.
Remember the <module> tag back in config.xml?
<module>Magentotutorial_Helloworld</module>
An Action Controller's name will
All Action Controllers need Mage_Core_Controller_Front_Action as an ancestor.
As we previously mentioned, Magento URLs are routed (by default) as follows
http://example.com/frontName/actionControllerName/actionMethod/
So in the URL
http://example.com/helloworld/index/index
the URI portion "helloworld" is the frontName, which is followed by index (The Action Controller name), which is followed by another index, which is the name of the Action Method that will be called. (an Action of index will call the method public function indexAction(){...}.
If a URL is incomplete, Magento uses "index" as the default, which is why the following URLs are equivalent.
http://example.com/helloworld/index http://example.com/helloworld
If we had a URL that looked like this
http://example.com/checkout/cart/add
Magento would
Let's try adding a non-default method to our Action Controller. Add the following code to IndexController.php
public function goodbyeAction() { echo 'Goodbye World!'; }
And then visit the URL to test it out:
http://example.com/helloworld/index/goodbye
Because we're extending the Mage_Core_Controller_Front_Action class, we get some methods for free. For example, additional URL elements are automatically parsed into key/value pairs for us. Add the following method to your Action Controller.
public function paramsAction() { echo '<dl>'; foreach($this->getRequest()->getParams() as $key=>$value) { echo '<dt><strong>Param:</strong>'.$key.'</dt>'; echo '<dt><strong>Value: </strong>'.$value.'</dt>'; } echo '</dl>'; }
and visit the following URL
http://example.com/helloworld/index/params?foo=bar&baz=eof
You should see each parameter and value printed out.
Finally, what would we do if we wanted a URL that responded at
http://example.com/helloworld/messages/goodbye
Here our Action Controller's name is messages, so we'd create a file at
app/code/local/Magentotutorial/Helloworld/controllers/MessagesController.php
with an Action Controller named Magentotutorial_Helloworld_MessagesController and an Action Method that looked something like
public function goodbyeAction() { echo 'Another Goodbye'; }
And that, in a nutshell, is how Magento implements the Controller portion of MVC. While it's a little more complicated than other PHP MVC framework's, it's a highly flexible system that will allow you build almost any URL structure you want.