Question

I am trying to get around some of the idiosyncrasies of the routing statements in ZF2. The Album module example suggests the following:

'router' => array(
    'routes' => array(
        'album' => array(
            'type'    => 'segment',
            'options' => array(
                'route'    => '/album[/:action][/:id]',
                'constraints' => array(
                    'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                    'id'     => '[0-9]+',
                ),
                'defaults' => array(
                    'controller' => 'Album\Controller\Album',
                    'action'     => 'index',
                ),
            ),
        ),
    ),
),

Let’s say, however, that I wanted to configure the project so that the url, /album/index/jazz would list only the albums in the jazz genre.

My first challenge is that in /album/index/jazz the second argument is not [/:id]. It wants to be [/:genre]. And, while I could easily just change the route to 'route' => '/album[/:action][/:arg1]', and the constraint to 'arg1' => '[a-zA-Z][a-zA-Z0-9_-]*', I don’t really want to give up the '[0-9]+', constraint on the id. What I’m looking for is a way to define alternative parameter patterns and constraints depending upon what the action is.

My second challenge lies in ZF2’s treatment of the default action. If I set up the routing as follows:

'router' => array(
    'routes' => array(
        'album' => array(
            'type'    => 'segment',
            'options' => array(
                'route'    => '/album[/:action][/:arg1]',
                'constraints' => array(
                    'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                    'arg1'   => '[a-zA-Z][a-zA-Z0-9_-]*',
                ),
                'defaults' => array(
                    'controller' => 'Album\Controller\Album',
                    'action'     => 'index',
                ),
            ),
        ),
    ),
),

then the url /album/index/jazz will send the “jazz” argument to the controller as I want it to. However, I have a problem getting ZF2 to construct the url using the default action. Following the syntax from the index.phtml in the Album module example, my expectation is that the following:

    <a href="<?php echo $this->url('album',
        array('action'=>'index', 'agr1' => 'jazz'));?>">Jazz</a>

would construct <a href="/album/index/jazz "> Jazz </a>. But (I assume because index is the default action) index gets stripped out and ZF2 gives <a href="/album/jazz ">Jazz</a>, which doesn’t target a real page. I’ve tried renaming index to something different in the router, the controller and the view; but have found that as long as it’s the default action, the $this->url( … ) function will strip out whatever name I use. There are probably a number of ways around this issue, but I think it’s best to solve it in the router configuration to avoid surprises with other unknown parts of the framework that may treat the default action similarly.

Was it helpful?

Solution 2

I have found a way to work around the issue that the default parameters get left out of the url that the url() helper configures. When zf2 develops the url from the route statements, it drops out everything within the [] brackets that match the default parameters. Therefore,

    <a href="<?php echo $this->url('album',
        array('action'=>'index', 'arg1' => 'jazz'));?>">Jazz</a>

constructs <a href="/album/jazz">Jazz</a> instead <a href="/album/index/jazz">Jazz</a> of when the route statement includes 'route' => '/album[/:action][/:arg1]', as I presented in the question. However, if I breakout the / character from the parameter(s) and give it it's own[] brackets, the url() helper only drops the :action part of the url. In other words, if I replace

                'route'    => '/album[/:action][/:arg1]',

with

                'route'    => '/album[/][:action][/][:arg1]',

then

    <a href="<?php echo $this->url('album',
        array('action'=>'index', 'arg1' => 'jazz'));?>">Jazz</a>

constructs <a href="/album//jazz ">Jazz</a>, which points to the page it should.


I also got a lot out of timdev's answer, "It's worth it to take a step back and decide how you want your url scheme to work, and then figure out how to implement it." If I want to add a whole lot of bells and whistles to how I want to query, filter and sort the data, theindex action (which manages multiple albums) is going to want to take a completely different set of parameters than the add, edit and delete actions (which each only manage a single album at a time.) So I set up two routes:

'router' => array(
    'routes' => array(
        'albums' => array(
            ...
                'route'    => '/albums[/][:action] [/][:arg1] [/][:arg2] [/][:arg3]   ...  ',
            ...
        ),
        'album' => array(
            ...
                'route'    => '/album[/][:action][/][:id]',
            ...
        ),
    ),
),

OTHER TIPS

It's worth it to take a step back and decide how you want you url scheme to work, and then figure out how to implement it.

If I were doing something similar, I'd might start like:

/albums[/:category] - lists all albums, optionally filtered by category

/albums/:id - display info about a single album

/albums/:id/edit - display a form to edit an album's information, and accept POST

/albums/:id/delete - POST only, deletes an album

/albums/new - Display form for adding an album, and accept POST.

I would advise against having general-purpose nouns in your routes (so, avoid :action, or :arg1). Instead, be explicit, and everything usually just works.

For the first go-around, just specify each route explicitly. Once you've got your head around that, you can start looking into using child routes to group them together.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top