views:

1041

answers:

2

I'm writing some routes for a REST service. One of my resource's URI looks like this

resources/user/:id

I also want to give access to individual attributes of a user, which would look like this

resources/user/:id/:attribute

But when I try to define that latter route, it doesn't work. Here's my ini where the routes are defined

routes.user.route = "resources/user/:id"
routes.user.defaults.controller = user
routes.user.defaults.action = get
routes.user.defaults.id = 0
routes.user.reqs.id = "\d+"

routes.user_attribute.route = "resources/user/:id/:attribute/"
routes.user_attribute.defaults.controller = user
routes.user_attribute.defaults.action = getAttribute
routes.user_attribute.defaults.id = 0
routes.user_attribute.defaults.attribute = ""
routes.user_attribute.reqs.id = "\d+"
routes.user_attribute.reqs.id = "reviews|lists"

When I try to access resources/user/4/reviews in my browser, I get the following output

An error occurred
Page not found
Exception information:

Message: Invalid controller specified (resources)
Stack trace:

#0 /usr/local/lib/ZendFramework/ZendFramework-1.7.1-minimal/library/Zend/Controller/Front.php(934): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#1 /home/baileyp/public_html/web/index.php(50): Zend_Controller_Front->dispatch()
#2 {main}

Request Parameters:

array(4) {
  ["controller"]=>
  string(9) "resources"
  ["action"]=>
  string(4) "user"
  [10]=>
  string(7) "reviews"
  ["module"]=>
  string(7) "default"
}

So, it's clearly not processing my 2nd route correctly, since it things the controller is "resources" and the action is "user". What am I doing wrong here? There's no examples on the Zend site that I've found that illustrate how to implement this.

And I don't want to use modules just to get this done - avoiding the creation of folders and such just to match a URI is the entire purpose of a routing system.

+1  A: 

I think you should only change the ordering of the rules ... so you first put more specific rules on the top and more general on the bottom!

routes.user_attribute.route = "resources/user/:id/:attribute/"
routes.user_attribute.defaults.controller = user 
routes.user_attribute.defaults.action = getAttribute
routes.user_attribute.defaults.id = 0
routes.user_attribute.defaults.attribute = ""
routes.user_attribute.reqs.id = "\d+"
routes.user_attribute.reqs.id = "reviews|lists"

routes.user.route = "resources/user/:id"
routes.user.defaults.controller = user
routes.user.defaults.action = get
routes.user.defaults.id = 0
routes.user.reqs.id = "\d+"

I had a similar problem and it fixed my problem!

Actually, that doesn't work. It throws the exact same exception. Thanks for the suggestion, though.
Peter Bailey
More general rules should be placed first
David Caunt
+1  A: 

Am not sure if you're defining both these side by side. In my experience, the first route you're attempting to map would would actually be handled just fine by the second (thanks to the default value you provided for attribute).

Thus, this route:

foo.com/resources/user/:id/:attribute

With this call:

foo.com/resources/user/12345/

Would assume your default attribute value:

foo.com/resources/user/12345//

As the blank value may be removed by your web server (apache treats multiple slashes as a single slash), I'd recommend defaulting the attribute to something that could be called by name and it will function similar to an index.html file in a folder:

foo.com/resources/user/12345/
foo.com/resources/user/12345/general/ (same result)

The best luck I've had troubleshooting is commenting out the route default values and requirements line-by-line in a sequence. You end up seeing the 'edge' of the route itself (as it will fall back to the defaults as you give it less information).

Also, in your example, note that you're defining your id requirements twice:

 routes.user_attribute.reqs.id = "\d+"
 routes.user_attribute.reqs.id = "reviews|lists"

Would be adjusted to:

 routes.user_attribute.reqs.id = "\d+"
 routes.user_attribute.reqs.attribute = "reviews|lists"

May have an effect on your results as well. Hope this helps.

Matt Gardner