How to build a RESTful API in Slim 3 - Part 1: Application Setup

Introduction

Slim framework took off in 2013 when Josh Lockhart released the first version of the framework. The idea was to create a PHP micro framework that helps PHP developers quickly and easily write web applications and APIs. Over time, Slim has risen to risen to the point of competing with other PHP frameworks, like Laravel, Symphony, Codeigniter amongst others.

In this tutorial, you will learn how to build a voucher pool microservice application using Slim 3 framework. We will build a voucher pool which will be a collection of voucher codes that customers can use on a website in order to get discounts. When a customer uses a voucher code once, we want to know who used the code and when it was used. Part one of this article will focus on our application setup and how to create our database migrations.

What we want to achieve:

  • For a given special offer and an expiration date, we will to generate for each recipient a unique voucher code.
  • We need to provide an endpoint, which will receive a voucher code and email and validates the voucher code. In vase it is valid, return the percentage discount and set the date of usage.
  • For any given email, we need to return all valid voucher codes with the names of the user and the name of the special offer.

Prerequisites

  • Basic knowledge of PHP
  • Have Composer installed on your local machine
  • Have PHP setup on your local machine (version >= 7.1.3)
  • Git installed on your local machine

Setting up the application

To set up Slim 3, we will use a Laravel-like boilerplate. If you use Laravel a lot, you will like this boilerplate. Run this command in your project directory.

    $ composer create-project codecourse/slender voucherPool

You will notice you have a new project in your project directory. Next, navigate into the directory and install third-party packages needed for our application to work. Since we have a Laravel-like structure, we can as well integrate Laravel's eloquent ORM and enjoy the goodness of manipulating our database using object-oriented paradigm.

Run this command in your terminal to include illuminate database component in our codebase:

    $ composer require illuminate/database

Once this is done, our application needs to know how to handle Eloquent queries. To achieve this, create a database file inside the bootstrap directory.

    $ touch bootstrap/database.php

Open the database file and replace the content with this:

1//  bootsrap/database.php
2    <?php
3    
4    use Illuminate\Database\Capsule\Manager as Capsule;
5    
6    $config  = $container['settings']['database'];
7    $capsule = new Capsule;
8    $capsule->addConnection(array_merge($config,[
9        'charset'   =>  'utf8',
10        'collation' =>  'utf8_unicode_ci'
11    ]));
12    
13    $capsule->bootEloquent();
14    $capsule->setAsGlobal();

Next, we need to include this file in our application boot process. Open the bootstrap/app.php file and edit as follows:

1[...]
2    require_once __DIR__ . '/database.php';
3    require_once __DIR__ . '/../routes/web.php';
4    [...]

For this guide, we will use an SQLite database. Open the .env file and include the following database settings:

1DB_DRIVER=sqlite
2    DB_DATABASE=_insert_absolute_path_to_sqlite_file

Remember to insert the absolute path to our SQLite file here: ./db/database.db

Once that is done, we also want to load our database credentials in our boot process. Open the bootstrap/app file one more time and replace the content of the $app variable with this:

1// /bootstrap/app.php
2    [...]
3    $app = new Slim\App([
4        'settings' => [
5            'displayErrorDetails' => getenv('APP_DEBUG') === 'true',
6    
7            'app' => [
8                'name' => getenv('APP_NAME')
9            ],
10    
11            'views' => [
12                'cache' => getenv('VIEW_CACHE_DISABLED') === 'true' ? false : __DIR__ . '/../storage/views'
13            ],
14    
15            'database' => [
16                'driver'    => getenv('DB_DRIVER'),
17                'database'  => getenv('DB_DATABASE'),
18            ]
19        ],
20    ]);
21    [...]

Next, we will use Phinx to manage our database migrations. Run this command to install Phinx in our codebase:

    $ composer require robmorgan/phinx

Awesome!

Now that we have Phinx installed, we need to create the folder where Phinx will run our migration files from and finally create a database.db for our SQLite. Create this folder in your project directory

1$ mkdir -p db/migrations
2    $ touch db/database.db

This command will create a migration folder inside of a db folder.

With Phinx installed, we also need to initialize it in our codebase. Run this command to initialize Phinx:

    $ vendor/bin/phinx init

You will notice a new file in your codebase, phinx.yml. The file stores your database credentials and points phinx to the location of your migrations and seeder files

Next, in other for Phinx to communicate with our database, open the phinx.yml file and edit as follows

1development:
2            adapter: sqlite
3            name: ./db/database
4            suffix: ".db"    # Defaults to ".sqlite3"

Creating migration files

Before creating our migration files, we need to understand our database schema. We will have three tables, users, offers, vouchers. (See image below)

slim3-schema-diagram

To create our migration files, run the following commands on your terminal

1$ vendor/bin/phinx create CreateUsersTable
2    $ vendor/bin/phinx create CreateOffersTable
3    $ vendor/bin/phinx create CreateVouchersTable

This command will create our users migration file inside the db/migrations/ directory.

Next, open the users migration file and replace the content with this:

1// /db/migrations/timestamp_create_users_table.php
2    <?php
3    use Phinx\Migration\AbstractMigration;
4    
5    class CreateUsersTable extends AbstractMigration
6    {
7        /**
8         * Migrate Up.
9         */
10        public function up()
11        {
12            $users = $this->table('users');
13            $users->addColumn('name', 'string', ['null' => true])
14                  ->addColumn('email', 'string')
15                  ->addColumn('created_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
16                  ->addColumn('updated_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
17                  ->addColumn('deleted_at', 'datetime', ['null' => true])
18                  ->save();
19        }
20        /**
21         * Migrate Down.
22         */
23        public function down()
24        {
25            $this->dropTable('users');
26        }
27    }

With that done, open the offers migration file and replace the content with this:

1// /db/migrations/timestamp_create_offers_table.php
2    
3    <?php
4    use Phinx\Migration\AbstractMigration;
5    
6    class CreateOffersTable extends AbstractMigration
7    {
8         /**
9         * Migrate Up.
10         */
11        public function up()
12        {
13            $offers = $this->table('offers');
14            $offers->addColumn('name', 'string')
15                  ->addColumn('discount', 'integer')
16                  ->addColumn('expires_at', 'datetime')
17                  ->addColumn('created_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
18                  ->addColumn('updated_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
19                  ->addColumn('deleted_at', 'datetime',['null' => true])
20                  ->save();
21        }
22        /**
23         * Migrate Down.
24         */
25        public function down()
26        {
27            $this->dropTable('offers');
28        }
29    }

Finally, open the vouchers migration file and replace the content with this:

1// /db/migrations/timestamp_create_vouchers_table.php
2    <?php
3    use Phinx\Migration\AbstractMigration;
4    
5    class CreateVouchersTable extends AbstractMigration
6    {
7         /**
8         * Migrate Up.
9         */
10        public function up()
11        {
12            $voucher = $this->table('vouchers');
13            $voucher->addColumn('code', 'string')
14                  ->addColumn('offer_id', 'integer', ['null' => true])
15                  ->addColumn('user_id', 'integer', ['null' => true])
16                  ->addColumn('is_used', 'integer',['default' => 0])
17                  ->addColumn('date_used', 'datetime', ['null' => true])
18                  ->addColumn('created_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
19                  ->addColumn('updated_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
20                  ->addColumn('deleted_at', 'datetime', ['null' => true])
21                  ->addForeignKey('user_id', 'users', 'id', ['delete'=> 'SET_NULL', 'update'=> 'NO_ACTION'])
22                  ->addForeignKey('offer_id', 'offers', 'id', ['delete'=> 'SET_NULL', 'update'=> 'NO_ACTION'])
23                  ->addIndex(['code'], ['unique' => true]) 
24    
25                  ->save();
26    
27                  
28        }
29        /**
30         * Migrate Down.
31         */
32        public function down()
33        {
34            $this->dropTable('voucher');
35        }
36    }

Now that we have created our migration files, we need to run them. Run this command on your terminal:

    $ vendor/bin/phinx migrate

The last package we will install is the validation package, we will need this when we want to validate all inputs supplied to our application. Open your terminal and run this command:

    $ composer require awurth/slim-validation

Next, we also need to include our validation package in our application boot process. Open the bootstrap/app.php file and edit as follows:

1[...]
2    $container['validator'] = function ($container) { return new Awurth\SlimValidation\Validator; };
3    require_once __DIR__ . '/database.php';
4    require_once __DIR__ . '/../routes/web.php';
5    [...]

Conclusion

In this tutorial, we have looked at how to set up our Slim 3 framework application. We set up our application to use Laravel’s Eloquent to communicate with our database. We created our database schema and we set up Phinx to help with our migration files. Finally, we included a validation package to ensure that users submit the right data to our API.

In the next tutorial of this two-part series, we will proceed to build our controllers and models. We will also create endpoints and test the output data using Postman.

The source code to the application in this article is available on GitHub.