Quickly Build RESTful APIs in PHP with Slim – Part 2

I was playing around with Slim Framework recently, and decided to write-up another article about it, since it’s such a breeze. The original post highlighted how simple it was to get your Get, Post, Put, and Delete all wired up and ready to do work. This post will show how to create a little class that utilizes Slim and could serve as a launching point to building your own RESTful API. I’m planning one last post on Slim which will show how to use jQuery to do Puts and Deletes (since modern browsers typically only support Gets and Posts), make the API responses more useful, and how to use Slim’s middleware support to limit the API to valid users.

Time for a QuickNap
To make things more manageable, I created a new class for the API implementation. It’s bare bones at the moment and takes the DB connection information as arguments for its constructor.

require 'lib/QuickNap/QuickNap.php';

$dbUser = "api";
$dbPass = "shhhhhhhhhhhhh";
$dbHost = "localhost";
$dbName = "api";

$quickNap = new \QuickNap\QuickNap($dbHost, $dbName, $dbUser, $dbPass);

$quickNap->enable();

The QuickNap class uses the Slim Framework:

namespace QuickNap;

require 'ext/Slim/Slim.php';
\Slim\Slim::registerAutoloader();

class QuickNap {
...

Its constructor:

    public function __construct($dbHost, $dbName, $dbUser, $dbPass) {
        $this->dbHost = $dbHost;
        $this->dbName = $dbName;
        $this->dbUser = $dbUser;
        $this->dbPass = $dbPass;

        // create new Slim
        $this->app = new \Slim\Slim();
    }

All pretty straightforward stuff. The $quickNap->enable() call connects to the database and enables the Slim routes:

   public function enable() {
        // connect to the DB
        $this->dbConnect();
       
        // setup the routes
        $this->app->get('/', array($this, 'getAll'));
        $this->app->get('/:id', array($this, 'getItem'));
        $this->app->post('/', array($this, 'postItem'));
        $this->app->put('/:id', array($this, 'putItem'));
        $this->app->delete('/:id', array($this, 'deleteItem'));
        
        // start Slim
        $this->app->run();
    }

And that’s about where things were left in Part 1, with the addition of a database connection. In that database is the table “commodores” which has three fields: id, name, and url.

Database Table - Commodores
Database Table – Commodores

Get
In most RESTful APIs, there are multiple types of Gets. The two most common are a call that returns all the results, and a call that returns a specific result. Two routes were setup to handle these.

   $this->app->get('/', array($this, 'getAll'));
   $this->app->get('/:id', array($this, 'getItem'));

They route the request to the getAll function and getItem function. The :id parameter in the second “get” route is passed into the getItem function as its first argument.

   public function getAll() {
       try { 
            $sql = "select * from commodores order by name";
            $s = $this->dbh->query($sql);
            $commodores = $s->fetchAll(\PDO::FETCH_OBJ);
            echo json_encode($commodores);
       } catch(\PDOException $e) {
            echo 'Exception: ' . $e->getMessage();
       }
    }
    
    public function getItem($id) {
       try { 
            $sql = "select * from commodores where id = :id";
            $s = $this->dbh->prepare($sql);
            $s->bindParam("id", $id);
            $s->execute();
            $commodore = $s->fetch(\PDO::FETCH_OBJ);
            echo json_encode($commodore);
       } catch(\PDOException $e) {
           echo 'Exception: ' . $e->getMessage();
       }
    }

To run the Get, you can simply point your browser to the specific URL. For the getAll:
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":"6","name":"Commodore 64","url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_64"}
...
{"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 request a specific item via getItem:
URL: http://local.api.ericbrandel.com/v1/get/6

{"id":"6","name":"Commodore 64","url":"http:\/\/en.wikipedia.org\/wiki\/Commodore_64"}

Above each set of the results is the URL utilized (btw, it’s not a publicly available server). The /v1/ is used to indicate the API version. This is a best practice when developing APIs. If you are going to change the functionality of an API to any significant degree you want to offer it at a different URI than the original so you do not cause problems for the users of the existing API.

Post
Post saves new items. The biggest difference here is the need to access the values posted. Slim provides a very easy way to do that via a Request object. The post route and the postItem function:

$this->app->post('/', array($this, 'postItem'));
   public function postItem() {
        $request = $this->app->request();
        $name = $request->post('name');
        $url = $request->post('url');
        try { 
            $sql = "INSERT INTO commodores (name, url) VALUES (:name, :url)";
            $s = $this->dbh->prepare($sql);
            $s->bindParam("name", $name);
            $s->bindParam("url", $url);
            $s->execute();
        } catch(\PDOException $e) {
            echo 'Exception: ' . $e->getMessage();
        }
    }

To post a new item:
Url: http://local.api.ericbrandel.com/v1/post.

Put
The Put is used to update existing data. Thus the URL you hit contains the id of the item you wish to update. The put route and the putItem function:

$this->app->put('/:id', array($this, 'putItem'));
    public function putItem($id) {
        $request = $this->app->request();
        $name = $request->put('name');
        $url = $request->put('url');
        
        try { 
            $sql = "update commodores set url=:url, name=:name where id=:id";
            $s = $this->dbh->prepare($sql);
            $s->bindParam("id", $id);
            $s->bindParam("name", $name);
            $s->bindParam("url", $url);
            $s->execute();
        } catch(\PDOException $e) {
            echo 'Exception: ' . $e->getMessage();
        }
    }

To Put item #6:
Url: http://local.api.ericbrandel.com/v1/put/6

Delete
The delete also takes the id of the item via the URL and is used to delete an item.

$this->app->delete('/:id', array($this, 'deleteItem'));
    public function deleteItem($id) {
        try { 
           $sql = "delete from commodores where id=:id";
           $s = $this->dbh->prepare($sql);
           $s->bindParam("id", $id);
           $s->execute();
        } catch(\PDOException $e) {
            echo 'Exception: ' . $e->getMessage();
        }
    }

To Delete item #6:
Url: http://local.api.ericbrandel.com/v1/delete/6

Might Want to Add a Touch of Security
And with that, you have a functioning RESTful API. Now, there are obviously a few things that still need to be done. The data submitted should be sanitized and validated and you clearly don’t want an API that allows anyone to modify and delete your data. In the third part of this series, I’ll show how to use Slim’s middleware to verify an API key and prevent unknown users from modifying your data, without having to change any of your existing functions. I’ll also show how to provide better JSON responses for all of the API calls. I’ll link up part three here when it is complete. Edit: Part 3 is complete.

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

Leave a Reply

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