I'm new to the world of API programming, I just have a bit of a side project at work at the moment and I'm learning as I write, so bear with me.
I'm unsure as to the best way to implement an API for multiple different functions. At the moment I just have a test script I run and an apache redirect that redirects anything under /api to this script, call it TestAPI.php (so /api/anything will redirect). I pass the path variable of the API to the script (so in that example the path would be 'anything').
At the moment I'm just writing it for 1 purpose, to look up some data based on the path, and eventually be about to update and delete etc with PUT/DELETE etc (it's restISH not restFUL). This is fine at the moment where everything redirects to this script, but what about if I need 2 different functions? So I want to look up a different data set? So for example now /api/data1 would go to the 1st set and /api/data2 would go to the second. This is where I start to get unsure.
Do I simply have 1 mega script that grows and grows so that /api/data1 and /api/data2 redirect to the same place (and thus handle any errors like 404s there). Or do I have a script for /api/data1 and /api/data2 with separate redirects to each, then a generic catchall for 404s (I would always like to return JSON/XML rather than HTML for a 404, so I need at least logic to return based on the Accept header).
As a 3rd option, do I have some sort of frontline controller that catches everything, then calls off to the sub components? So 1 script that is redirects to for anything under /api, which then calls off to the required components or 404s if it's an invalid path. This seems like the best way to do it to me, but I have no idea how. Do I have some section of the site that only that script can call, or do I use cURL from the frontline controller to the back end API sections (as I'd need to pass POST/PUT data I assume I'd have to use cURL, is there any other way?). How is this best implemented in Apache?
Yes, you use a front controller. The front controller can use convention like first thing after /api processes the request
i.e.
/api/firstprocessor/method1
/api/firstprocessor/method2
/api/secondprocessor/method14
You can check out Zend_Framework for an example of this in action, or it can be something as simple as
$name = 'Script_' . $this->generateCommandName($request->getPathVariable(1));
$this->executeScript($name, $request);
public function executeScript($class, Request $request) {
if (file_exists("scripts/".$class.'.php')) {
//include the script
require_once "scripts/".$class.'.php';
//execute the script
$command = new $class;
$command->execute($request);
}
}
Then all your scripts just have an execute method that uses $request to get $_GET or $_POST variables
Related
Ive used a common approach to a custom MVC pattern
controller/action/param
$controller->action($param)
How can I implement a method of redirecting i.e. using header() because theres always an initial output from the main controller but Im not intentionally outputting anything.
NOT INTERESTED IN USING POPULAR FRAMEWORKS AND IM NOT TRYING TO REINVENT THE WHEEL JUST TRYING TO LEARN.
you can enable output buffering to store the initial output and flush it after the header function
ob_start();
echo "output";
header("Location: other.php");
ob_end_flush();
or you can use javascript to redirect:
echo '<script type="text/javascript">window.location='.$url.'</script>';
EDIT:
There is a common case when writing class, you accidentally leave a whitespace after the closing ?> tag, maybe it's causing the output.
to avoid such accidents just leave out the closing ?>, it's completely legal, unless you have only php in your file.
One way is to buffer all the output with ob_start functions. And in your redirect function you can use the header.
Another approach would be to send a meta tag for html redirect.
I wouldn't redirect, it is not necessary and may be time consuming (more requests, responses to/from the server)
I would use index.php (as a main controller) and handle any request to the server, then calling to the requested controller action and then calling the view to render the requested view, and finally the main controller sending the rendered view to the client.
Doing it this way you can use the index.php as a main controller or main app, and centralize anything you need to (segurity, cache, debug, logs).
In order to get the index controller handle every request you can use mod_rewrite.
This is one approach I did for my MVC, maybe this helps to you on understanding the flow (this is not the only one approach, check wikipedia or resources).
MAIN CONTROLLER: catches the request, and invokes the appropiate controller with the request data (POST / GET)
CONTROLLER: executes the action involved, calling models to retrieve the data, and sending the output data to the view, getting the output from the view and sending it back to the main controller.
MODEL: gets data from the database and operates it
VIEW: uses layouts, templates, and incoming data to build the html output
I have an external javascript file that uses the getScript() function to run another JS file.
I have those all on static.mydomain.com. (I'm new to setting up CDNs)
getScript() doesn't seem to allow cross-domain requests because my HTML is on domain.com. But then I tried using relative paths according to this post: Dynamic URLs in CSS/JS
It works for CSS but does not work for JS (specifically within the getScript() function). What's going on here? What are some ways to mitigate this problem when dealing with CDNs?
The getScript method actually makes an ajax call, hence the reason it's not working. Unless you need access to things like 'was the script successfully found' and the like, it's better to just write up a quick method like...
function addScript(source, domain) {
$("head").append("<script src='"+ (domain ? domain + source : source) +"'></script>");
}
That will just add scripts to the head of the page, and let you add an optional domain to point to in case you want to change it up.
For example let's say I have a controller called News. Methods of News include Create, Edit, Delete, etc. So let's say for users with javascript enabled, we can use AJAX to deal with these things and use the controllers if not. For example, going to /news we can create news, edit, or delete without leaving this page...with AJAX. Without javascript, we would have to go to /news/create, /news/edit/1, /news/delete/1, etc.
So then what way is more efficient to deal with AJAX requests? On each controller should I just have a conditional to check whether the request was sent with AJAX, something like:
if(isset($_POST['ajax'])) {
// serve ajax request
} else {
// serve regular request
}
Or should I create additional methods such as ajaxCreate, ajaxEdit, ajaxDelete, etc?
Shouldn't your AJAX requests be almost the same as your regular requests, except the regular requests have an additional layer (rendering a page) on top?
Your conditional check should simply regulate whether you respond with something like a JSON response, or a full page - in effect, what view you invoke (assuming an MVC-style setup).
Never use /news/delete/1 to delete news!
use POST /news/delete and pass a news id
With that said, both ajax request and regular requests should be made to the same URL. the difference is that regular requests will return an html response and an ajax request will return xml/json/etc
Page: /news/create/
// create new news item code goes here
// most newer javascript libraries set this header so you know the page
// was requested via ajax
if ( isset( $_SERVER['X-Requested-With'] && $_SERVER['X-Requested-With'] == 'XMLHttpRequest' ) {
//output status code or new news item
exit;
}
// not an ajax request load a view
require( 'views/news_create.php' );
I prefer to pull this functionality into an API. For example, I can have a folder structure like the following:
web
- api
- 1.0
- news
create.php
news.php
Then your page is accessible at the 'news.php' URL. Inside this file you consume your own api. The AJAX would also directly access the API at the 'api/1.0/news/create.php' URL for instance.
This leads to less API code in the controllers/views.
Bob
I have a website where people can place bids on products. So the first thing I have done is created a normal HTML-PHP version of the website which works great.
I have created some basic PHP functions that look at the url -> route you to the controller -> perform the action -> take the POST object etc.
Now I have implemented an AJAX version for this action but I want to know if I am doing the correct thing.
I made a new folder called ajax, and there I made a new controller for the ajax request. Now when somebody clicks on 'bid' then the request is hijacked by Javascript and sent to the AJAX controller. Now in the AJAX controller I strip down the link and then load the controller and model, and the work is done by the class.
Now the response the class has to send back is different for the AJAX version and for the HTML version. Now in the code of the class I have something like this
if(ajax version){
give ajax response
}else{
give html response
}
Now there are more things in the classes that are different.
Now my question is is it ok to use the same classes and config files for the ajax and non ajax function, or do they have to be seperate.
Or is there a more elegant manner of tackling this problem, maybe using interfaces.
If you follow the MVC pattern correctly, which it looks like from your description you are, then you should have a different action per request.
The flow should be
a different URL and action to receive the AJAX request
perform the business logic in your model
the action should then format the response in the style required (AJAX)
By having a switch (the if statement) in a shared controller, you run the risk of adding logic into the controller, which reduces the re-usability of your code.
So, put simply. Have a different PHP page for the different actions.
An easy way to do this is set a GET or POST parameter in the request when using AJAX, such as ajax=1 then check for this in the PHP:
if (isset($_GET['ajax'])){
give ajax response
} else {
give html response
}
AJAX requests have a header HTTP_X_REQUESTED_WITH with a value XMLHttpRequest
For example:
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
// Ajax
} else {
// Normal request
}
As a rule of thumb, it is usually better to keep the same Model and Controller and, based on data in the request (such as a value in the query string, or a custom header such as X-Requested-By), switch Views (probably between an HTML Template View and a JSON View).
I'm trying to figure out what the best way to go about rewriting urls to accommodate not only a directory structure, but appended actions for a menu for a site I'm building in PHP.
I have one page that dynamically loads content based on whatever "page" is being loaded. Includes are pulled based on what the page is. Right now that's tied to directories in my includes folder. So folder/page will include includes/folder/page.php.
If I have a structure like "root/page/action/param" that works fine as long as I say "whatever is in this place in the url is this." But I ran into trouble when I started adding folders that hold different levels of pages. If I explode page out to "folder/subfolder/page" then using placement to determine what is what goes out the window. I was thinking about using something like a hyphen as a delimiter to go "root/folder-subfolder1-subfolder2-page/action/param" so I can identify the page easily but I don't really like that option.
I also don't really like tying my includes directory structure to my urls but the only other way I would think to go about avoiding that would be to have an in-memory object to handle what exactly goes where. But I want to avoid that as I want the menu to be dynamic, based on an XML file that gets parsed and urls generated accordingly. I don't want the overhead of parsing the xml and not only generating URLs but also dynamically generating an in-memory object that has seemingly arbitrary rules.
Any suggestions?
PHPonTrax uses an implementation that I think would allow for this type of routing that you want, and it's open source so you could look at the code for some inspiration. (Note: I am not making any judgement as to the framework, I just have seen the code and think it might work for you).
Basically, you store your routes in an array and you provide some placeholder's as well so that you can have multiple possible controllers/actions/ids after a given path.
So if $routes[] is our array to hold everything, we could add:
$routes[0]['path'] = "foo/bar/:controller/:action";
$routes[0]['params'] = null;
$routes[1]['path'] = "baz/:action";
$routes[1]['params'] = array("controller"=>"blah");
$routes[2]['path'] = "catalog/:id";
$routes[2]['params'] = array("controller"=>"products", "action"=>"view");
$routes[3]['path'] = ":controller/:action/:id";
$routes[3]['params'] = null;
":controller", ":action", and ":id" are unique tokens that indicate what you are allowing a token to represent in the path. So "/baz/edit", "/baz/delete", "/baz/create", "/baz/funkify", etc are all valid provided controller "blah" has edit, delete, create, and funkify methods. If the path is "/catalog/1234", then '1234' is the id and it's a valid path if and only if you can find a product with id=1234. If your request is to "/products/dothis/12345", routes 0-2 don't match, but route 3 does, so you can then look for a controller file named "products", and provided it has a method named "dothis", then you can call "dothis(12345)". Obviously, if there's no "dothis" method, or '12345' can't be found, it's an invalid path and you can raise an exception. Note you need to provide a default route, and in this case it's $routes[3].
In your file structure, you can have:
/app
/controllers
/views
/etc, etc
Possible drawback is that in your rewrite, you send every request to one php script.
If this interests you: PHPonTrax. You will want to look at the router class and action_controller class (see the process_route() and recognize_route() methods).
Use Absolute Paths instead of relative in your menu html & scripts and you shouldn't have problems as it will always point to correct source.
Absolute Path Example (notice the /):
Home
Services
My Page
My Page 2