David Jones

Building an API with Slim

Installing Slim

The best way to install Slim is to use Composer. Run the following command to install everything we need.

composer require slim/slim

Next we need to create an index.php file in the route of our project with the following line in our PHP tags. The autoloader file is automatically generated by Composer so you can access all your libraries that have their own autoload information.

require 'vendor/autoload.php';

That's it! Pretty simple right?

Setting our app

In its most basic form we can instantiate a Slim application by using one line.

$app = new \Slim\Slim();

This is good and super easy but we might want to configure some settings before we start to create our routes. For this we can pass an array as the first argument when instantiating our application. For example we could specify the following.

$app = new \Slim\Slim(array(
    'mode'             => 'development',
    'debug'            => true,
    'cookies.encrypt'  => true,
    'cookies.lifetime' => '10 minutes'
));

A full list of settings can be found in the documentation.

Creating our first route

As we are making a RESTful API lets create routes that will give use CRUD functionality to a users table.

Our first route will get a user based on their ID.

// User GET
$app->get('/api/v1/user/:id', function ($id) {
    echo "Here is our ID: $id";
});

As you can see we are calling the get method and passing in an anonymous function where we define a variable for the URI parameter that will represent the ID. For example the URI user/1 would look for a user with an ID of 1.

Lets set up our database and run the following SQL to create our users table.

-- UP
DROP TABLE IF EXISTS `Users`;
CREATE TABLE `Users` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(255) NOT NULL,
    `password` varchar(255) NOT NULL,
    `created_at` timestamp NOT NULL,
    `updated_at` timestamp NOT NULL,
    PRIMARY KEY (`id`)
);

Lets also seed some users by running an insert query.

-- Seeds
INSERT INTO `Users` (`username`, `password`, `created_at`, `updated_at`) VALUES
    ('david.jones', 'password', NOW(), NOW()),
    ('dave', 'password01', NOW(), NOW()),
    ('david', 'password02', NOW(), NOW());

Setting up an ORM

In order to access our database we can use an Object-relational mapping (ORM) tool such as Eloquent. Yes, it is the same one that is shipped with Laravel.

This can also be installed using Composer. Lets add it to our composer.json file so it looks like this.

{
    "require": {
        "slim/slim": "^2.6",
        "illuminate/database": "*"
    }
}

When we run composer update Composer will install everything Eloquent needs. We are now ready to configure Eloquent and make our User model.

Configuring Eloquent

Lets create a new file called db.php where we can configure and instantiate Eloquent.

use Illuminate\Database\Capsule\Manager as Capsule;

$capsule = new Capsule;

$capsule->addConnection([
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'slim-api',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
]);

$capsule->setAsGlobal();
$capsule->bootEloquent();

The above code for the most part is pretty self explanatory. We are instantiating the Capsule object and adding a connection. We are then going to get it as global so we can access our models statically. This is the same as in Laravel. Then we are going to boot Eloquent. We just need to include this file underneath where we include the autoloader from Composer.

Creating our User model

In the root of our project create a directory called models and a file called User.php. Inside this we can add the following.

use Illuminate\Database\Eloquent\Model;

class User extends Model {

    public    $timestamps = true;
    protected $table   = 'Users';
}

This is all we need. The two things we need to configure is the name of the table and setting timestamps to true. This will make sure our created_at and updated_at columns are automatically updated with the correct values.

Our index.php file should now look something like this.

require 'vendor/autoload.php';
require 'db.php';
require 'models/User.php';

//set up our application
$app = new \Slim\Slim(array(
    'mode'             => 'development',
    'debug'            => true,
    'cookies.encrypt'  => true,
    'cookies.lifetime' => '10 minutes',
));

// User GET
$app->get('/api/v1/user/:id', function ($id) {
    echo "Here is our ID: $id";
});

// Run the application
$app->run();

We are now ready to query our database and form our response.

Querying the database

At this point we will be wanting to get a user by their username or all users. So we need to change our route to accept an optional parameter called username. If username is passed in as a URI segment then we will want to query the Users table by the username column. If not then we will return all users. Now our get route should look something like this.

// User GET
$app->get('/api/v1/user(/:username)', function ($username = null) {
    if (isset($username)) {
        $data = User::where('username', $username)->first();
    } else {
        $data = User::all();
    }

    die(var_dump($data));
});

Creating a JSON response

We have the data we need so now we need to create a JSON response so it can be returned to our client.

We can format the response headers in the slim to return the content type application/json. After we instantiate our slim application we can add this line to set our response to json. We can safely do this here because we will only ever return json.

// Set response as JSON
$app->response->headers->set('Content-Type', 'application/json');

We are currently have objects being returned from our models so we will need to make sure these are encoded into json before we return it from our get methods closure. Slim will automatically handle setting the response body when we call the echo() function so we do not need to worry about appending to or explicitly setting the response body. Although there are methods to do this (setBody() and write()). With everything in place our get route should look something like this.

$app->get('/api/v1/user(/:username)', function ($username = null) {
    $response = [];
    if (isset($username)) {
        $data = User::where('username', $username)->first();
    } else {
        $data = User::all();
    }

    if ($data) {
        $response['data'] = $data;
    } else {
        $response['error'] = 'user.no-results-found';
        $app->halt(404, json_encode($response));
    }

    echo(json_encode($response));
});

As you can see if we pass in a username we search for a user by their username and if a row is returned we set that into our response array. Likewise if we do not specify a username we get all users and there are any. We then encode the array into a json format and it is added to the response body when we call echo.

If we open up a browser and go to the route /user we should see the following response. Notice this is all the data that we inserted into our users table earlier.

{"data":[{"id":1,"username":"david.jones","password":"password","created_at":"2015-06-17 21:18:08","updated_at":"2015-06-17 21:18:08"},{"id":2,"username":"dave","password":"password01","created_at":"2015-06-17 21:18:08","updated_at":"2015-06-17 21:18:08"},{"id":3,"username":"david","password":"password02","created_at":"2015-06-17 21:18:08","updated_at":"2015-06-17 21:18:08"}]}

Now if we go to the route user/david we get the following response.

{"data":{"id":3,"username":"david","password":"password02","created_at":"2015-06-17 21:18:08","updated_at":"2015-06-17 21:18:08"}}

This is a basic set up of Slim and how to access a database using the Eloquent ORM. There are several ways this can (must) be improved if it were ever to be used in production. Things like authentication, a wider use of CRUD and proper error handling. These will be coming in future posts.