2

I'm trying to implement REST and parseExtension like functionality in my app, running on CakePHP 2.

Instead of having URLs like http://myapp.dev/controller/action.json I would like to use http://myapp.dev/json/controller/action.

The reason for this is that sometimes extensions look a little stupid when put onto something like http://myapp.dev/controller/index/show:10/page:2.json.

While this could be implemented in a custom route, I already have lots of custom routes, and do not want to have to create duplicates of each with a :type field in there for maintenance reasons.

What would be ideal is setting it up such that any url with /json /xml /html etc. in first place would be treated as if json, xml, html etc. were the extension.

While Prefix Routing looks ideal for this, it requires new methods (e.g. json_index, html_index etc. and I would have to specify each format as a separate prefix).

Is there any good way of doing this? I just want parseExtensions to instead be like a parsePrefixes method instead.

GTF
  • 8,031
  • 5
  • 36
  • 59
  • Some alternatives would be to use rewrite rules, or change `/show:10/page:2.json` to `/action.json?show=10&page=2` – tigrang Jul 10 '12 at 18:24
  • The latter isn't especially satisfactory, being quite ugly, and doesn't really solve the problem of using prefix-like routes. I thought about the former but was unsure as to how to work it out since I can't really pass parameters to CakePHP with rewrite rules can I? – GTF Jul 11 '12 at 00:20

1 Answers1

1

You should try the following :

Router::connect('/:ext/', array(':ext'), array('pass' => 'ext'));
Router::connect('/:ext/:controller/*', array(':ext'), array('pass' => 'ext'));
Router::connect('/:ext/:controller/:action/*', array(':ext'), array('pass' => 'ext'));

Then, the router will pass the :ext argument, in the "ext" value of the route parameters. Add it for all your rules !

If you want to use traditionnel routes to work, you need to use a custom CakeRoute. Create a file "/libs/routes/RestRoute.php" in your app folder, add the following into it :

class RestRoutes extends CakeRoute {
    function parse($url) {
        $params = parent::parse($url);

        if (empty($params)) {
            return false;
        }

        if(!in_array($params['ext'], Router::extensions())) {
            return false;
        }

        return $params;
    }
}

And in your /core/routes.php :

App::import('Lib', 'routes/RestRoute');
Router::connect('/:ext/', array(':ext'), array('pass' => 'ext', 'routeClass' => 'RestRoute'));
Router::connect('/:ext/:controller/*', array(':ext'), array('pass' => 'ext', 'routeClass' => 'RestRoute'));
Router::connect('/:ext/:controller/:action/*', array(':ext'), array('pass' => 'ext', 'routeClass' => 'RestRoute'));

So, when your custom route won't be able to pass an url, it would try the default url, without the ext parameters. Otherwise, the order of the parameters are not quiet allright.

Maybe not the best or cleaner solution, but it's a good start.

Gael.D
  • 500
  • 1
  • 5
  • 16
  • Thanks, this has given me a couple of ideas. I'll give it a go. – GTF Jul 24 '12 at 21:51
  • There is also a good plugin, https://github.com/kvz/cakephp-rest-plugin, that can allows to force html extensions in json. But the plugin is not supported anymore, and it ask a lot of modifications and corrections to make it work correctly. – Gael.D Jul 25 '12 at 07:54
  • 1
    Tried it, and passing the `/:ext/...` routes have worked the best so far. I just leave it have the `ext` variable as a named variable because then `Router::parseExtensions()` works automatically. Thanks! – GTF Aug 21 '12 at 16:46