Download the code from GitHub: Current version 1.0.0
In days of yore, web application URLs would be stuffed full of all kinds of parameters…
But those days are hopefully behind us, and we should now be providing our users with much more intuitive, clean URLs…
This not only looks nicer, but is friendlier to search engines, helps users see where they are in your site, gives more meaning to links before you click them, and offers many other additional benefits.
The Box UK Routing library provides a comprehensive solution for applications that implement a Front Controller architecture. It handles incoming URLs, extracts their parameters, and rewrites output to include these clean URLs for the client.
Including the library is easy and is done in exactly the same way as other Box UK PHP libraries, just include the bootstrap file.
That’s all you need to do, as this then defines its own PSR0 compliant autoloader to load its classes.
So let’s look at an example of how you’d integrate the router into your application. Somewhere in your bootstrapping you’ll need something like this:
$req = new BoxUKRoutingInputStandardRequest(); $url = ‘/some/url’; $helper = new BoxUKRoutingHelper(); $helper->setRoutesFile( ‘/path/to/routes.txt’ ); $router = $helper->getRouter(); $router->process( $req, $url )
The first line creates a request object. In this example we’re using one provided with the library that wraps the $_REQUEST superglobal, but in a real application you’ll probably want to implement your own (see the BoxUKRoutingInputRequest interface). We’re also hard-coding the URL here, just for the example, this will probably come from $_SERVER or somewhere similar.
Next we create the helper object, which takes care of creating and configuring all the other objects we’re going to use.
Finally we run the router and it will alter the Request object with the parameters from our route. We’ll then be able to access it like this:
$req->getValue( ‘controller’ ); $req->getValue( ‘action’, ‘defaultAction’ );
And so on… We can pass a second argument in as the default in case it hasn’t been specified.
Route files are just plain text files. Here’s an example with a few route definitions:
/cars/:word = car:show( brand ) /cars = car() / = home()
The format is:
METHOD URL = CONTROLLER:ACTION( PARAMS )
The method is optional but can be used to restrict routes to particular request types. The action is also optional and will default to ‘index’ if not specified.
You can see in the URLs that we can specify what types of parameters we’re looking for using a colon and then the name of the type (:word, :num, :file, and :any are provided by default) – this then maps over to the name of the parameter on the right hand side. So in the first example above when we received URLs such as /cars/mazda, and /cars/ford that route would be matched and we’d get a request variable named brand with the value ‘mazda’, ‘ford’ etc.
When writing your routes you’ll often find you have a bunch of routes for one controller, so rather than having to repeatedly specify the controller name we can do it just once using a controller block:
[cars:/cars] /:word/:num = showYear( brand, year ) /:word = show( brand ) / = () [*]
Here you can see we start the block by specifying the name of the controller (which we can then exclude from all the following definitions) and a base path (which we can also exclude). The block is ended using a *, meaning all controllers.
The base URL part of the block definition is optional, but specifying it means all the routes below will be prefixed with it (e.g. /cars/:word:num, /cars/:word etc.).
The library provides a number of different default types that we can specify in our routes (eg. :num). But we can easily specify our own to handle our application-specific data formats. Say for example we have a customer number format that we use a lot (which is the string ‘ID’ followed by a number); we specify it with a regexp:
:custid = IDd+
And then it’s available to be used in our routes:
/customers/:custid = customer:show( id )
We’ve seen how we can use the router to process requests and extract the parameters we’ll use to create our controller and dispatch an action. Now we’ll see how we can use the rewriting part of the library to dynamically rewrite our output from standard URLs to clean URLs generated from our routing file.
The library takes a dynamic approach to clean URL generation, so rather than hard coding your URLs in your templates, you specify them as parameterised links which are then automatically converted into matched routes. This makes it super easy to change your routes in one place.
$html = ‘<a href=”server.php?controller=cars&action=show&brand=ford”>Show Ford</a>’; $filter = $helper->getFilter(); // helper configured as earlier $filter->process( $html );
The snippet above takes some HTML with a link to our application (the name of the front controller script is assumed to be ‘server.php’, but this can be customized), and then rewrites it using the routes specified in our routes file. The output will then be:
<a href=”/cars/ford”>Show Ford</a>
It’s not just links that are rewritten, it can also handle forms. If we specify the parameters as hidden fields:
<form action=”server.php”> <input type=”hidden” name=”controller” value=”cars” /> <input type=”hidden” name=”action” value=”create” /> … </form>
Using the route /cars/:word = cars(action), this becomes:
<form action=”/cars/create”> … </form>
In the last section our output filter was internally using a rewriting object to change the HTML. You might also find this object useful in isolation to do things such as redirecting the user to a correct clean URL:
$url = ‘server.php?controller=user&action=show&id=123’ ;$rewriter = $helper->getRewriter(); header( ‘Location: ‘ . $rewriter->rewrite($url) );
(Though you’ll probably have a more centralised way of handling this kind of thing).
Having clean URLs is no longer a ‘nice to have’, it’s a must. We use Box UK Routing in our flagship Web CMS product with great success, and I’m certain that you will find it useful too.