is theoretically possible use more services for routing?
For example if somebody use Silex and has this code:
$app = new Silex\Application();
$app->get('/test/{id}', function ($id) {
// ...
});
$app->run();
And i create api using Slim like that:
$app = new \Slim\Slim();
$app->get('/api/' . $version . 'something', function () use ($app){
$data = $app->request->params();
});
$app->run();
How user could implement my API withou rewrite Slim route function to Silex route function?
Thank you very much.
Giving a quick though, the way I see it you have 3 options:
Refactor controller closures to a named function
Both, Silex and Slim[1] can use any form of callable, so instead of passing a closure just pass a function name (or an array with class name, method name) or any other callable. This way with 1 declaration you'll be able to call it from both Slim and Silex (yes, you have to define the routes on both sides).
This has its drawbacks as the controller signature is different for the 2 frameworks, so you'll need to hook into silex flow and change the parameters (you have the Kernel.controller event to do that).
Also you'll need to redefine all your services and use the container wisely (i.e, don't use it as a service locator in your controllers).
This is probably the most inflexible way.
Define the routes in Silex and instatiate a Slim APP inside to call and return that
You'll need to define the api routes again in Silex and in each route you can try to instatiate the Slim APP (using something as require "path/to/the/slim/app/php/file.php") and then force the run method with the silent option on[2]
You'll probably need to refactor your Slim application a bit, you'll have to define your app in a file and call the run method in another.
Create a Silex middleware to handle all the /api/ calls
The easiest (?) way that I can think of is redefine the second option and create a catch all incoming request to the /api/ mount point by creating a Silex middleware that checks for each incoming request if the request has the /api/ string inside the request. Then you'll simply forawrd the request to the Slim application as I've told you in the second option.
Using this method you don't need to redefine the routes in Silex as everything under the /api/ point will be forwarded to the Slim application.
NOTE
In all cases you'll probably need to transform the response given from Slim to a Symfony response. Slim 3 uses the new PSR-7 HTTP interface so you have a solution here already. Slim 2 echos the response directly so you'll need to catch that and put it inside a Symfony response (new Response(ob_get_clean())
[1] This is for the Slim3, Slim2 may also be able to do that but I'm not sure (and the signature is different!)
[2] Again this is for Slim3, Slim2 does not has this option, so you'll need to figure something to get the response (maybe ob_get_clean()?)
Related
I am creating APIs for an app. Now app developer wants me to create a fixed base url and pass the ROUTE NAME (Which will point to controller function) as POST variable. Example:
http://example.com/Api
and POST variables like:
action=>'ROUTE_NAME'
But in laravel we can define the routes based upon the url parts as:
http://example.com/Api/ROUTE_NAME
I have tried using a single controller and loading the other controllers based upon SWITCH statements. But that doesn't seem to be a standard practice as i need to add switch condition every time I'll create a new API. Also middleware will not work on the loaded controllers dynamically.
Is there a way in laravel to achieve this? I am using laravel 5.4
You could implement a middleware that listens on the /Api route, which gets the ROUTE_NAME from the $request, then you could use the Route() helper function to find the url of that named route, then redirect the request to that route.
Something like:
// Generating ROUTE_NAME url...
$url = route($request->route_name);
// Redirect to that route...
return redirect()->route($url);
Obviously you'll need to add code to handle if it doesn't find a route etc, maybe return a json response back with a proper error code etc.
This is my long quest to develop web apps using core PHP and follow the best possible practices and not using a framework. I have achieved many things by structuring my project a better way. However ...getting a clean URL is often a problem for large apps.
Till now...I have used the Slim Framework only for creating RESTFUL services outside my web apps.
I am using Slim Framework to create APIs for a PHP project. Now, I have an install of Slim up and running fine. I have my routes talking to the database and doing what they're supposed to do, generally. My question has to do with modularizing the code. At the moment, all my routes are defined in my index.php file in the root directory. I would very much like to separate these out, say into a /controllers folder.
As I liked the way Slim makes pretty good URLs...I am wondering if it is possible to use Slim as my app architecture ...and let all my pages or APIs be accessible through the Slim index.php.
Yes pretty easily here are the steps I have taken on a recent project.
First lets say your have a HomeActionController
class HomeActionController {
//The below line I have moved into an abstract Controller class
public $view = null;
//This is using Slim Views PhpRenderer
//This allows for a controller to render views can be whatever you need
//I did not like the idea of passing the whole DC it seemed overkill
//The below method I have moved into an abstract Controller class
public function __construct(\Slim\Views\PhpRenderer $view = null){
if($view != null){
$this->view = $view;
}
}
//View could be any action method you want to call it.
public function view(Request $request, Response $response, array $args){
$data['user'] = "John Doe";
return $this->view->render($response, 'templates/home.php', $data);
}
}
Now you need to be able to call an instance of this controller from a route so you need to add the controllers you have to the DC
Where ever you are creating your instance of slim you will need to get the DC and add an instance of your controller:
$app = new \Slim\App($config['slim']);
// Get Dependency Container for Slim
$container = $app->getContainer();
$container['HomeActionController'] = new Controller\HomeActionController($container['view']); //Notice passing the view
As a note the above instantiations could have been a closures but I did not see the point at the time or making them. Also, there are ways to lazy load that I have not explored yet see here for more information.
Now the last thing you need to do is be able to call these on the routes which is not a huge challenge.
$app->get('/home', 'HomeActionController:view');
Granted you cannot have an action with parameters but I have not had an issue just passing them along in the request and then getting them from there.
If you want to create a app with no framework, then i would recommend looking through this small github repo:
https://github.com/PatrickLouys/no-framework-tutorial
It goes through with you settings everything up in terms of routing, plus would make everything go through the index.php in a public folder like your asking.
I have a simple silex application and using a lot of different externals controllers.
I am using register and mount to connect it to my application.
$app->register($externalController = new ExternalController());
$app->mount('/control', $externalController );
It adds the routes login, logout, etc in its service provider class:
$controllers->get('/start', 'user.controller:loginAction')
->bind('control.start');
I want to add an event or middleware listener to actions provided by it.
I have searched the silex and symfony documentation, but I didn't find an easy way.
I have tried to use $app['controllers'], but this returns a ControllerCollection without any possibility to change something (or I didn't understand it).
What is the recommended way to add a new listener to an existing non self written controller?
I have found one possible way in the meantime to flush the controllers to create the RouteCollection and retrieve it via binding name.
You will receive a Route instance and can use there the normal middleware listener methods like before, after and so on.
$app->flush();
$route = $app['routes']->get('control.start');
$route->before(function(Symfony\Component\HttpFoundation\Request $request) use ($app) {
throw new RuntimeException('You should see me.');
});
I'm working in a RESTFul API with laravel, and I want to use content negotiation in my project, but I don't know how to accomplish that.
I have my controllers separted by api version, and I want to distinguish between api versions and use the correct controllerdepending on the version.
My API router is:
Route::group(array('prefix' => 'api'), function() {
Route::resource('users', 'API\V1\UsersController');
});
Should I create a api.type filter to use in my route group or Should I do that in the route group clousure or maybe, in each controller?
Don't be afraid to branch out your application logic into library classes. You don't have to fit everything inside of Laravel's given folder structure.
In fact, adding in your own namespaced group of classes can have a big benefit. You can see some setup on creating your own application library here.
Once you have that set up, you can create a class whose responsibility is to decide what content type to return. This might be based on an Accept header, URL parameter or whatever you define (That's up to you, the API creator).
Perhaps this class will take the Accept header and normalize it to something like "json", "xml" and "html".
The Request class has some methods to help you if you do it via the Accept header.
So, in pseudo code (syntax errors to follow!), your controller might do something like this:
/* GET /api/users */
public function index()
{
// Might return "json" or "xml" or "html"
$contentType = \My\ContentType\Class->getContentType();
$users = User::all();
// Not shown here is logic to set `content-type` header in the returned response (json vs xml vs html)
// Perhaps a method to go into your implementation of \My\ContentType\Class
return View::make('users.'.$contentType)->with(array( 'users' => $users ));
}
That's just an idea of what you might do. The key point is to get yourself working in a library that you can put business logic into to get you started with an idea of how to accomplish adding in business logic for your application.
Hope that helps.
I'm exploring a few PHP frameworks and the current front runner is Kohana.
Having a Rails background I've become used to what the rails community calls "RESTful" routes. So a "GET /posts" displays all posts and is handled by the index method of the Posts Controller. A "POST /posts" creates a new post object and is handled by a different method of the Posts Controller.
Since the path in both these 2 requests is identical, the router needs to make decisions based on the HTTP method.
Is the router in Kohana capable of doing this?
Kohana does not support RESTful routes by default, but there is a RESTful module that adds support for it. See the RESTful wiki for usage.
Kohana v3.x supports RESTful controllers directly. Just extend Controller_REST instead of Controller and all the route action will be the request method. (A POST request would be targeted to action_post, etc.)
You could also add these lines to your controller's before() method:
if ($this->request->method() == "POST")
{
$this->request->action("post_".$this->request->action());
}
so GET /controller/posts will be handled by the action_posts() method in your controller, while POST /controller/posts will be handled by the action_post_posts() method.
PS: The built-in Controller_REST was removed in Kohana 3.2
Checking the HTTP method in the class constructor feels like poor design to me. Like Rails, Kohana 3.3 can create RESTful routes in the router (where they belong).
Check out the documentation for Kohana 3.3 Route Filters.
Here’s an example:
Route::set('Posts', 'posts/<id>', array('id' => '\d+'))
->filter(function($route, $params, $request) {
$params['action'] = strtolower($request->method());
return $params;
})
->defaults(array(
'controller' => 'Post',
));