I have been looking at APIs and developing a REST API for a project that we are working on.
The API only accepts connections from one source in JSON format, I understand that bit fine.
If understand the majority of what is being said, however I don't understand the 3rd code example down and where the routing information would go.
The example they have provided is:
$data = RestUtils::processRequest();
switch($data->getMethod)
{
case 'get':
// retrieve a list of users
break;
case 'post':
$user = new User();
$user->setFirstName($data->getData()->first_name); // just for example, this should be done cleaner
// and so on...
$user->save();
break;
// etc, etc, etc...
}
The part I am unsure on is how to accept the original request i.e. /get/user/1 - how do you route that to the correct part of the script.
If there has been another SO question (I have searched for quite some time) or any further educational examples please do point me in the right direction.
Update
I have found a few routing PHP classes out there, but nothing thats just small and does what it says on the tin, everything seems to do routing + 2000 other things on top.
I now have all the classes I need for this project named as I wish to access them from the URI i.e.:
/data/users
/data/users/1
/hash/users
/hash/users/1
/put/users/1?json={data}
So all of these should use the users class, then one of the data, hash or put methods passing anything additional after that into the method as arguments.
If anyone could just explain how that bit works that would be a huge help!
Thanks :)
From the outset it looks like the website you've pointed out does not include a router or a dispatcher. There are plenty of PHP5 frameworks around which include a route and/or a dispatch or some description. (http://en.wikipedia.org/wiki/Comparison_of_Web_application_frameworks#PHP)
A router would be a class which have a list of predefined routes these could be really basic or quite complex, all depends on want you want to do. A good REST router IMO would look something like this:
:module/:controller/:params
And then the router would then router to the correct action based on the HTTP request (GET, POST, PUT, DELETE, OPTIONS)
public function getAction($id) {
// Load item $id
}
In your case, you will need a redirect rule that will send the request to something like this
index.php?user=id. Then you can process the get request.
The best solution I found for php REST architecture (including routing) is:
http://peej.github.com/tonic/
Related
I am a bit familiar with PHP MVC. Say, I have a controller like so:
class Customers{
public function method1( param1, param2, param3, ..., param_n ){
}
}
In my bootstraping page, I can grab a browser URL like so:
$url = explode('/', filter_var(rtrim( $_GET['url'], '/' ), FILTER_SANITIZE_URL));
I do $controller = $url[0] and $method = $url[1]. Any other elements in $url after the second index are parameters and can be collected into an array variable, say $params. Then I route to the relevant controller method and parameters like so:
call_user_func_array([$controller, $method], $params);
PLEASE NOTE: Above code is for illustration purposes. I always do checks in real-life situations. Those checks are not shown here, so do not use the above examples for serious projects.
Now, I want to implement a RESTful API using MVC pattern. What I already know:
No browser is involved, so $_GET['url'] is out of it.
The endpoint is obtained from $_SERVER['REQUEST_URI']
The method is obtained from $_SERVER['REQUEST_METHOD']
How do I route to an endpoint, for example, customers/{12345}/orders to get the orders of a particular customer with id 12345?
How can I do this?
The quickest way to achieve what you want would be to just use FastRoute. But where is fun in that :)
That said, I am a but confused about the premise of yours. Is it a REST API, which will be consumed by some other code (like in a phone or a 3rd party web app), or is it a proper website, where you just want to get pretty URLs?
Because if it's the former case, then making fancy URL parsing is completely pointless. Instead of messing round with URLs, you should be reading this article. Real REST API does not need a fancy URL parsing.
I will assume that what you are actually makingis a proper website, but with pretty URLs.
First you would have to implement a routing mechanism, which takes a list of regexp patterns, matches them agains your provided URL (which you could be extraction from $_GET['url'] or maybe $_SERVER[REQUEST_URI] (your code actually wouldn't care from where the URL was gathered ... you shouldn't be accessing superglobals inside functions/classes).
A simple version of this is explained in this answer. I am to lazy to rewrite it all =P
The second (and highly optional) part is creating code, that would take a human-readable route notation (like: /users/{id|[0-9]+}as an example) and turning it into the regular expression, which can be consumed by your routing mechanism.
If you decide to have the human-readable notations, then there are two major directions:
inline notations (see the example above or FastRoute)
config file (probably JSON or YAML) with notations
As for "how the end result might look like", you can probably look at the code sample here. That would illustrate one of the available option for the router's public interface.
TL;DR
Your question is vague and it is hard to understand what exactly would be helpful for you.
I am working on an API via which I embed images of country flags on my website & several others.
I am taking in 3 parameters i.e
Country (Name of Country - ISO Code or Full Name)
Size (Dimension of Image)
Type (Styles like flat flag, shiny round flag etc...)
Now, have everything setup correctly but stuck in handling URI.
Controller -> flags.php
Function -> index()
What I have now is :
http://imageserver.com/flags?country=india&size=64&style=round
What I want
http://imageserver.com/flag/india/64/round
I went through some articles and made this route but all of them failed
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/country/$1/size/$2/style/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/$1/$2/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index?country=$1&size=$2&style=$3";
I have also been having trouble with routes while writing my custom cms. Reading through your question, I see a couple issues that might very well be the answer you are looking for.
For starters, let's look at the routes you have tried:
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/country/$1/size/$2/style/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index/$1/$2/$3";
$route['flag/(:any)/(:num)/(:any)'] = "welcome/index?country=$1&size=$2&style=$3";
If you want to run the index method from your flags class, which it looks like you do, you don't want to route to the welcome class at all. Currently, however, you are. Your routes should look like:
$route['flag/(:any)/(:num)/(:any)'] = "flags/index";
That way, Codeigniter will run the index method from your flags class. You don't have to worry about the country, size, or style/type in the route. The best option there would be to use the URI segment function like this:
$country = $this->uri->segment(2); //this would return India as per your uri example.
$size = $this->uri->segment(3); //this would return 64 as per your uri example.
$style = $this->uri->segment(4); //this would return round as per your uri example.
You could then use those variables to query your database and get the correct flag or whatever else you need to do with them.
So to restate my answer with a little more explanation as to why:
The routes you have currently are running the welcome controller/class and the index function/method of that controller/class. This, obviously, is not what you want. So you need to make sure your routes are pointing to the correct controller and function like I did above. The extra segments of the URI don't need to be in your route declaration, so you would then just use the uri_segment() function to get the value of each segment and do what you need with them.
I hope this helps you. I may not have found an answer for my problem, but at least I could provide an answer for someone else. If this seems confusing to you, check out the user guide at http://ellislab.com/codeigniter/user-guide. The main links you need for this are:
http://ellislab.com/codeigniter/user-guide/libraries/uri.html
and
http://ellislab.com/codeigniter/user-guide/general/routing.html
Let me know if you need more help or if this helped solve your problem.
I am wanting to have a form of dynamic routing.
Basically I want to catch the 404 (done through routes.php 404_override).
This is easy enough.
But then what I want to do is run through a series of checks.
e.g pay no attention to the fact the is_xxx functions are not coming from anywhere
$what = $this->uri->segment(1);
if(is_vanity_name($what)){
//now I want to route this to the profile controller and call the display function like so
//display('vanity', $what);
}
elseif(is_region_name($what)){
if(($deal = $this->uri->segment(2)) !== false){
//now I want to route to the deals controller can call the display function like so
//display($deal, $what);
}
else {
//now I want to route to the deals controller and call the daily function like so
//daily($what);
}
}
elseif(is_deal_name($what)){
//now I want to route to the deals controller and call the display function like so
//display($what);
}
else{
$this->load->view('errors/404');
}
And thats a pretty basic example!
So my question is,
How would I go about re-routing to the other controllers once the decision has been made?
Broadly speaking, its kind of messy doing this with CodeIgniter. Coming from java land the RequestDispatcher.forward() takes care of these sorts of things, but in PHP space its usually a redirect(): there typically isn't the server side infrastructure to (effectively) pass requests from one PHP file to another without the client-side round trip.
Looking through Google's eyes: http://codeigniter.com/forums/viewthread/55212/P15/ is a long discussion with a CI helper that claims to let you do this, http://codeigniter.com/wiki/Wick/ is a library that appears to actually do it.
Let me know if either of those work out: you might be better off refactoring those out, but that does get tedious after awhile so I can understand the desire to just pass the handling off to the other controller (why bother round-tripping if you don't have to).
In most of the tutorials for PHP MVC design structures, a router class is used to take user input and look up the right controller in order to process that input accurately. The input normally takes the form of a url. For example http://example.com/foo/bar/ would ask the router to find the controller named foo and fire the method bar.
PHP also has an auto-include function which requires a consistent naming system to used for your classes. For example, if I wanted to get an instance of the class Foo, I would need the file to be called Foo.php and inside would be a class called Foo.
Unfortunately, in tandem, these mechanisms place competing demands on the naming system. The url is very 'front facing' and as such, clients will often need it to reflect the content of a particular page. For example, if I have a page that gives directions to a venue, the client may need this page to be at http://example.com/venues/directions/. Later the content may change and the number of venues is reduced to 1 and now the client wishes the url to read http://example.com/venue/directions/. Obviously, this is a very trivial example but I think the need to change urls occasionally is clear.
Since the urls are very tightly connected to the controller classes, a change to a url means the class file name, the class name itself and any instances of that class will all require changing. For more complex systems this seems very time consuming, to me.
What sort of solutions are there to this problem? It seems to me that a directory of sorts will be necessary in which relations between urls and controllers are stored such that any url could be used to call any controller regardless of what either is named. Thus we would have a venues controller and a venues url and when the client requests a change in the url, we just make a change in the directory translating the new "venue" into "venues".
I think the best way to do this would be in a database table but this then requires an extra query for every user request. Is a database the right way to implement a directory? Is a directory even the best way to solve the above problem? Is this problem even a problem in the first place???
Some solutions I have seen to this is to explicitly specify a routing "connection" to a controller/action.
For example, in NiceDog, you can specify routes like
R('venues?/directions')
->controller('VenueController')
->action('directionsAction')
->on('GET');
Allowing a regular expression to match the URL. (The expression above will match venue/directions or venues/directions) I like this method, because it decouples the controller naming scheme from the URL naming scheme.
Another more configuration-based approach would be to have some kind of explicit mapping:
$routes = array(
'venues?' =>
array('controller'=>'VenueController','action'=>'indexAction'),
'venues?/directions' =>
array('controller'=>'VenueController','action'=>'directionsAction')
);
And then a simple dispatch function:
function dispatch($url,$routes) {
foreach ($routes as $pattern=>$map) {
if (preg_match($pattern,$url,$matches)) {
$controller = new $map['controller']();
$action = $map['action'];
call_user_func_array(
array($controller,$action),
array_slice($matches,1)
);
return true;
}
}
throw new Exception('Route not found.');
}
A good way to solve this problem is for the MVC framework to allow you to specify your own routing rules. For example, see URI Routing in the CodeIgniter framework's documentation.
To go along with your example, this would allow you to remap requests for /venues/ to /venue.
Rather than using controller/action/key1/value1/key2/value2 as my URL, I'd like to use controller/action/value1/value2. I think I could do this by defining a custom route in my Bootstrap class, but I want my entire application to behave this way, so adding a custom route for each action is out of the question.
Is this possible? If so, how would I then access valueN? I'd like to be able to define the parameters in my action method's signature. e.x.:
// PostsController.php
public function view($postID) {
echo 'post ID: ' . $postID;
}
I'm using Zend Framework 1.9.3
Thanks!
While I don't think it's possible with the current router to allow N values (a fixed number would work) you could write a custom router that would do it for you.
I would question this approach, however, and suggest that actually listing all of your routes won't take long and will be easier in the long run. A route designed as you've suggested would mean that either your named parameters are always in the same order, i.e.
/controller/action/id/title/colour
or that they are almost anonymous
/controller/action/value1/value2/value3
With code like
$this->getRequest()->getParam('value2'); //fairly meaningless
Does it have to be N or can you say some finite value? For instance can you imagine that you'll never need more than say 5 params? If so you can set up a route:
/:controller/:action/:param0/:param1/:param2/:param3/:param4
Which will work even if you don't specify all 5 params for every action. If you ever need 6 somewhere else you can just add another /:paramN onto the route.
Another solution I've worked with before is to write a plugin which parses the REQUEST_URI and puts all the extra params in the request object in the dispatchLoopStartup() method. I like the first method better as it makes it more obvious where the params are coming from.