i am developing a Rest Controller with Zend and i am confused with the mapping of urls to the Router.
Basically i read about Zend Router and i could not plan my urls in order to satisfy the mentioned routes.
These are some of my urls that should be mapped to Routers.
http://localhost/api/v1/tags.xml
http://localhost/api/v1/tags.xml?abc=true (param: abc=true)
http://localhost/api/v1/tags/123456.xml (param: 123456.xml)
http://localhost/api/v1/tags/123456/pings.xml (params: 123456, pings.xml)
http://localhost/api/v1/tags/123456/pings.xml?a=1&b=2 (params: 123456, pings.xml, a=1, b=2)
http://localhost/api/v1/tags/123456/pings/count.xml (params: 123456, pings, count.xml)
I am planning such that for the url patterns 1 to 3, "tags" should be the controller and for the url patterns 4 to 6, "pings" should be the controller.
Now i am unsure about how to configure the routers such that the above scenarios will work.
Note that i cannot change these urls. I can offer 100 of my reputation score to the good answer.
First two URLs can be combined to one router.
$r = new Zend_Controller_Router_Route_Regex('api/v1/tags.xml',
array('controller' => 'tags', 'action' => 'index'));
$router->addRoute('route1', $r);
To differentiate the first two routes, check for the presence of the abc parameter in your tags controller. Add the following in your tags controller, index action.
if($this->_getParam('abc') == "true")
{
//route 2
} else {
// route 1
}
Similarly, routes 4 and 5 can be combined into one route.
I have explained for Route 6. For route 3, you can use the same logic.
$r = new Zend_Controller_Router_Route_Regex('api/v1/tags/(.*)/pings/(.*)',
array('controller' => 'pings', 'action' => 'index'),
array(1 => 'param1',2=>'param2')
);
$router->addRoute('route6', $r);
The parameters can then accessed like the following in pings controller.
$this->_getParam('param1') and $this->_getParam('param2')
For Route 5 :
$r = new Zend_Controller_Router_Route_Regex('api/v1/tags/(.*)/pings.xml',
array('controller' => 'pings', 'action' => 'index'),
array(1 => 'param1')
);
$router->addRoute('route5', $r);
The parameters (part of the URL after ?) will not be handled in the Router. By default, they will be passed to your controller.
To get a specifc parameter value passed in your URL, use the following in your controller.
$this->_getParam('a');
The logic is use (.*) in your route and assign them a parameter name and access them in your controller
Here's a starter for a piece of algorithm that distills the controller, indexed params, and extension from the request, which you could incorporate into an extended version of Zend_Rest_Route::match()
:
public function match( $request )
{
$path = $request->getPathInfo();
// distill extension (if any) and the remaining path
preg_match( '~(?U:(?<path>.*))(?:\.(?<extension>[^\.]*))?$~', $path, $matches );
$this->_values[ '_extension' ] = isset( $matches[ 'extension' ] ) ? $matches[ 'extension' ] : null;
$path = isset( $matches[ 'path' ] ) ? $matches[ 'path' ] : '';
// split the path into segments
$pathSegments = preg_split( '~' . self::URI_DELIMITER . '~', $path, -1, PREG_SPLIT_NO_EMPTY );
// leave if no path segments found? up to you to decide, but I put it in anyway
if( 0 == ( $length = count( $pathSegments ) ) )
{
return false;
}
// initialize some vars
$params = array();
$controller = null;
// start finding the controller
// (presumes controller found at segment 0, 2, 4, etc...)
for( $i = 0; $i < $length; $i += 2 )
{
// you should probably check here if this is a valid REST controller
// (see Zend_Rest_Route::_checkRestfulController() )
$controller = $params[] = $pathSegments[ $i ];
if( isset( $pathSegments[ $i + 1 ] ) )
{
$params[] = $pathSegments[ $i + 1 ];
}
}
// remove the param which is the actual controller
array_splice( $params, $i - 2, 1 );
// set the controller
$this->_values[ 'controller' ] = $controller;
// merge the params and defaults
$this->_values = array_merge( $this->_values, $params, $this->_defaults );
return $this->_values;
}
It's hardly tested, and thus not production material of course. But it should get you started.
What this DOES give you so far is:
The controller
The extension
The indexed parameters
What this DOES NOT give you is:
The action (post, put, delete, etc. The algorithm for this is already in Zend_Rest_Route::match()
)
The named parameters (Zend_Controller_Request_Http
takes care of that already)
EDIT
I realize this answer might be considered a bit vague so far. The point is to merge this algorithm with the match()
algorithm of Zend_Rest_Route
. But this above code still needs a lot of attention; you want to account for modules too probably (as does Zend_Rest_Route
), and maybe even an optional baseUrl (not sure how ZF deals with this internally actually).