How to create a REST API (RestFul) in CodeIgniter 4?

- Andrés Cruz

En español
How to create a REST API (RestFul) in CodeIgniter 4?

Remember to watch the complete video to create a Rest Api in CodeIgniter where we explore many more tricks that will surely help you a lot :)

 

The Rest APIs are a set of methods that can be consumed via GET, POST, PUT, PATCH, DELETE requests, in order to communicate our web application, in this case CodeIgniter 4 with other types of applications, Flutter, Vue and a long etc.

REMEMBER THAT a Rest API is not a protocol, it is not a standard, therefore it is not necessary to follow a particular pattern or specific order, you are free to define the methods as you see fit to perform the actions that you consider.

One of the features that the new version of CodeIgniter 4 brings with it is that we can now create a Rest API without the need to install third-party dependencies; For this, we have to create a class of type ResourceController in which we have to forcefully specify the model and the default format:

    protected $modelName = 'App\Models\MovieModel';    protected $format = 'json';

So, let's see how we can create a CRUD Rest Api or ApiRest using our CodeIgniter 4 (ResfFul).

The models

As you can see, when we define a controller of this type, of type Resource or ResourceController, we are binding a model to said class; In this case we are using the following model, which is the one we use in my CodeIgniter 4 course

<?php /blog/codeigniter/curso-para-dar-los-primeros-pasos-en-codeigniter-4-el-framework-php-del-futuro
namespace App\Models; 
use CodeIgniter\Model; 
class MovieModel extends Model {   
     protected $table = 'movies';    
     protected $primaryKey = 'id';   
     protected $allowedFields = ['title', 'description','category_id']; 
}

And this is used by the previous model:

<?php
namespace App\Models;
use CodeIgniter\Model;

class CategoryModel extends Model
{
    protected $table = 'categories';
    protected $primaryKey = 'id';
    protected $allowedFields = ['title'];
}

Creating the resource type class for the Rest API

We are going to create a controller class of type resource for our RestApi that implements the operations of type CRUD:

<?php
use CodeIgniter\RESTful\ResourceController;
class RestMovie extends ResourceController
{
    protected $modelName = 'App\Models\MovieModel';
    protected $format = 'json';
}

As you can see, we have the common operations to manage an entity that in this case would be a Movie, but it can be anything, a Post, an article...

Creating the Restful API in CodeIgniter 4

Before starting to show the operations, remember that this is a resource type controller, therefore, all the methods that we are going to use for the CRUD have defined names to which their functions refer; for example, the function that is in charge of displaying a list is called index or that of displaying the detail of the show entity and so on for the rest.

Customize the response of the Rest Api

I'm going to create a helper function in this very class that will allow me to return responses in a custom format for easy consumption; items like:

  1. Data
  2. Response status code
  3. Message

They are components that I always like to return to the Rest response and this is to give more information and flexibility when working with this type of structure:

function genericResponse($data, $msj, $code)
{
    if ($code == 200) {
        return $this->respond(array("data" => $data,                "code" => $code));
        //, 404, "No hay nada"        
    } else {
        return $this->respond(array("msj" => $msj,                "code" => $code));
    }
}

Creating the Restful methods

Get operations

For the Get operations, which are the simplest and we can easily use test from our browser, they allow us to obtain a particular record, given the identifier and all the records, for this it is not necessary to pass any identifier:

public function index()    {        
    return $this->genericResponse($this->model->findAll(), NULL, 200);    
}    
public function show($id = NULL)    {       
     return $this->genericResponse($this->model->find($id), NULL, 200);    
}

Post operations

With this type of operation, you are interested in creating a new resource, in this case a movie, therefore, we have to do some verification, how to process or validate the data that we are going to receive, and its subsequent saving in the database. then in response we can simply return the created resource.

Remember that for this type of operation, we have to create a method or function called create:

public function create()    {        
    $movie = new MovieModel();        
    $category = new CategoryModel();        
    if ($this->validate('movies')) {            
        if (!$this->request->getPost('category_id'))               
         return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);            
         if (!$category->get($this->request->getPost('category_id'))) {               
             return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);            }            
             $id = $movie->insert([                'title' => $this->request->getPost('title'),                'description' => $this->request->getPost('description'),                'category_id' => $this->request->getPost('category_id'),            ]);            return $this->genericResponse($this->model->find($id), NULL, 200);        }       
 $validation = \Config\Services::validation();        
 return $this->genericResponse(NULL, $validation->getErrors(), 500);   
 }

Here, as you can see, we are using a category since a movie belongs to a category, therefore we validate that it exists, and if it does not exist, we simply return an error response.

We also apply some validations through the validate function which is using the validations that we created in the corresponding configuration file for the validations and they look like this:

    public $movies =[        'title' => 'required|min_length[3]|max_length[255]',        'description' => 'min_length[3]|max_length[5000]'    ];

PUT or PATCH operations

For this type of request, which is to update a resource, in this case a movie, it can be the same scheme that we saw before to create a resource, since we need to validate the data that we are going to receive but this time we are also going to receive an identifier for the url that will allow us to update this resource, otherwise it is similar to a request of the post type:

public function update($id = NULL)
{
    $movie = new MovieModel();
    $category = new CategoryModel();
    $data = $this->request->getRawInput();
    if ($this->validate('movies')) {
        if (!$data['category_id'])
            return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
        if (!$movie->get($id)) {
            return $this->genericResponse(NULL, array("movie_id" => "Película no existe"), 500);
        }
        if (!$category->get($data['category_id'])) {
            return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
        }
        $movie->update($id, ['title' => $data['title'],                'description' => $data['description'],                'category_id' => $data['category_id'],]);
        return $this->genericResponse($this->model->find($id), NULL, 200);
    }
}

Delete operations

In this case, we are interested in developing a simple function that simply receives an identifier to be able to delete a record, in order to use it, we need to send a request of type delete from our http.

This has been one of the simplest operations, if not the simplest that we can perform:

public function delete($id = NULL)
{
    $movie = new MovieModel();
    $movie->delete($id);
    return $this->genericResponse("Producto eliminado", NULL, 200);
}

Final code of the Rest

In the end, our code, our Restful class looks like this:

<?php

namespace App\Controllers;

use App\Models\CategoryModel;
use App\Models\MovieModel;
use CodeIgniter\RESTful\ResourceController;

class RestMovie extends ResourceController
{
    protected $modelName = 'App\Models\MovieModel';
    protected $format = 'json';
    public function index()
    {
        return $this->genericResponse($this->model->findAll(), NULL, 200);
    }
    public function show($id = NULL)
    {
        return $this->genericResponse($this->model->find($id), NULL, 200);
    }
    public function delete($id = NULL)
    {
        $movie = new MovieModel();
        $movie->delete($id);
        return $this->genericResponse("Producto eliminado", NULL, 200);
    }
    public function create()
    {
        $movie = new MovieModel();
        $category = new CategoryModel();
        if ($this->validate('movies')) {
            if (!$this->request->getPost('category_id'))                return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
            if (!$category->get($this->request->getPost('category_id'))) {
                return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
            }
            $id = $movie->insert(['title' => $this->request->getPost('title'),                'description' => $this->request->getPost('description'),                'category_id' => $this->request->getPost('category_id'),]);
            return $this->genericResponse($this->model->find($id), NULL, 200);
        }
        $validation = \Config\Services::validation();
        return $this->genericResponse(NULL, $validation->getErrors(), 500);
    }
    public function update($id = NULL)
    {
        $movie = new MovieModel();
        $category = new CategoryModel();
        $data = $this->request->getRawInput();
        if ($this->validate('movies')) {
            if (!$data['category_id'])                return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
            if (!$movie->get($id)) {
                return $this->genericResponse(NULL, array("movie_id" => "Película no existe"), 500);
            }
            if (!$category->get($data['category_id'])) {
                return $this->genericResponse(NULL, array("category_id" => "Categoría no existe"), 500);
            }
            $movie->update($id, ['title' => $data['title'],                'description' => $data['description'],                'category_id' => $data['category_id'],]);
            return $this->genericResponse($this->model->find($id), NULL, 200);
        }
        $validation = \Config\Services::validation();
        return $this->genericResponse(NULL, $validation->getErrors(), 500);
    }
    private function genericResponse($data, $msj, $code)
    {
        if ($code == 200) {
            return $this->respond(array("data" => $data,                "code" => $code)); //, 404, "No hay nada"        
        } else {
            return $this->respond(array("msj" => $msj,                "code" => $code));
        }
    }
}
Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.