HowTo Use Child and Segment Routes to Build Simple Routing Tables

Routing is one of the key requirements in modern applications, especially in Zend Framework 2; but they shouldn’t be overly-complicated. Today, we’re going to look at how to build a routing table, simply and easily using child and segment routes.


Routing is one of the key requirements in modern applications, especially in Zend Framework 2; but they shouldn’t be overly-complicated. Today, we’re going to look at how to build a routing table, simply and easily using child and segment routes.

Before I cover how to do it though, let’s first set the scene, laying out the reason why. That way, the examples will be more practical, holding more semantic meaning. Recently I’ve been building an application to scratch an itch of mine, helping me forecast my freelance writing income and workload.

When finished, the application will show me what’s in my writing pipeline (upcoming, submitted and published work), and through that, how much I can expect to earn and by when. To keep it simple, the app’s composed of two controllers: earnings and pipeline. And these controllers will have commonality between them, specifically with the routes. Here’s some examples of what I mean:

  • This Week, This Month, This Quarter, This Year
  • Next Week, Next Month, Next Quarter, Next Year
  • Last Week, Last Month, Last Quarter, Last Year

You can see that whilst there are quite a number of routes, they have two common patterns. The first is a period specifier, the second is a period type. i.e.:

  • Period Specifier: this, next and last
  • Period Type: week, month, quarter and year

So despite having a lot of potential routes, they all boil down to two parameters which we can react to in the controllers, via a switch statement or a class implementing the factory pattern. Given that, they should be combined into only a few, composite, routes in our routing table. Wouldn’t you agree?

But how would we do that? Gladly, it’s quite simply, using a combination of two route types: ****Segment and Child Routes. I’ve made a complete example, which’s available in this Gist. Feel free to skip straight to that. But otherwise, let’s step through the annotated version together.

return array(
'router' => array(
    'routes' => array(
        'forecaster' => array(
            'type'    => 'Literal',
            'options' => array(
                'route'    => '/forecaster',
                'defaults' => array(
                    '__NAMESPACE__' => 'Forecaster\Controller',
                    'controller'    => 'Forecaster',
                    'action'        => 'index',
                ),
            ),
            'may_terminate' => true,

In our module’s module.config.php, we’ve first setup a Literal route, which contains the route prefix: /forecaster, along with the default controller and action. All routes in this group will now start with /forecaster, which makes sense.

'child_routes' => array(

Here, we setup the remaining routes, as child routes of the parent, which we just created.

'earnings-period' => array(
                    'type' => 'segment',

Here, we’ve created our new, composite, route, using the segment type. This allows us to have segments, or route parameters, which can be both interchanged, and accessed in controllers using the fromRoute method, on $this->params(), which we’ll see an example of shortly.

'options' => array(
      'route' => '/earnings/[:period]/[:type]',

Here we’ve specified our route pattern or segment structure. To match, all routes need to start with /earnings/. Following this, they need to contain two further sections, period and type. An example of a matching route is: /forecaster/earnings/this/week. You start to see how it’s easy to make multiple routes from one definition.

'constraints' => array(
    'period' => '(this|previous|last|next|current)',
    'type' => '(week|month|quarter|year)'
),

Now, we add the real magic. Here we set the constraints for what’s allowed in both the period and type segment elements.

  • period can be one of: this, previous, last, next, or current
  • type can be one of: week, month, quarter, or year

NB: You may be wondering why have previous and last, and this and current? There is no necessity to have these. They’re more utility options, just to make things a bit simpler for the user.

'defaults' => array(
                            'controller' => 'earnings',
                            'action' => 'view'
                        )
                    )
                ),
            ),
        ),
    ),
),

Finally, we finish up by specifying the controller and action to use, in this case, the view action on the earnings controller. That’s it. To handle the pipeline, we could just copy it, changing the default controller to &‘pipeline’.

If the constraint syntax looks a little foreign to you, I suggest getting to know the basics of regular expressions (regexes). Not only do these come in very handy, but they save you a lot of time and pre, when even basically mastered.

Accessing the Segments in the Controller

As I mentioned earlier, we need some way of accessing the segment information in the controller, so we can differentiate between the route specified. Gladly, it’s a no brainer. So far, my view action’s really rudimentary, which you can see below:

public function ViewAction()
{
    $period = $this->params()->fromRoute('period');
    $type = $this->params()->fromRoute('type');
    return new ViewModel(array(
        'period' => $period,
        'type' => $type,
    ));
}

Here, I’ve initialised both type and period, by calling fromRoutes() on $this->params, which retrieves the named route parameter, or segment, from the last executed route. What could be simpler?

Security Alert

I need to stress that I've not added any security checks here and off the top of my head am not aware of any. But don't take this pre as a stellar example of security.

Wrapping Up

There we have it; a simple way to keep the composition of a routing table extremely compact. If your table is only small, you may not see a need to take this approach. But with time, I’m sure it, like everything else, will grow.

Have you seen this approach before? Would you use it? Share your thoughts in the comments and let’s kick of an animated discussion.

image credit - nicolasnova


You might also be interested in these tutorials too...

Tue, Apr 15, 2014

Howto Use Constructor Injection In ZF2

Is it right to use setter injection? Or is it evil, to be avoided at all costs, for the explicitness of constructor injection? In today’s post, we explore that and how to implement constructor injection in ZF2 controller classes.

Wed, Jan 29, 2014

Easy Setter Injection in Zend Framework 2

Want to learn how to have initialized objects with minimal code and nearly no hands-on configuration in Zend Framework 2? Come learn about setter injection.

Thu, Jun 20, 2013

\Zend\Db\Sql - Build SQL Where Clauses Easily and Efficiently

In this tutorial we’re working through the basics of \Zend\Db\Sql\Where, showing how to build SQL Where clauses for database queries. We’ll show how to create different predicates from simple to nested queries using of and, or, like, between and in conditions.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus