Writing a RESTful Web Service with Slim

Through a series of articles here at SitePoint you’ve learned about what is REST and how it works. In this article, I’d like to show you how to create a RESTful web service using Slim, a PHP micro-framework inspired by Sinatra, a Ruby framework. It’s well-suited for creating simple RESTful web services and comes with some minimal components like Router, Request, Response, and View. It’s very simple, and is easy to understand and use.

Say Hello to Slim World

To get started with Slim, you’ll need to download it first. It requires PHP 5.2+ and lets you write applications in both procedural style or in 5.3+ style, e.g. using anonymous functions. In this article I’ll be using 5.3 style coding for all examples, assuming you are already familiar with it.

To create a Hello World application, remove the default index.php file in the application directory and create a new index.php file with the following code:

<?php
require "Slim/Slim.php";

// create new Slim instance
$app = new Slim();

// add new Route 
$app->get("/", function () {
    echo "<h1>Hello Slim World</h1>";
});

// run the Slim app
$app->run();

Your first Slim application is ready now. If you access index.php through your browser, you should see a big “Hello Slim World.”

To use Slim in your applications, you need to include Slim.php and Slim will autoload all of the other files that it needs. Then you can create one or more instances of the Slim object and add your routes.

The Slim constructor accepts an array of application configuration values. MODE, TEMPLATES.PATH and VIEW are some important configurations that we often use. Use MODE to set the application environment to be used, like development or production. TEMPLATES.PATH sets the location to use for template files. Slim uses Slim_View to render the view by default, but you can write custom view handlers and attach them to Slim by using the VIEW value. The following example shows how to to create a new Slim instance with a custom TEMPLATES.PATH and sets the environment to development mode.

<?php
$app = new Slim(array(
    "MODE" => "development",
    "TEMPLATES.PATH' => "./templates"
));

The most important part of creating an application using Slim is creating routes. A route helps to map a URI to a callback function for specific request methods. Slim provides a simple and intuitive way to map the same URIs with different requests methods. It will invoke the first callback function that matches the current URI and request method, or produce a 404 error if it is unable to match. After adding the routes, you need to call the run() method on the Slim instance to run the application.

Writing a Library Service

As a more in-depth exercise, let’s create a simple Library management web service application using Slim. In this application we’ll be able to list, add, delete, and update book details using web service calls.

The following table lists the endpoints that will be supported by the web service:

For database interactions, I’ll use NotORM, a PHP library written by Jakub Vrána as an alternative to ORM which provides a simple and intuitive API to work with data from your database. NotORM uses PHP’s PDO extension to access the database, and so a PDO instance is passed to NotORM‘s constructor.

<?php
require "NotORM.php";

$pdo = new PDO($dsn, $username, $password);
$db = new NotORM($pdo);

Listing Books

The first endpoint lists all of the books in the library, so let’s use Slim to create the endpoint and return the data encoded in JSON format.

<?php
...
$app = new Slim(
    "MODE" => "developement",
    "TEMPLATES.PATH' => "./templates"
);

$app->get("/books", function () use ($app, $db) {
    $books = array();
    foreach ($db->books() as $book) {
        $books[]  = array(
            "id" => $book["id"],
            "title" => $book["title"],
            "author" => $book["author"],
            "summary" => $book["summary"]
        );
    }
    $app->response()->header("Content-Type", "application/json");
    echo json_encode($books);
});

get() is a Slim method that routes to a GET request on the given URI. It’s first argument is the URI and last argument is a callback function. The use keyword enables us to access the outer variables from inside the scope of the anonymous function.

Within the function, we create an array of books by looping through each record returned by the database ($db->books() returns a traversable reference to the books table). The response’s Content-Type header is sent as “application/json” and we emit the encoded array of book data.

Now let’s write the endpoint for getting the details of a book with a given ID:

<?php
...
$app->get("/book/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $db->books()->where("id", $id);
    if ($data = $book->fetch()) {
        echo json_encode(array(
            "id" => $data["id"],
            "title" => $data["title"],
            "author" => $data["author"],
            "summary" => $data["summary"]
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "Book ID $id does not exist"
            ));
    }
});

Here we have added a parameter to Route for passing the ID of the book. While executing this route, Slim will invoke the callback function with the value for the parameter as an argument.

Note that the parameter is mandatory. You can make it optional by placing it inside a parenthesis like: /book(/:id) If you are making a parameter optional, though, you won’t be able to specify parameters for the callback function. In that case you can make use of the func_get_args() function to get any arguments passed to the callback.

Adding and Editing Books

Now let’s address the endpoints responsible for adding and updating book information. We’ll use the post() method to add new data, and put() to update existing data.

<?php
...
$app->post("/book", function () use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $app->request()->post();
    $result = $db->books->insert($book);
    echo json_encode(array("id" => $result["id"]));
});

$app->put("/book/:id", function ($id) use ($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $db->books()->where("id", $id);
    if ($book->fetch()) {
        $post = $app->request()->put();
        $result = $book->update($post);
        echo json_encode(array(
            "status" => (bool)$result,
            "message" => "Book updated successfully"
            ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "Book id $id does not exist"
        ));
    }
});

$app->request() returns the current request object (Slim_Http_Request) with POST or PUT data. You can get the POST values using the post() method of this object, and the PUT values using the put() method. Here we’re assuming both POST and PUT data is key/value pairs of information with table column names as keys. In a real-world application, you will need to add some validation and error handling, but I’ve omitted it here for the sake of simplicity.

If you plan on accessing your Slim application from a browser, you won’t be able to easily make PUT requests, as browsers don’t normally expose the method via HTML. To overcome this, Slim have a provision that let’s you override POST requests to PUT by placing a hidden field in your form. The field’s name should be “_METHOD” and the value set to “PUT”.

<form action="#" method="post">
 <input type="hidden" name="_METHOD" value="PUT">
 Title: <input type="text" name="title"><br>
 Author: <input type="text" name="author"><br>
 Summary: <textarea name="summary"></textarea>
 <br>
 <input type="submit" value="Submit">
</form>

Deleting Books

Now that we have the endpoints to add, edit, and list books in our web service, the next obvious thing we need to is the endpoint to delete books. It should accept the ID of book to be deleted and remove the corresponding record from the database.

<?php
...
$app->delete("/book/:id", function ($id) use($app, $db) {
    $app->response()->header("Content-Type", "application/json");
    $book = $db->books()->where("id", $id);
    if ($book->fetch()) {
        $result = $book->delete();
        echo json_encode(array(
            "status" => true,
            "message" => "Book deleted successfully"
        ));
    }
    else{
        echo json_encode(array(
            "status" => false,
            "message" => "Book id $id does not exist"
        ));
    }
});

Everything is pretty straightforward here. First we fetch the row corresponding to the given ID from the database, just like we have done when getting book details earlier. Calling the delete() method on the row object will delete that record from database.

We have created all the necessary endpoints related to books. In some cases you may want to have a single route that will respond to more than one request methods. It can be implemented using the map() method of Slim, which is explained here.

Summary

In this article we’ve discussed creating a RESTful web service using the Slim framework. Now you should be able to create your own web service applications without much trouble.

Of course, there are many more things you can do than the simple things discussed here. You can have routes with many parameters, data validation, and so on. So dig into it and use tools like Slim and NoORM to help you achieve your objectives.

You can download the source code of this article from GitHub.

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.volny.cz/pvalen/ Bugg in Editing Books

    Part Editing Books does not work because the array $post contains “_METHOD”=”PUT”. Therefore, it does not update the database. This item must be removed and then to call the function $book->update($post);

  • http://astaza.com/ games

    thank you sir it’s very useful but did you test it i think this notorm are complex thank you anyway

  • vector

    its really helped me a lot, but i want more examples to practice,
    is there any documentation on RESTful PHP?
    if so please provide me a link for the documentation….
    thank you

  • zewa

    hi there had an issue getting the first step to work.
    For all getting the same 500 internal error when trying the first part replace the line:
    $app = new Slim();
    with
    $app = new SlimSlim();

    Regards
    Zewa

  • Santiago L Caballero

    this code is KO! very bad