on Tutorials

Creating simple tasks app with Backbone.js and Slim micro framework (part 1)

11 comments
Tasks app with Slim and Backbone.js
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon

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).

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:

- tasks (root directory)
    – client
    – server
    – doc

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).

Database entities

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:

<?php
require_once 'NotORM.php';

$pdo = new PDO('mysql:dbname=tasks;host=localhost', USERNAME, PASSWORD);

$db = new NotORM($pdo);

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):

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);
});

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):

[{
    "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"
}]

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:

/*
 * 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 (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:

Source files

I know you are eager to go further and use that Backbone.js, so go ahead to the Part 2 of the tutorial.

Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon



  • Till

    Arrgh, great stuff, but when will the next chapter arrive? :)
    Can’t wait for it! ^^

    • http://codeforest.net Zvonko Biškup

      Thanks for the comment, next chapter is coming in a few weeks.

  • Gonzalo

    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();
    });

    • http://lukapeharda.com Luka Peharda

      I never did found anything other than my data in output. There shouldn’t be any HTML in the server part though.

  • Chet D

    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?

    • http://lukapeharda.com Luka Peharda

      Is the path to the Slim framework correct? Check line #8 and the require_once.

  • http://www.goodbytes.be GoodBytes

    @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.

  • adrian

    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()

    • http://lukapeharda.com Luka Peharda

      Sorry, I’ll specify the Slim version in the article.

  • adrian

    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

    • http://lukapeharda.com Luka Peharda

      I haven’t run into the f3 earlier, looks interesting. Thanks for the link