Implementing api endpoints in Laravel REST API – part 3

Share me please

Scope of laravel api

This article is part of series explaining how to implement REST API using Laravel framework with PHP artisan generator. Additionally you can go back to my previous articles that describes step by step:

At this time I will focus on implementing api endpoints in Laravel. We will see how to define the endpoints and how to map them into models and database tables.

Generating model files

Undoubtedly the power of Laravel is with its Artisan generator. Using it we can create tones of code without any additional actions. We will use it to generate model files.

Model files help Laravel explicitly translate database tables into objects in PHP. Every table from database has its own model file with list of fields and type definition. To demonstrate the power Artisan let’s run the command that generates all models:

php artisan make:model Book
php artisan make:model Reservation

You should see as a result that two model files are created: Book.php and Reservation.php. Probably you will be surprised why we don’t generate models for users. The answer is very simple we don’t have to.

The User.php model was created for us when we generated the Laravel project out of the box.

Going deeper into laravel models

How one of generated models looks like ? There should not be any surprise. It is a small file like DTO class, but in a specific for Laravel way. For example User.php model filled automatically looks like:

namespace App;
 
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
 
class User extends Authenticatable
{
    use Notifiable;
 
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];
 
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

However it is nothing special we have two tables that are filled with the names of columns from database. As you remember we generated the columns in the previous article about creating connection to database with artisan.

The first table named $fillable contains of column’s names that will be available through REST API for end user. The array must be filled. The second array $hidden don’t need to be filled, but here we typed columns that will not be exposed publicly in any get responses from the REST API.

Now when you know what means every of two arrays it is high time to fill two new models with correct columns from tables:

class Book extends Model
{
    //
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'id', 'title', 'isbn', 'year', 'return_date', 'borrow_data', 'is_available', 'created_date',
    ];    
}

And the Reservation.php model:

class Reservation extends Model
{
    //
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'id', 'books_id', 'users_id', 'created_date',
    ];
 
}

Additionally you can get more information about models and meaning of all attributes you can find under the official Laravel website about Eloquent model mechanism.

Looks like we have database connection, mapping tables into models, but should be something more that will enable us connection between models and requests as json from our Laravel REST API.

Controllers in Laravel

Without a doubt you are absolutely right. This places are controllers. Controller is a php class that translates every post, get, put, delete requests (coming from web browsers or other applications to the server) into php objects like models. They help us to manipulate data both sides from/into models to get and store data in database or just run different services using the data.

It this time we will generate controller’s classes. Artisan generator helps us doing this job easily with one command:

php artisan make:controller UserController
php artisan make:controller BookController
php artisan make:controller ReservationController

As a result Artisan created for us totally new directory named Http with bunch of files inside of this. Let’s have a look inside the folder:

All three controller files are in Controllers directory created with some additional staff. However we are not interested in it for now. How the controller’s class look like ? What we need to define inside is to enable our first endpoints in REST API ?

Fill BookController with actions

Like everything in Laravel also controllers are simple PHP classes. This time the classes will help us to controll all crazy things that we need to have enabled for external world through endpoints. Let’s check BookController class:

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
class BookController extends Controller
{
    //
}

We see that our BookController class inherits from base Controller class (generated by Artisan for us). We have also enabled Controllers and Requests classes that we can use in our implementation.

Firstly we should start playing with the controller from putting inside it all standard types of requests like:

  • index – lists resources
  • show – displays one selected resource by id
  • store – creates resource
  • update – updates some fields of resource
  • destroy- removes resource by id

More or less you should have this skeleton of BookController class:

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Model;
use App\Book;
 
class BookController extends Controller
{
    //
    public function index()
	{
		$books= Book::all();
 
                return "ok";
	}
 
	public function show($id)
	{
		$brick = Book::find($id);
 
                return "ok";
	}
 
	public function store(Request $request)
	{	
		/*$title = $request->title;
		$isbn = $request->isbn;
		$year = $request->year;
		$return_date = $request->return_year;
		$borrow_date = $request->borrok_date;
		$is_available = $request->is_available;
		$created_date = $request->created_date;
 
		$book = Book::create(
		  [
		    'title' => $title,
		    'isbn' => $isbn,
		    'year' => $year,
		    'borrow_date' => $borrow_date,
		    'is_available' => $is_available,
		    'created_date' => $created_date
			]
		);*/
 
		// or shorter version
 
		$values = $request->only('title', 'isbn', 'year', 'return_date', 'borrow_date', 'is_available', 'created_date' );
 
		$book = Book::create($values);
 
                return "ok";
	}
 
	public function update(Request $request, $id)
	{
		$book= Book::find($id);
		$book->save();
 
                return "ok";
	}
 
	public function destroy($id)
	{
		$book= Book::find($id);	
 
                return "ok";
	}

In fact I believe that every action in BookController is self-describing.

BookController actions

In fact what we do in actions is:

  • finding all books in index action using Book:all() method
  • finding one book by id in show action using Book::find($id), where $id is correlated with database index
  • getting data from request (in store action) to save them using Book:create($values) method, as result we expect to receive object created in database
  • finding book by $id to update the object by data received from request
  • deleting book by $id in destroy action

In order to get the data from REST API we need to create the correct mapping between controller and website address. We name this mapping process as a routing.

Adding routes to REST API controller

Routing is very easy to configure. In order to define it we need to go to the /routes/api.php file that looks like below:

use Illuminate\Http\Request;
 
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
 
Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

At this point the question is what endpoint address we want to use for booking address. Let’s say that we are interested in running such address: /api/v11/book. The prefix ‘v11’ we can use as a version number of REST API.

Remove existing code in the api.php file and some new lines like below:

 
use Illuminate\Http\Request;
 
 
Route::group( array( 'prefix' => '/v11'), function()
{
	Route::resource('book', 'BookController');	
});

Overall we should be able to run GET /api/v11/book endpoint that correlates with index action in BookController. Let’s see the results in Postman tool. More about Postman you can read in this postman article.

Run the php artisan serve command to verify working of current state of REST API.

Error – In Inflector.php line 267

Besides seeing response from REST API you may see at this point one incurious error. The only thing that you know from the error is that something went wrong with syntax in your code.

Don’t worry you didn’t crash all the work we did. The reason why we have an error is because of version of one package in Laravel’s vendors. The package is doctrine/inflector and is in incorrect version. The version that generates error is used for PHP 7. As you remember our REST API uses PHP in version 5.6.

Installing doctrine/inflector package

Firstly we need to install package doctrine/inflector in the version 1.1 from github: https://github.com/doctrine/inflector/tree/1.1.x and exchange the content of our existing folder package into the downloaded files.

Secondly find in your project folder with the path: \vendor\doctrine\inflector and copy/paste files from unzipped package.

Finally hit again the command php artisan serve in your command line and now everything works without any errors.

Adding correct responses to BookController

In this case we removed error with inflator package. Running server with current state of REST API will send us back response as a simple string “OK”. Indeed it is not correct HTTP response from the perspective of REST API.

Correct responses should be organized like below:

  • 200 (OK) – request is correctly processed
  • 201 (CREATED) – new element in api collection is created
  • 400 (BAD REQUEST) – request is incorrectly prepared or any errors on api side occured

With all responses we will generate also message and if it is needed serialized version of modified/created object from the api collection.

New BookController class with improvements

In this section I prepared new version of BookController class with possible HTTP responses. Moreover full list of possible HTTP responses you can find under the link: https://restfulapi.net/http-status-codes/.

New version of BookController with correct HTTP responses looks quite well. However feel free to improve it on your own.

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Book;
 
class BookController extends Controller
{
    //
    public function index()
    {
        $books= Book::all();
 
        return response()->json([ 'data' => $books ], 200);
    }
 
    public function show($id)
    {
        $book = Book::find($id);
 
        return response()->json([ 'data' => $book ], 200);
    }
 
    public function store(Request $request)
    {  
 
        /*$title = $request->$title;
        $isbn = $request->isbn;
        $year = $request->year;
        $return_date = $request->return_year;
        $borrow_date = $request->borrok_date;
        $is_available = $request->s_available;
        $created_date = $request->created_date;
 
        $book = Book::create(
			[
			   'title' => $title,
               'isbn' => $isbn,
               'year' =>$year,
               'borrow_date' =>$borrow_date,
               'is_available' => $is_available,
               'created_date' => $created_date
			]
        );*/
 
        // or shorter version
 
        $values = $request->only('title', 'isbn', 'year', 'return_date', 'borrow_date', 'is_available', 'created_date' );
 
        $book = Book::create($values);
 
        return response()->json(['message' => 'Book is correctly added'], 201);
    }
 
    public function update(Request $request, $id)
    {
        $book= Book::find($id);
        $book->save();
 
        return response()->json(['message' => 'The book has been updated'], 200);
    }
 
    public function destroy($id)
    {
        $book= Book::find($id);
 
        return response()->json([ 'message' => 'The book has been deleted'], 200);
    }
}

In this section I replaced all returns with “OK” string into one simple function. However the response object enables json function that requires two arguments:

  • array with objects that will be translated into json response
  • type of HTTP response code

For example response for first funtion in BookController “index” returns list of all book objects available in database in format of json:

Filling Reservation and User controllers

We nearly finished enabling REST API endpoints. Book controller endpoints are enabled. At this time ReservationController and UserControllers wait for fill ing their body in the same manner like we did with BookController.

To clarify ReservationController is filled with code looks like:

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\Reservation;
 
class ReservationController extends Controller
{
    //
    public function index()
    {
        $reservations= Reservation::all();
 
        return response()->son([ 'data' => $reservations ], 200);
    }
 
    public function show($id)
    {
        $reservation = Reservation::find($id);
 
        return response()->json([ 'data' => $reservation ], 200);
    }
 
    public function store(Request $request)
    {
        $values = $request->only('books_id', 'users_id', 'created_date');
 
        $reservation = Reservation::create($values);
 
        return response()->json(['message' => 'Reservation is correctly added'], 201);
    }
 
    public function update(Request $request, $id)
    {
        $reservation= Reservation::find($id);
        $reservation->save();
 
        return response()->json(['message' => 'The reservation has been updated'], 200);
    }
 
    public function destroy($id)
    {
        $reservation= Reservation::find($id);
 
        return response()->json([ 'message' => 'The reservation has been deleted'], 200);
    }
}

UserController is filled with code and looks like:

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use App\User;
 
class UserController extends Controller
{
    //
    public function index()
    {
        $users= User::all();
 
        return response()->json([ 'data' => $users ], 200);
    }
 
    public function show($id)
    {
        $user = User::find($id);
 
        return response()->json([ 'data' => $user ], 200);
    }
 
    public function store(Request $request)
    {
        $values = $request->only('name', 'email', 'password');
 
        $user = User::create($values);
 
        return response()->json(['message' => 'User is correctly added'], 201);
    }
 
    public function update(Request $request, $id)
    {
        $user= User::find($id);
        $user->save();
 
        return response()->json(['message' => 'The user has been updated'], 200);
    }
 
    public function destroy($id)
    {
        $user= User::find($id);
 
        return response()->json([ 'message' => 'The user has been deleted'], 200);
    }
}

You can still read all api endpoints designed in my first article from series about rest api in Laravel framework. The article is available here.

Summary of api endpoints

It seems like we finished all codes for proper working of our rest api. However there are some missing points at this moment that you should focus creating production rest api. The list of activities that you need to do before running the rest api is:

  • add oauth protocol for authorization and authentication
  • move some parts of code in controllers into specialize services
  • handle invalid parameters sent to api

Moreover some of these activities we will solve in the next parts of rest api laravel series. Send me feedback if you have any questions.

One Comments

  • Dimitris 6th January 2021 Reply

    Perhaps you should write another article regarding the relationship between the models(belongsTo,hasMany)?

Leave a Reply