I code a client/server application.
- Server side is powered by CakePHP 2.4.7.
- Client side run with angularjs and cordova on mobile devices
I use several cakephp route prefixes whose 'admin' and 'mobile'.
(And I use $resource and $httpInterceptor angularjs factories to setup my REST requests.)
I want to call /website/mobile/posts/add.json
with json data posting.
So I call /website/mobile/posts.json
with a POST ajax query:
The problem is here: the called controller action is 'index', not 'add';
On top of my PostsController, I added this:
echo $this->request->params['action'] . "\n";
echo ($this->request->isPost())? "post" . "\n" : "not post";
die;
and the response is always:
mobile_index
post
So the ajax request seems correct but cakephp don't map it to the add action, but index one.
However my configuration seems good too; here is a fragment of my routes.php
Router::mapResources(array('users','posts'));
Router::parseExtensions('json');
Any idea ?
Prefix routing doesn't work with REST routing out of the box
REST routing works by automatically creating routes for the controllers passed to Router::mapResources()
. Prefix routing pretty much does the same, it creates default routes including the prefixes defined in Routing.prefixes
.
However both functionalities don't know about each other, they both create separte routes, so Router::mapResources()
will connect to URLs without prefixes (the prefix
option for this method is not using actual prefix routing, it will just add the value of that option to the beginning of the URL to connect to!), and therefore your request to /mobile/...
doesn't actually use REST routing but only prefix routing.
Defining prefixed REST routes manually
There is no simple fix for this problem like using an option or something, instead you'll have to define the REST routes manually so that the prefix option is included, simple enough though.
See Modifying the default REST routes and Custom REST Routing.
A sample Route could look like this:
Router::connect(
'/mobile/users',
array(
'prefix' => 'mobile',
'mobile' => true,
'controller' => 'users',
'action' => 'add',
'[method]' => 'POST'
)
);
This would connect POST requests to /mobile/users
to UsersController::mobile_add()
. Similary you'll have to do this for all other methods like GET
and PUT
, with and without passing an id
, etc.
Note that when connecting manually you can ditch the Routing.prefixes
option and of course the call to Router::mapResources()
.
Automated mapping
Defining all those routes by hand is kinda exhausting, you're better of automating it with respect to the Router::resourceMap()
configuration.
Here's an example on how to do that, it's somewhat similar to Router::mapResources()
, but it accepts a prefix
option that actually makes use of prefix routing:
function mapResources(array $controllers) {
$resourceMap = Router::resourceMap();
foreach($controllers as $controller => $options) {
if(!is_array($options)) {
$controller = $options;
$options = array();
}
$options += array(
'prefix' => null,
'plugin' => null,
'id' => Router::ID . '|' . Router::UUID
);
foreach($resourceMap as $params) {
$url = '';
if($options['prefix']) {
$url .= '/' . $options['prefix'];
}
if($options['plugin']) {
$url .= '/' . $options['plugin'];
}
$url .= '/' . $controller;
if($params['id']) {
$url .= '/:id';
}
Router::connect(
$url,
array(
'prefix' => $options['prefix'],
$options['prefix'] => !!$options['prefix'],
'plugin' => $options['plugin'],
'controller' => $controller,
'action' => $params['action'],
'[method]' => $params['method']
),
array(
'id' => $options['id'],
'pass' => array('id')
)
);
}
}
}
You would call it like this:
mapResources(array(
'books' => array(
'prefix' => 'mobile'
)
));
and it would map all the REST routes for your books
controller using the mobile
prefix.