Nitty Gritty of Route Setup
Routes will use your defaults to try and minimize the required length of your URL whenever possible. For example:
$m->connect(':controller/:action/:id', array('action'=>'view', 'id'=>4)); # Will match all of the following # /content/view/4 # /content/view # /content
Trailing dynamic parts of a route path that have defaults setup are not required to exist in the URL being matched. This means that each of the URL examples shown above will result in the same set of keyword arguments being sent to the same controller and action.
If a dynamic part with a default is followed by either static parts or dynamic parts without defaults, that dynamic part will be required despite having a default:
// Remember that :action has an implicit default $m->connect('archives/:action/:article', array('controller'=>'blog')); # Matches: # /archives/view/introduction # /archives/edit/recipes # Does Not Match: # /archives/introduction # /archives/recipes
This way, the URL coming in maps up to the route path you created, part for part.
When using Groupings, parts will still be left off, but only if the remainder of the URL has no static after it. This can lead to some odd looking URLs being generated if you aren't careful about your requirements and defaults. For example:
# Groupings without requirements $m->connect(':controller/:(action)-:(id)') # Matches: # /archives/view-3 # /archives/view- # Generation: $utils = $m->utils; $utils->urlFor(array('controller'=>'archives', 'action'=>'view'); # /archives/view-
It's unlikely you want such a URL, and would prefer to ensure that there's always an id supplied. To enforce this behavior we will use Requirements:
# Groupings without requirements $m->connect(':controller/:(action)-:(id)', array('requirements'=> array('id'=>'d+'))); # Matches: # /archives/view-3 # /archives/view-2 # Does Not Match: # /archives/view- # Generation: $utils = $m->utils; $utils->urlFor(array('controller'=>'archives', 'action'=>'view', 'id'=>2)); # /archives/view-2
If you end up with URLs missing parts you'd like left on when using Groupings, add a requirement to that part.
The above rule regarding minimum URLs has two built-in implicit defaults. If you use either action or id in your route path and don't specify defaults for them, Routes will automatically assign the following defaults to them for you:
array('action' => 'index', 'id' => null)
This is why using the following setup doesn't require an action or id in the URL:
$m->connect(':controller/:action/:id'); # '/blog' -> controller='blog', action='index', id=None
When setting up your routes, remember that when using routes the order in which you set them up can affect the URL that's generated. Routes will try and use all the keyword args during route generation and if multiple routes can be generated given the set of keyword args, the first and shortest route that was connected to the mapper will be used. Hardcoded variables are also used first if available as they typically result in shorter URLs.
# Route Setup $m->connect('archives/:year', array('controller'=>'blog', 'action'=>'view', 'year'=null)); $m->connect(':controller/:action/:id'); # Route Usage $utils = $m->utils; $utils->urlFor(array('controller'=>'blog', 'action'=>'view') -> '/archives'
You will typically want your specific and detailed routes at the top of your Route setup and the more generic routes at the bottom.
Wildcard Limitations and Gotchas
Due to the nature of wildcard parts, using wildcards in your route path can result in URL matches that you didn't expect. Wildcard parts are extremely powerful and when combined with dynamic parts that have defaults can confuse the new Routes user.
When you have dynamic parts with defaults, you should never place them directly next to a wildcard part. This can result in the wildcard part eating the part in the URL that was intended as the dynamic part.
$m->connect('*url/:username', array('controller'=>'blog', 'action'=>'view', 'username'=>'george')); # When matching url variable username variable # /some/long/url/george /some/long/url/george george # /some/other/stuff/fred /some/other/stuff/fred george
This occurs because Routes sees the default as being optional, and the wildcard part attempts to gobble as much of the URL as possible before a required section of the route path is found. By having a trailing dynamic part with a default, that section gets dropped.
Notice how removing the dynamic part default results in the variables we expect:
$m->connect('*url/:username', array('controller'=>'blog', 'action'=>'view')); # When matching url variable username variable # /some/long/url/george /some/long/url george # /some/other/stuff/fred /some/other/stuff fred
Let's try one more time, but put in a static part between the dynamic part with a default and the wildcard:
$m->connect('*url/user/:username', array('controller'=>'blog', 'action'=>'view', 'username'=>'george')); # When matching url variable username variable # /some/long/url/user/george /some/long/url george # /some/other/stuff/user/fred /some/other/stuff fred
Not currently supported in the PHP version.