Structure of api versioning system in PHP - php

So I want to add backward in-compatible changes to my API.
I was thinking of doing the following.
All of my api endpoints are accessed as follows:
/v2/account
/v2/order
Having a v2 controller that is passed an API version and calls the appropriate function in version specific controllers that are subclass to v2.
so for version 2013_02_13 it calls v2_2013_02_13::account for account api call
How would you implement an API versioning system to support backward incompatible changes using a PHP mvc framework?

In Kohana you could just use a directory for that. So your controllers would be placed like this.
application/classes/Controllers/V2_2013_02_13/Enpoint1Controller
Than you can set up routes for your different versions.
Route::set('v2', 'v2/<controller>(/<action>)')
->defaults(array(
'directory' => 'V2_2013_02_13',
'controller' => 'welcome',
'action' => 'index',
));
This would be the most easy approach, however if you really want to have some dynamic way to call specific versions of your controllers than I would look at the HMVC of Kohana.
My guess would be, that you need an entry controller for each version you have and do an internal request to the correct controller.
Maybe something like this.
Route:
Route::set('versioned', '<version>/<someAction>')
->defaults(array(
'version' => 'v2',
'someAction' => 'user'
'controller' => 'welcome',
'action' => 'index',
));
Controller:
class Controller_Welcome extends Controller {
public function action_index()
{
// Your Version and the action
$this->request->param('version');
$this->request->param('someAction');
// Do an internal request to the right controller (the v2/user is an example)
$internalRequest = Request::factory('v2/user');
}
}
I hope this helps.

Related

How do explicit routes fail to find the Controller

I set up the following routes. The routes are explicit and the controller is in src/Controller directory. I did a git pull on the stage server and suddenly cakePHP (3.6) could not find the method, and was looking in the wrong controller. Below are the routes that are explicitly coded.
Router::scope('/', function (RouteBuilder $routes) {
$routes->connect('/<foo>/methodOne/*', ['controller' => 'SomeController', 'action' => 'methodOne']);
$routes->connect('/<foo>/methodTwo/*', ['controller' => 'SomeController', 'action' => 'methodTwo']);
$routes->fallbacks('DashedRoute');
}
This happens with new controllers I create sometimes. I have added the correct namespaces and use statements. To fix this issue I make changes in the controller and then it works. Any idea why this happens? Anyway to prevent this from happening over and over again?

phalcon router not working

I try to use a router with phalcon. This is how it is included in index.php right after registering the 'events manager':
$di->set('router', function(){
require __DIR__.'/../app/config/routes.php';
return $router;
});
and this is how the routes.php looks like:
<?php
$router = new Phalcon\Mvc\Router(false);
$router->add("/", array(
'controller' => 'index',
'action' => 'index'
));
$router->add("/topics", array(
'controller' => 'wurst',
'action' => 'index'
));
$router->handle();
return $router;
The website reacts as if the router was not existent. /topics and topics say this:
TopicsController handler class cannot be loaded
and I also cannot use a die("test"); function inside routes.php . nothing happens.
I also tried to activate it without a separate file, but the result was the same :(
(The sample web-application INVO was used as starting point for my site )
$router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI); will use default $_SERVER['REQUEST_URI']
If your index/index action is working when you access domain.com/index.php, check that you are using proper uri source, if using nginx or php built-in server you might have some problems with routing and $_GET['_uri'] which phalcon use for handling uris.
can find more about it on phalcon router documentation about uri sources -> http://docs.phalconphp.com/en/latest/reference/routing.html#uri-sources
now it seems to work:
Action 'route404' was not found on handler 'index'
the problem was , that I put the function to set the router in index.php within "set dispatcher function". ..did not see the closing braces.

Phalcon Router and Loader for subfolder structure getting bigger. How to set up?

I have a quite big project that I need to program. I used to code with CodeIgniter but since they stopped maintaining the framework, I decided to switch to another. I chose Phalcon framework. The folder structure of the application that I want to implement is next:
app/
controllers/
admin/
users/
UsersController.php
UserGroupsController.php
dashboard/
system/
another_subfolder/
AnotherSubFolderController.php
production/
settings/
SettingsController.php
dashboard/
flow/
another_subfolder/
AnotherSubFolderController.php
website1/
customers/
CustomersController.php
CompaniesController.php
another_subfolder .... /
models/
sub_folder_structure/ // The same as controllers
This is just example of the folder structure of the application. There will be quite a lot of folders and sub-folders in this project to make it manageable.
From my understanding I need to register all namespaces at the loader for each sub-folder, so that Phalcon knows where to search and load the class.
//Register an autoloader
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
// Main
'Controllers' =>'app/controllers/',
'Models' =>'app/models/',
// Admin Routing
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin\Users'=>'app/controllers/admin/users',
'Models\Admin\Users'=>'app/models/admin/users'
))->register();
The loader will look quite big.
Then I have to set-up the router so that requests redirected to the right controller. Right now I have next:
// Setup Router
$di->set('router', function(){
$router = new \Phalcon\Mvc\Router(false);
$router->removeExtraSlashes(true);
$router->add('/', array(
'namespace' => 'Controllers',
'controller' => "login"
));
$router->add('/:controller/:action/', array(
'namespace' => 'Controllers',
'controller' => 1,
'action' =>2
));
$router->add('/admin/:controller/:action/', array(
'namespace' => 'Controllers\Admin',
'controller' => 1,
'action' =>2
));
$router->add('/admin/users/:controller/:action/', array(
'namespace' => 'Controllers\Admin\Users',
'controller' => 1,
'action' =>2
));
return $router;
});
That will be also very big if I need to manually setup router for each sub-folder and namespace.
So the questions that I have are this:
Is there any way to make a loader prettier and smaller? As it will grow bigger.
How can I setup router so that I don't need to enter all of the namespaces and combinations of subfolders? Is there any way to make it smaller? May be modify dispatcher or any other class? Or is there a way I can set a path variable in the router to the location of the controller?
I have researched Phalcon documentation but could not find the way to do that.
Any help and suggestions are very much appreciated.
Thanks
Ad. 1. You can change your namespaces to be more PSR-0 (in my opinion), so I would make:
app
controllers
Admin
Users
UsersController.php
The you can register one namespace Admin or any other. Then you need to register only the top most namespace to work (keep in mind that your UsersController must have namespace Admin\Users\UsersController; to work). Thena autoloader should have only:
$loader
->registerDirs(
array(
// It's taken from my config so path may be different
__DIR__ . '/../../app/controllers/'
// other namespaces here (like for models)
)
);
I'm using registerDirs so I only point loader to the folder in which some namespace exists.
Ad. 2.
For this you can use groups of routes so you can pass a namespace as a parameter for config array of constructor and then do the repeative task in one place. Then just create new instances with different parameters;
$router->addGroup(new MahGroup(array('namespace' => 'Mah\\Controller'));
So inside MahGroup class could be:
class MahGroup extends Phalcon\Mvc\Router\Group {
public function _construct($config = array()) {
$this->setPrefix('/' . $config['perfix']);
$router->add('/:controller/:action/', array(
'namespace' => $config['namespace'],
'controller' => 1,
'action' => 2
));
// etc...
}
}
And then configuring routes:
$router->addGroup( new MahGroup(array('prefix' => 'mah-namespace', 'namespace' => 'Mah\\Namespace' )) );
$router->addGroup( new MahGroup(array('prefix' => 'mah-other-namespace', 'namespace' => 'Mah\\Other\\Namespace' )) );
But given examples for second question are just what could be done. I usually create Group class for each namespace and then declare some routes that my app uses since I'm not using english names for routes and I need rewriting polish urls to controllers which have also some namespaces.

Phalcon router doesn't react to subfolders and namespace declaration

So I've been reading a ton of stackoverflow and phalcon forum threads.. (I'm starting to hate this framework), but nothing seem to work and it doesn't explain why like Laravel does, for example.
I'm just trying to be able to operate with this application structure:
As you can see, all I want is to use namespaced controllers in subfolders to make more order for my code.
According to all explanations, here's my loader.php:
<?php
$loader = new \Phalcon\Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$loader->registerDirs(
array(
$config->application->controllersDir,
$config->application->modelsDir,
$config->application->pluginsDir
)
)->register();
AFAIK, Phalcon should traverse all subfolders for not found classes when used via registerDirs.
Then I define my routes to specific controller after the main route to index controllers in base directory:
<?php
$router = new Phalcon\Mvc\Router(false);
$router->add('/:controller/:action/:params', array(
'namespace' => 'App\Controllers',
'controller' => 1,
'action' => 2,
'params' => 3,
));
$router->add('/:controller', array(
'namespace' => 'App\Controllers',
'controller' => 1
));
$router->add('/soccer/soccer/:controller', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1
));
$router->add('/soccer/:controller/:action/:params', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1,
'action' => 2,
'params' => 3
));
return $router;
And one of my controllers look like:
<?php namespace App\Controllers\Soccer;
use App\Controllers\ControllerBase as ControllerBase;
class IndexController extends ControllerBase
{
public function indexAction()
{
}
}
What's wrong here? Default top namespace is not registered? Am I missing something?
This just doesn't work. When I try to open myserver.com/soccer which I expect to go to app/controllers/soccer/IndexController.php, but instead it tells me:
SoccerController handler class cannot be loaded
Which basically means it's looking for SoccerController.php in /controllers directory and totally ignores my subfolder definition and routes.
Phalcon 1.3.0
Stuck on this for a week. Any help - Much appreciated.
I was having a problem with loading the ControllerBase and the rest of the controllers in the controllers folder using namespaces. I was having a hard time since other example projects worked fine and I realized that I was missing a small detail in the despatcher declaration where I was supposed to setDefaultNamespace
(ref: https://github.com/phalcon/vokuro/blob/master/app/config/services.php)
$di->set('dispatcher', function () {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('App\Controllers');
return $dispatcher;
});
or you can specify it directly on the routing declaration file like this
$router->add("/some-controler", array(
'namespace' => 'App\Controllers'
'controller' => 'some',
'action' => 'index'
));
that should work as well, it might be a bit confusing at first with namespaces but once you get a hang of it you will love this extremely fast framework
It looks like your namespaces have capitals
App\Controllers\Soccer
and your folder structure does not
app\controllers\soccer
In my app I have controllers with a namespace and I have just tried changing the case of the folders so they don't match and I get the missing class error.
Of course it does raise a question, how many controllers are you planning on having that namespacing them is worthwhile? Are you grouping controllers and action by function or content? I used to have loads of controllers in Zend, but now I have grouped them by function I only have 16 and I am much happier. e.g. I suspect soccer, rugby, hockey etc will probably be able to share a sport controller, articles, league positions etc will have a lot of data in common.
ps Don't give up on Phalcon! In my experience it is sooo much faster than any other PHP framework I have used it is worth putting up with a lot :)
Are you sure Phalcon traverses subfolders when registering directories to the autoloader? Have you tried adding a line to your autoloader which explicitly loads the controllers\soccer directory?
Alternatively, if your soccer controller is namespaced, you can also register the namespace: "App\Controllers\Soccer" => "controllers/soccer/" with the autoloader.

Routing in Kohana 3.3.1

I recently started working with the kohana 3.3.1 framework and ran into some problems.
I want to create different routes for different entry points. Right now, this is the default route, which seems to work fine(I think):
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
When I go to the website, it displays 'hello, world!'.
I have a controller called Street, located in application/classes/Controller/Street.php.
The code to this controller is:
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Street extends Controller {
public function action_index()
{
$this->response->body('hello, street!');
}
The problem is, that I want to type /something behind the URI, and it should redirect to the defined controller, and action. But it doesn't seem to work. I get 404 error's when I type ANYTHING behind the default URI.
For routes, i use this
/**
* Set the routes. Each route must have a minimum of a name, a URI and a set of
* defaults for the URI.
*/
Route::set('test1', 'street/<id>')
->defaults(array(
'controller' => 'street',
'action' => 'index',
));
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
Any help would be gladly appreciated.
EDIT
I just tried #Darsstar 's instruction to go to /index.php/street, and it worked!
But now, when I try to go to /index.php/street/derp, I get an error saying
The requested URL derp was not found on this server.
So it's not yet working properly I guess
Since the /index.php/street version works go and read the Clean URLs tutorial if you haven't already. If you have, double check everything!
If you have just those two routes, in that order, /index.php/street/derp should have matched the route 'test1'.
The error message 'The requested URL derp was not found on this server.' says you went to /index.php/derp, not index.php/street/derp. Which would match the default route and be dispatched to Controller_Derp::action_index(), but it doesn't exist so Request_Internal::execute() throws a HTTP_Exception_404.
The default route is more of and example. The is a discussion on Kohana's issue tracker to remove it since a catchall default route is a bad practice. Routes should be specific. I recommend to remove it.
And if you think having a catchall route is a good way to catch all 404 requests, please let the Custom Error Pages tutorial prove you wrong.

Categories