Zend Framework Routing Solutions
Posted on | April 18, 2013 | 6 Comments
Routing is the action of taking the requested url and pointing the application to the correct module, controller, and action so that the request can be correctly processed. There are a number of ways to implement this using the Zend Framework, the most common of which is to create a routing object in the application bootstrap file. An alternative, which this blog post explores, is to store all possible urls in your database, with the corresponding default route stored alongside it.
The default route in a Zend Framework application is structured ‘module/controller/action/parameters’, such that visiting the url example.com/products/index/view/id/99
would internally redirecte to the module named products, the index controller, running the view action, passing in the parameter id with a value of 99. As you can see this is hardly a user-friendly url, which is why many developers create alternate routes for their applications – known as ‘pretty-urls’. For example, any url beginning with ‘product’ should be redirected to the products module, with the rest of the url passed in as a parameter – e.g. example.com/products/my-fancy-socks-with-pockets.html
. Below is an example of how this can be implemented in the bootstrap file:
[php]
bootstrap(‘frontController’);
/* @var $frontcontroller Zend_Controller_Front */
$frontcontroller = $this->getResource(‘frontController’);
$router = $frontcontroller->getRouter();
//add route
$router = $frontcontroller->getRouter();
$router->addRoute(
‘product’, new Zend_Controller_Router_Route_Regex(‘products/([a-zA-Z0-9\-]+)\.html’,
array(
‘module’ => ‘products’,
‘controller’ => ‘index’,
‘action’ => ‘product’,
),
array(‘product’ => 1),
‘products/%s.html’
)
);
}
[/php]
In the index controller of our products module we would then load the product based on the url.
Instead of adding all possible routes on a per module basis (e.g. blog, categories, products, pages, admin area, forum, etc) we instead use a plugin that maps the request to the default route. The benefits of this approach are:
- We can remove the prefix in the url (e.g. get rid of that annoying
/products/
) allowing our urls to be even nicer-looking and remember-able for humans. - By centralising the routing operations we do not need to duplicate the code on a per-module basis, i.e. each module does not need to look in a different table to locate during the dispatch process to determine if the page/product/whatever exists before deciding to return a 404 or not
- It is possible to have multiple URLs pointing to the same resource (useful if, for example, you have a blog post in multiple categories)
The Code
[php]
getRedirect($request->getRequestUri());
//no route found – set to return 404
if(count($route) === 0)
{
throw new Zend_Controller_Action_Exception(‘This page does not exist’, 404);
}
//set new route
$request->setRequestUri($route->redirect);
$request->setDispatched(false);
//Not required. Do a return anyway
return $request;
}
}
[/php]
[php]
fetchRow(
$this->select()
->where(‘slug = ?’, $slug)
);
}
}
[/php]
The columns in the routes
table are ‘id’, ‘slug’, and ‘redirect’. The slug column holds the pretty url (e.g. some-category/somepage.html
) while the redirect column holds the application path, including parameters (e.g. products/index/view/id/314
)
There are a few hooks that registered plugins can latch onto during various stages of the application execution process (in order):
- routeStartup
- routeShutdown
- dispatchLoopStartup
- preDispatch
- postDispatch
- dispatchLoopShutdown
More information about each can be found in the documentation on the official Zend Framework website.
This plugin hooks onto the routeStartup call, passing the request url (e.g. some-category/somepage.html
) into a table object which checks if the route exists. If a route has not been found, then we call another plugin – Zend_Controller_Plugin_ErrorHandler
– telling it we have found a 404. This error handler routes to /default/error/error (default module, error controller, error action) which sets the 404 header and displays an unpleasant not found message – I recommend you edit the view script to something a bit more pleasing :) If a route is found, then have the request point to the default-route-structured-redirect and continue the dispatch process. The application will then load the correct module/controller/action with the correct parameters.
Posted By:Richard Parnaby-King