1

I want to be able to support multiple versioned endpoints from my api simultaneously, such as:

/api/v1.1/counties/get
/api/v1.2/counties/get

But in trying to implement the routing for this, a bit perplexed as to how Cake wants this as I keep getting a

Error: Controller class Counties could not be found.

Attempt 1:

Router::scope('/api', function ($routes) {

    $routes->setExtensions(['json']);
    $routes->fallbacks('DashedRoute');

    $versions = [
        1.1
    ];

    foreach ($versions as $version) {
        $routes->scope('/' . $version, function($routes) {

            $routes->resources('Counties', [
                'controller' => 'Counties',
                'prefix' => 'api',
                'map' => [
                    'get' => [
                        'action' => 'get',
                    ]
                ]
            ]);

        }
    }

});

Attempt 2:

Router::scope('/api', function($routes) {

    $routes->scope('/v1.1', function($routes) {
        $routes->resources('Counties', [
            'controller' => 'Counties',
            'map' => [
                'get' => [
                    'action' => 'get'
                ]   
            ]   
        ]); 
    }); 

    $routes->connect(
        '/v1.1/counties/get',
        [   
            'controller' => 'Counties',
            'action' => 'get',
        ]   
    );  
});

The directory structure I'm currently using (which is still open for debate):

src/Controller/Api/V1.1, which would use base controllers from src/Controller/Api and extend them with stub methods to override if needed. Most of my "fat" is in the models.

and src/Controller/Api/V1.1/CountiesController.php has:

namespace App\Controller\Api\V1.1;

class CountiesController extends AppController
{
}

Would appreciate any insight

ndm
  • 59,784
  • 9
  • 71
  • 110
meder omuraliev
  • 183,342
  • 71
  • 393
  • 434
  • If you need `major.minor` versioning of your API on the URL you're making way too many changes to your API. It might be better to think about how to make future changes with improved backwards compatibility. Another possibility is you're just using the software version of your backend as the API version. APIs shouldn't mutate often and a simple `v1`, `v2`, etc. should be good enough. – Reactgular Jan 27 '18 at 14:54

1 Answers1

4

You cannot use chars like dots in the namespace (folder) structure, as that is invalid PHP.

What you are looking for is using prefix routing and the path option, so that you can connect prefixes that are valid in namespaces, and supply a custom path (URL segment) for the route, something like:

Router::prefix('api', function (RouteBuilder $routes) {
    // ...

    $routes->prefix('v11', ['path' => '/v1.1'], function (RouteBuilder $routes) {
        $routes->resources('Counties', [
            'map' => [
                'get' => [
                    'action' => 'get'
                ]  
            ]
        ]);
    });
});

That would connect the following routes (you can check the connected routes in the shell via bin/cake routes):

+---------------------+-----------------------+--------------------------------------------------------------------------------------------------+
| Route name          | URI template          | Defaults                                                                                         |
+---------------------+-----------------------+--------------------------------------------------------------------------------------------------+
| v11:counties:index  | api/v1.1/counties     | {"controller":"Counties","action":"index","_method":"GET","prefix":"v11","plugin":null}          |
| v11:counties:add    | api/v1.1/counties     | {"controller":"Counties","action":"add","_method":"POST","prefix":"v11","plugin":null}           |
| v11:counties:view   | api/v1.1/counties/:id | {"controller":"Counties","action":"view","_method":"GET","prefix":"v11","plugin":null}           |
| v11:counties:edit   | api/v1.1/counties/:id | {"controller":"Counties","action":"edit","_method":["PUT","PATCH"],"prefix":"v11","plugin":null} |
| v11:counties:delete | api/v1.1/counties/:id | {"controller":"Counties","action":"delete","_method":"DELETE","prefix":"v11","plugin":null}      |
| v11:counties:get    | api/v1.1/counties/get | {"controller":"Counties","action":"get","_method":"GET","prefix":"v11","plugin":null}            |
+---------------------+-----------------------+--------------------------------------------------------------------------------------------------+

The CountiesController class would then be expected in

src/Controller/Api/V11/CountiesController.php

with a namespace of:

App\Controller\Api\V11

See also

ndm
  • 59,784
  • 9
  • 71
  • 110
  • I actually did try a combination of V1.1, V1_1 because I knew there'd be some funkyness/invalidity going on. Will try your suggestion - thanks! – meder omuraliev Jan 26 '18 at 18:37