Quickly Build RESTful APIs in PHP with Slim – Part 3

The first part of this mini-series showed how to use the Slim Framework to set up a really basic RESTful API. The second part discussed how to actually use that API to do the standard Get, Put, Post, and Delete of data. This part is going to discuss two things:

  • How to use jQuery to do Puts and Deletes
  • Make the API responses more useful

The final topic, How to use Slim’s middleware support to limit the API to valid users, is going to be the subject of Part 4. This post was lengthy enough without diving into that subject.

How to use jQuery to do Puts and Deletes
Since modern browsers do not support PUT or DELETE directly from a form, you have to use their support of XMLHttpRequest, and probably the easiest way to do that is with jQuery. To check your browser’s support of XMLHttpRequest, there is an excellent array of tests available at this site.

The code for doing a Get and a Delete with jQuery are almost identical:

$(document).ready(function(){
	$("#put").submit(function(event) {
		event.preventDefault();
		$.ajax({
			url: '/v1/' + $("#idPut").val(),
			type: 'PUT',
			data: $("#put").serializeArray(),
			success: function(data) { $("#results").html(data); }
		}); 
	});
	
	$("#delete").submit(function(event) {
		event.preventDefault();
		$.ajax({
			url: '/v1/' + $("#idDelete").val(),
			type: 'DELETE',
			data: $("#delete").serializeArray(),
			success: function(data) { $("#results").html(data); }
		}); 
	});
});

The (very simple) forms being submitted:

<form id="put">
	Update Commodore<br />
	Id: <input type="text" name="idPut" id="idPut"/><br />
	Name: <input type="text" name="name" /><br />
	URL: <input type="text" name="url" /><br />
	<input type="submit" />
</form>

<form id="delete">
	Delete Commodore<br />
	Id: <input type="text" name="idDelete" id="idDelete"/>
	<input type="submit" />
</form>

A couple of notes. If you are familiar with jQuery, this probably looks very familiar. The Put and Delete function almost identical to the Get and Post, except that they do not get the handy shorthand functions like jQuery.get() and jQuery.post(). For those not familiar with event.preventDefault(), it’s the proper way of telling jQuery not to submit the form (the default action). A lot of people know this, but a lot of people (including myself), have used return false for a long time, and there are actually some issues with that. Also, the #results in the anonymous success function is simply a div to display any results or error messages. Ideally, you’d want to use that success function to actually do something useful, like pop a jGrowl (a nice little library) message to inform the user that their action has been successful.

Make the API Responses More Useful
Previously, the API responses were ok, but they could be better. Here’s an example of what they’re returning now:
URL: http://local.api.ericbrandel.com/v1/

[
{"id":"12","name":"Commodore 128",
"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_128"},
{"id":"9","name":"Commodore 16",
"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_16"},
...
{"id":"8","name":"Commodore SX-64",
"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_SX-64"},
{"id":"3","name":"Commodore VIC-20",
"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_VIC-20"}
]

That’s great and all, but what if you wanted to send back more than just a list of the Commodores? What if you wanted to support sending back a status message or other important information? Simply throwing that on the end of the result will likely cause your client-side JavaScript to break. One option would be to return something like this:

{	
	"status": "success",
	"commodores":
		[
			{"id":"12","name":"Commodore 128",
			"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_128"},
			{"id":"9","name":"Commodore 16",
			"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_16"},
			...
			{"id":"8","name":"Commodore SX-64",
			"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_SX-64"},
			{"id":"3","name":"Commodore VIC-20",
			"url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_VIC-20"}
		]
}

To do that only requires a slight change to the code:

public function getAll() {
	try { 
		 $sql = "select * from commodores order by name";
		 $s = $this->dbh->query($sql);
		 
		 $commodores = $s->fetchAll(\PDO::FETCH_OBJ);
		 // Let's remove this
		 // echo json_encode($commodores);
		 $result = array("status" => "success" ,"commodores" => $commodores);
		 echo json_encode($result);
	} catch(\PDOException $e) {
		 echo 'Exception: ' . $e->getMessage();
	}
}

So, instead of just passing $commodores directly into json_encode(), it’s first added to an array containing the status message.

A key thing here is that the JSON you are sending back is also now far more extensible. You can now change what is being returned without breaking things going forward. For instance, if you were creating a rate limited API, you could add in the remaining number of calls the user can do during the allotted time period.

The errors can be handled in a similar way as well. Currently, they are just dumped, which will likely cause the client-side code to break:

echo 'Exception: ' . $e->getMessage();

Instead of that, send the client proper JSON:

$result = array("status" => "error", "message" => 'Exception: ' . $e->getMessage());
echo json_encode($result);

which will result in:

{
	"status":"error",
	"message":"Exception: SQLSTATE[28000] [1045] Access 
	denied for user 'api'@'localhost' (using password: YES)"
}

The client can now be setup to handle both successful requests and errors, by looking at the value of status. An updated jQuery Put might look like this:

$.ajax({
	url: '/v1/' + $("#idPut").val(),
	type: 'PUT',
	data: $("#put").serializeArray(),
	dataType: "json",
	success: function(data) { 
		if (data.status == "success") {
			alert("Happy face");
		} else if (data.status == "error") {
			alert("Sad face");
		}
		
	}
});

The other addition to this code is the datatType. dataType: “json” lets jQuery know to expect the server to return JSON.

Edit: Part 4 of this series is now live.

This entry was posted in jQuery / Javascript, PHP, Web Development and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *