Router abuse

by Naneau

Routing?

Today a post about Routing, that’s right, a real new post! How to use routes to get from a (nice looking) URL to modules/controllers/actions is something that has been explained in dept here and at other places. This post is not about that. This post is about how to use the router to reverse the process, to build your own pretty urls. For simplicity’s sake I’m going to assume that you use the router to do just that, parse and create URLs. You could of course use it for other funky things, like parsing CLI input.

The router

The router lives in front-controller-land, you have problably retrieved it before, to add routes to it.

1
2
3
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
//the router, retrieved from the front controller

The router holds a collection of routes, that it tries to match against the current request. You can retrieve one of those routes using the getRoute() method. The method getCurrentRouteName() will give you the name of the current route. Remember when you added routes to it you had to give them a name? That is going to come in useful now. If you didn’t add any routes to the router, or aren’t using any special route, the name of the route will be “default”, seeing as how that is… The default.

1
2
3
4
5
$currentRoute = $router->getCurrentRouteName();
//get the current route name

$route = $router->getRoute($currentRoute);
//get the actual route object matching the current name

Assembling routes

While getting a route is fun, it doesn’t do you any good unless you put that route to work. Which is what we’re going to do next! The assemble() method has to be defined by all classes that implement Zend_Controller_Router_Route_Interface (along with the match() method that does the matching of the request). Like it’s name suggests, it is supposed to assemble a url for a given request. It takes an array of parameters as the first param, and an optional boolean as a second, to reset the current request params for assembling.

1
2
3
4
5
6
7
8
9
10
$params = array(
    'module' => 'blog',
    'controller' => 'post',
    'action' => 'read'
    'id' => 13
);
$route = $router->getRoute('default');
$url = $route->assemble($params, true);
//create a url using just the params set in $params
//this will look like blog/post/read/id/13

This is also useful, however, when you don’t use the default route:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$router->addRoute(
    'post_read',
    new Zend_Controller_Router_Route(
        'post/:id',
        array(
            'action' => 'read',
            'controller' => 'post',
            'module' => 'blog',
        )
    )
);
//add a route for purrrty urls

$router->getRoute('post_read');
$url = $route->assemble(array('id' => 13), true);
//$url will be post/13

Why do it yourself?

Yes, I am acutely aware that most scenarios for when you’d want to do this are covered. For controllers there’s the redirection action helper, which uses this method to generate it’s urls. For views there’s the url view helper, which helps you generates links to put in your hrefs. But there are places where you don’t have the benefit of these helpers (without hackisly using them out of context). For instaces, generating actions for forms can’t be done automatically (yet). So, why not add a little method to your form?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * set the action based on a route
 *
 * @param array $params
 * @param string $routeName
 * @param bool $reset
 */

public function setRouteAction($params = array(), $routeName = null, $reset = false)
{
    $frontController = Zend_Controller_Front::getInstance();
    //the FC
   
    $router = $frontController->getRouter();
    //the router (assumes rewrite router)

    if ($reset) {
        $routeName = 'default';
    }
    //hmm... this may lead to unexpected results
    //but so will getting the current route name
       
    if ($routeName == null) {
        try {
            $routeName = $router->getCurrentRouteName();
        } catch (Zend_Controller_Router_Exception $e) {
            $routeName = 'default';
        }
    }
    //requested routename/default
   
    $route = $router->getRoute($routeName);
    //the current route
   
    $action = rtrim($frontController->getBaseUrl(), '/') . '/';
    $action .= $route->assemble($params, $reset);
   
    $this->setAction($action);
}