Magento is not designed for URL designers.
When an interactive developer creates a content site, or uses another system (like Magento) to build a content site, the URL structure is as important as the content sitting on the page. Unfortunately, Magento’s routing system doesn’t make this a pleasant or easy task.
When you’re setting up a code module in Magento, (which is how you properly add anything to the system), you need to pick a module “frontname”. This frontname becomes the first part of any URL you end up exposing with your module.
http://example.magento.com/frontname
The second and third part of your URL are driven by the name of your controller file, and its action methods. Something like this
#File: Packagename/Modulename/controllers/TestController.php
class Packagename_Modulename_TestController extends Mage_Core_Controller_Front_Action
{
public function pageAction()
{
}
}
would give you a URL like this
http://example.magento.com/frontname/test/page
There’s two major problems here for URL designers. The first is this scheme forces your into a three/deep/hierarchy
approach to your URLs. While you can achieve something shallower using Index
controllers and index
actions, doing so incurs a confusing amount of mental overhead. Additionally, the three deep URL is still exposed to the world, which exposes your site/application to duplicate content problems.
The other problem is the price of change. If you decide mid-project that a URL should be designed differently, you’re going to impact other parts of the system that use front names, controller names and action names to drive behavior. This means the URL designer needs to take on that work, or burn cycles getting the rest of the team aligned to the change. Inertia almost always wins in these situations and the sub-optimal URL remains.
What about Rewrites?
We’ve previously covered Magento’s rewrite system, which offers another solution for URL designers. Again though, there’s a base level of complexity to the system that reenforces natural inertia. Which URL rewrite system should we use? Rumor has it the configuration based system is considered deprecated, but the model based rewrite system requires transferring database information between environments (always a tricky tasks in interactive production environments).
Also, there’s nothing obvious about either rewrite system. A new developer will default to using the front name, controller name and action name because a database based rewrite stays hidden in the system. Again, inertia wins, and developers inclined towards URL design end up stewing and loathing Magento projects.
This may seem esoteric if you’re not involved with the bleeding edge front-end development community, but I see it time and time again with the interactive agencies I consult with. Because of that, I’ve created the Simple Page extension.
Getting Started with Simple Page
Simple Page provides Django style routing for Magento. It doesn’t replace the existing routing system, but instead focuses on providing developers with an easy alternative to setup URLs for Magento pages. It’s easy to approach for beginners, but also provides access to Magento’s powerful layout features without the need for Layout XML Updates.
Once you’ve installed the Magento Connect package (direct download here), go to the following URL
http://store.example.com/test/page
Assuming you’re using the default theme, you should see a page that looks something like this
If you’re using a different theme, copy the
app/design/frontend/default/default/template/pulsestorm_simplepage
folder to your theme folder.
The above page is the only default route that ships with the extension, but don’t worry. The next section covers adding your own URLs to the system.
URL Configuration in simplepage.xml
Let’s take a look at the existing URL configuration. Open up the following file (we’ve removed some of the comments for easy web viewing)
<!-- File: app/code/community/Pulsestorm/Simplepage/etc/simplepage.xml -->
<config>
<pulsestorm_simplepage_routes>
<!-- this is the minimum configuration for a route -->
<a_uniquely_identified_node_name_maybe_use_your_package_namespace>
<url_regex><![CDATA[^test/(?P<foo>page)]]></url_regex>
<view>hello</view>
</a_uniquely_identified_node_name_maybe_use_your_package_namespace>
</pulsestorm_simplepage_routes>
</config>
This configuration file contains a single Simple Page url configuration node
<!-- File: app/code/community/Pulsestorm_Simplepage/etc/simplepage.xml -->
<a_uniquely_identified_node_name_maybe_use_your_package_namespace>
<url_regex><![CDATA[^test/(?P<foo>page)]]></url_regex>
<view>hello</view>
</a_uniquely_identified_node_name_maybe_use_your_package_namespace>
At its most basic, a simple page url route is two things: A regular expression that’s matched against an incoming URL (<url_regex/>
) and the name a view function (<view/>
) to call if the URL matches. The route configured above has a regular expression of
^test/(?P<foo>page)
This says
match any url starting with
test/page
. If this matches an incoming URL, the view function that’s called ishello
Notice that the URL structure is completely decoupled from the PHP code that’s called. This lets us avoid making a decision on what to do with dashes, underscores, camel case, and all the other URL form edge cases that other PHP systems deal with differently.
Let’s add a view handler for our own custom URL
http://example.magento.com/tutorial-example
To do this, we’d add a new node to the simplepage.xml
configuration file
<!-- File: app/code/community/Pulsestorm_Simplepage/etc/simplepage.xml -->
<config>
<pulsestorm_simplepage_routes>
<!-- ... -->
<pulsestorm_tutorial_example> <!-- pulsestorm_tutorial_example
doesn't matter, but needs to be
unique -->
<url_regex><![CDATA[^tutorial-example$]]></url_regex>
<view>tutorial</view>
</pulsestorm_tutorial_example>
</pulsestorm_simplepage_routes>
</config>
With the above configuration in place, load up your URL
http://magento1point6point1.dev/tutorial-example
If you’re in developer mode, you’re going to see an error something like this
Warning: call_user_func() expects parameter 1 to be a valid callback, function 'tutorial' not found or invalid function name in /path/app/code/community/Pulsestorm/Simplepage/Controller/Base.php on line 33
The important bit is function 'tutorial' not found
. PHP is yelling at us because we’ve configured this view function without defining it. We’ll be taking care of that next.
View Function
Views are defined in the following file
#File: app/code/community/Pulsestorm/Simplepage/views/simplepage.php
function hello($block, $layout, $request, $response)
{
$block->setTemplate('pulsestorm_simplepage/example.phtml');
return $block;
}
Right now the file contains our hello
view function. Let’s add a tutorial
function
#File: app/code/community/Pulsestorm_Simplepage/views/simplepage.php
function tutorial($block)
{
$block->setTemplate('tutorial-example.phtml');
return $block;
}
We’ll also need to add a template for this block. In the base of your theme’s template folder, add the following file (this example assumes the default theme’s template folder)
<!-- File: app/design/frontend/default/default/template/tutorial-example.phtml -->
<h1>Tutorial Template</h1>
<p>
Hello World
</p>
With the above in place, load your URL again, and you should see something like the following
Congratulations, you’ve created your first Simple Page route!
A view function’s responsibility is to return a Magento block. The block returned by a view function will be automatically inserted into the Layout’s content block. In the interest of making this as simple as possible, the first paramater of any view function is a pre-instantiated template block.
function tutorial($block)
{
$block->setTemplate('tutorial-example.phtml');
return $block;
}
In the examples above we’ve set a template on the block that was passed in and returned it. However, there’s no reason you need to use that default block. You’re free to instantiate your own. Give the following a try
function tutorial($block, $layout, $request, $response)
{
$new_block = $layout->createBlock('core/text')
->setText('<h1>No Templates Here</h1>');
return $new_block;
}
The first thing to notice is the full paramater list.
function tutorial($block, $layout, $request, $response)
In addition to passing in an instantiated block, every view function will receive a $layout
, $request
, and $response
variable. The $layout
variable contains the Magento layout singleton, while $request
and $response
contain the global Magento request and response objects.
The above function uses the layout object to create a new text block and return that instead of the passed in template block. At this point you could also use the $layout
object to show and hide particular columns, or add a number of different blocks if you want. Anything you normally do in local.xml
you can do here via PHP.
Adding to your Own Module
There’s one problem with the examples above: We’re editing files inside the Pulsestorm_Simplepage
module itself. When there’s an update for the module, a manual change merge will need to happen (sound familiar?). Fortunately, the Simple Page module is built in a way that lets you apply your routes in any code module.
With Pulsestorm_Simplepage
installed, create a new empty module. We’ll call ours Pulsestorm_Helloroute
. Then, add both a simplepage.xml
and simplepage.php
file to your new module
<!-- File: app/code/local/Pulsestorm/Helloroute/etc/simplepage.xml -->
<config>
<pulsestorm_simplepage_routes>
<pulsestorm_tutorial_example_two>
<url_regex><![CDATA[^another-example$]]></url_regex>
<view>our_view</view>
<module>Pulsestorm_Helloroute</module>
</pulsestorm_tutorial_example_two>
</pulsestorm_simplepage_routes>
</config>
<!-- File: app/code/local/Pulsestorm/Helloroute/views/simplepage.php -->
<?php function our_view($block)
{
$block->setTemplate('tutorial-example-two.phtml');
return $block;
}
<!-- File: app/design/frontend/default/default/template/tutorial-example-two.phtml -->
<h1>Another Example</h1>
<p>
This is a test.
</p>
With the above three files in place, try loading the URL
http://magento.example.com/another-example
You should see something like this
Other than changing a few of the details, there’s two new things with the above code. The first, and most obvious, is we’ve placed our files in a module completely separate from the Simple Page module.
The second is the new <module/>
node in simplepage.xml
file
<pulsestorm_tutorial_example_two>
<!-- ... -->
<module>Pulsestorm_Helloroute</module>
</pulsestorm_tutorial_example_two>
By including <module>Pulsestorm_Helloroute</module>
, we’ll telling Magengto that if this particular URL regular expression matches, the views/simplepage.php
file should be loaded from Pulsestorm_Helloroute
instead of Pulsestorm_Simplepage
.
URL Paramaters
The final thing we’ll cover is URL parameters. Try changing your our_view
function so it matches the following
function our_view($block)
{
$block->setTemplate('tutorial-example-two.phtml');
var_dump($block->getParams());
return $block;
}
You should see a data dump at the top of your page something like
array
0 => string 'another-example'
Simple Page will populate its default block with an array of data parameters. By default, the first parameter is the request path. However, you can add to this array by adding capture parenthesis to your url regular expression. Change <url_regex>
to match the following.
<url_regex><![CDATA[^(another)-(example)$]]></url_regex>
Reload your page, and you’ll see a data dump something like this
array
0 => string 'another-example' (length=15)
1 => string 'another' (length=7)
2 => string 'example' (length=7)
Simple Page has passed your captured parenthesis into the block’s parameters data member. You can also also use named parameters
<url_regex><![CDATA[^(?<first>another)-(example)$]]></url_regex>
and your data will include string indexed keys
array
0 => string 'another-example' (length=15)
'first' => string 'another' (length=7)
1 => string 'another' (length=7)
2 => string 'example' (length=7)
A practical use for this would be setting up an ID grabbing URL, something like this
<url_regex><![CDATA[^magazine-articles/(?<id>\d+)]]></url_regex>
Wrap Up
While Magento provides you a lot of functionality out of the box, it’s inevitable that you, your clients, or your employers will outgrow the built in Magento content management functionality. Magento CMS is great for content entry, but not ideal for the sort of hand crafted HTML that world class agencies are known for. Simple Page offers you the raw tools you’ll need to quickly setup and build out pages in your Magento system without diving deep into the module configuration.
This first release is stable, but only a beta. The module’s been submitted for review in Magento Connect, so keep an eye on Twitter for an announcement, or download the extension here. Feedback from other software engineers or interactive developers is more than welcome. Either use the comment below or get in touch directly.