The app in this tutorial will be called “Tasks”. I know, right? Super creative.
This first article will give an introduction to the technologies and the idea of the series. Also, we’ll cover project structure and entire PHP side of the story.
In second article we’ll cover Backbone.js side of the story and create simple tasks application which we’ll refine and upgrade in later articles (third and fourth one).
Table of Contents
Backbone.js
Backbone.js is a very light javascript framework that allows user to structure JavaScript code in MVC fashion by providing models with key-value data, binding and custom events as well as collections that can connect with with RESTful backend in seamless JSON fashion. It also provides rich API and view handling.
Source: http://backbonejs.org/
Slim
Slim micro framework is a simple but powerful PHP 5 framework for creating RESTful web applications. Slim is perfect for this tutorial as it provides exactly what we need and it’s super easy to use and develop on. In this series I’m using Slim version 1.6.4.
Source: http://www.slimframework.com/
Project (directory) structure
I have created the following directory structure:
[code lang=”html”]
– tasks (root directory)
– client
– server
– doc
[/code]
The idea of this app is to be built using client-server paradigm where we can swap either of these two components with complety different technologies. The only requirement is that they are using RESTful services.
As you can see I have separated “client” and “server” parts into two separate folders. In client folder we’ll put our backbone.js and client side part, whilst the server directory will hold our Slim framework (and server part).
There is one additional directory, “doc”, which will hold database model as well as SQL files.
Ok, lets see what our entities will look like as well as our database tables (and relations).
We have two entities – project and task. Project consists of tasks. Basic and nothing unusual here.
Go ahead and create a database and import tasks.sql into it. As you can see database holds some sample project/tasks.
Server part
Download Slim framework stable release from http://www.slimframework.com/install, unpack it and copy Slim directory (and its contents) to server directory in our project structure.
Slim framework is so slim that it doesn’t have database component so we’ll use NotORM for this. Download and extract to “server” directory.
Source: http://www.notorm.com/
Lets start with our server component. Create index.php in /tasks/server/ directory. We’ll instantiate our database layer:
[code lang=”php”]
<?php
require_once ‘NotORM.php’;
$pdo = new PDO(‘mysql:dbname=tasks;host=localhost’, USERNAME, PASSWORD);
$db = new NotORM($pdo);
[/code]
Next, we need to create CRUD (Create, Read, Update and Delete) operations on our two entities (project and task). REST uses HTTP methods for this operations:
– GET > read
– POST > create
– PUT > update
– DELETE > delete
Lets start with with Slim and our task entity (code is pretty much well documented and should be clear):
[code lang=”php”]
require_once ‘Slim/Slim.php’;
/*
* Creating new Slim application
*/
$app = new Slim();
/*
* Get tasks or single task (depending on whether ID was provided)
*/
$app->get(‘/task(/:id)’, function ($id = null) use ($app, $db) {
/*
* We check if $id was provided
*
* If it is, then we wish to fetch single task item, else we’ll fetch whole set
*/
if (null === $id) {
$data = array();
/*
* We’re fetching tasks and filling an array that we’ll return to the client
*/
foreach ($db->task() as $task) {
$data[] = array(
‘id’ => $task[‘id’],
‘task’ => $task[‘task’],
‘project_id’ => $task[‘project_id’],
‘date_created’ => $task[‘date_created’],
‘date_due’ => $task[‘date_due’],
‘status’ => $task[‘status’]
);
}
} else {
$data = null;
/*
* We’re fetching single task
*/
if ($task = $db->task()->where(‘id’, $id)->fetch()) {
$data = array(
‘id’ => $task[‘id’],
‘task’ => $task[‘task’],
‘project_id’ => $task[‘project_id’],
‘date_created’ => $task[‘date_created’],
‘date_due’ => $task[‘date_due’],
‘status’ => $task[‘status’]
);
}
}
/*
* We’ll output our result in JSON so we need to set ‘Content-Type’ HTTP header
*/
$app->response()->header(‘Content-Type’, ‘application/json’);
/*
* Outputing encoded $data
*/
echo json_encode($data);
});
[/code]
In line 11. we are creating new route with verb GET and mapping a callback function to a resource URI. /task(/:id) means that we are listening on a /task URI, and we accept optional ID (of a single task).
We are using closures and lambda functions to create inline callback functions.
Now if we visit http://localhost/tasks/server/index.php/task we’ll get JSON result set (of course if we did insert sample data):
[code lang=”javascript”]
[{
"id":"1",
"task":"Task A1",
"project_id":"1",
"date_created":"2012-07-10 00:00:00",
"date_due":null,
"status":"0"
},{
"id":"2",
"task":"Task A2",
"project_id":"1",
"date_created":"2012-07-17 00:00:00",
"date_due":null,
"status":"0"
},{
"id":"4",
"task":"Task A3",
"project_id":"1",
"date_created":"2012-07-18 00:00:00",
"date_due":null,
"status":"0"
},{
"id":"5",
"task":"Task A4",
"project_id":"1",
"date_created":"2012-07-29 17:28:51",
"date_due":"2012-07-31 12:34:56",
"status":"0"
}]
[/code]
Try to fetch single task just to see the difference, http://localhost/tasks/server/index.php/task/1.
With some simple .htaccess and Apache rewrite rules we could remove index.php from our URI but lets ignore it for now.
Rest of our task routes looks like this:
[code lang=”php”]
/*
* Create new task
*/
$app->post(‘/task’, function () use ($app, $db) {
/*
* We are reading JSON object received in HTTP request body and converting it to array
*/
$task = (array) json_decode($app->request()->getBody());
/*
* Inserting new task to DB
*/
$data = $db->task()->insert($task);
/*
* Again, setting appropriate HTTP ‘Content-Type’ header
*/
$app->response()->header(‘Content-Type’, ‘application/json’);
/*
* Outputing request
*/
echo json_encode($data[‘id’]);
});
/*
* Updating existing task (hence the ID param)
*/
$app->put(‘/task/:id’, function ($id) use ($app, $db) {
/*
* Fetching task for updating
*/
$task = $db->task()->where(‘id’, $id);
$data = null;
if ($task->fetch()) {
/*
* We are reading JSON object received in HTTP request body and converting it to array
*/
$post = (array) json_decode($app->request()->getBody());
/*
* Updating task
*/
$data = $task->update($post);
}
$app->response()->header(‘Content-Type’, ‘application/json’);
echo json_encode($data);
});
/*
* Delete specified task
*/
$app->delete(‘/task/:id’, function ($id) use ($app, $db) {
/*
* Fetching task for deleting
*/
$task = $db->task()->where(‘id’, $id);
$data = null;
if ($task->fetch()) {
/*
* Deleting task
*/
$data = $task->delete();
}
$app->response()->header(‘Content-Type’, ‘application/json’);
echo json_encode($data);
});
/*
* Runing the Slim app
*/
$app->run();
[/code]
Code (and its comments) should be self explanatory. Notice the $app->run(); at the end as this is needed for the app to run.
Project entity routes are analog to this so we’ll skip it. This concludes our server part (as well as PHP part). In next chapter we’ll switch to client part and Backbone.js.
You can download all files (including project entity and database schema) below:
I know you are eager to go further and use that Backbone.js, so go ahead to the Part 2 of the tutorial.
Arrgh, great stuff, but when will the next chapter arrive? 🙂
Can’t wait for it! ^^
Thanks for the comment, next chapter is coming in a few weeks.
Excelent solution but I had a problem with the returning, the response comes with all the current html code so I added an exit(); function to the last line of every route example:
$app->delete('/task/:id', function ($id) use ($app, $db) {
/*
* Fetching task for deleting
*/
$task = $db->task()->where('id', $id);
$data = null;
if ($task->fetch()) {
/*
* Deleting task
*/
$data = $task->delete();
}
$app->response()->header('Content-Type', 'application/json');
echo json_encode($data);
exit();
});
I never did found anything other than my data in output. There shouldn’t be any HTML in the server part though.
I am getting this error after visiting http://localhost/tasks/server/index.php/task :
Fatal error: Class ‘Slim’ not found in C:\wamp\www\tasks\server\index.php on line 13
I am using Slim version 2.2.0 could this be why?
Is the path to the Slim framework correct? Check line #8 and the require_once.
@Chet, are you using Composer by any chance? If so, use the autoloader composer gives you. If not, double check if your path is correct like Luka says. That might help.
this tutorial doesn’t seem to work in the latest version of slim
you get the error Class ‘Slim’ not found
you can fix it by doing this:
require_once ‘Slim/Slim.php’;
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
but then you only get a blank page when going to the index and the routes return 404 even after defining the routes then running $app->run()
Sorry, I’ll specify the Slim version in the article.
also seems like this tutorial could be a lot shorter on the php side by using f3’s map method to create the restful api
https://github.com/bcosca/fatfree#representational-state-transfer-rest
also comes with it’s own ORM without all this overhead
I haven’t run into the f3 earlier, looks interesting. Thanks for the link
Comments are closed.