Zend Framework Routing Solutions
Posted on | April 18, 2013 | 6 Comments
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