¿Cómo crear una API REST (RestFul) en CodeIgniter 4?

- Andrés Cruz

In english
¿Cómo crear una API REST (RestFul) en CodeIgniter 4?

Recuerda ver el vídeo completo para crear una Api Rest en CodeIgniter en donde exploramos muchos trucos más que seguramente te serviran de mucho :)

 

Las Rest APIs son un conjunto de métodos que pueden ser consumidos vía peticiones GET, POST, PUT, PATCH, DELETE, para poder comunicar nuestra aplicación web, en este caso CodeIgniter 4 con otros tipos de aplicaciones, Flutter, Vue y un largo etc.

RECUERDA QUE una Api Rest no es un protocolo, no es un estándar por lo tanto no es necesario que siga un patrón en particular u orden en especifico, tu eres libre de definir los métodos como mejor te parezca para realizar las acciones que tu consideres.

Una de las características que trae consigo las nueva versión de CodeIgniter 4 es que ahora podemos crear una Api Rest sin necesidad de instalar dependencias de terceros; para esto, tenemos que  crear una clase de tipo ResourceController en la cual tenemos que especificar a fuerza el modelo y el formato por defecto:

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

Así que, vamos a ver cómo podemos crear una Rest Api o ApiRest de tipo CRUD empleando nuestro CodeIgniter 4 (ResfFul).

Los modelos

Como puedes ver, cuando definimos un controlador de este tipo, de tipo recurso o ResourceController, estamos atando un modelo a dicha clase; en este caso estamos empleando el siguiente modelo que es el que empleamos en mi curso de CodeIgniter 4 en Udemy:

<?php 
namespace App\Models; 
use CodeIgniter\Model; 
class MovieModel extends Model {   
     protected $table = 'movies';    
     protected $primaryKey = 'id';   
     protected $allowedFields = ['title', 'description','category_id']; 
}

Y este lo emplea el modelo anterior: 

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

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

Creando la clase de tipo recurso para la Rest Api

Vamos a crear una clase controlador de tipo recurso para nuestra RestApi que implementa las operaciones de tipo CRUD:

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

Como puedes ver, tenemos las operaciones comunes para administrar alguna entidad que en este caso sería una Movie, pero puede ser cualquier cosa, un Post, un artículo...

Creando la Api Restful en CodeIgniter 4

Antes de comenzar a mostrar las operaciones, recuerda que este es un controlador de tipo recurso, por lo tanto, todos los métodos que vamos a emplear para el CRUD tienen nombres definidos a lo que se refiere sus funciones; por ejemplo, la función que se encarga de mostrar un listado se llama index o la de mostrar el detalle de la entidad show y así para el resto.

Personalizar la respuesta de la Rest Api

Voy a crear una función de ayuda en esta misma clase que me permitirá devolver las respuesta en un formato personalizado para facilitar su consumo; elementos como:

  1. Data
  2. Código de estado de la respuesta
  3. Mensaje

Son componentes que siempre me gusta devolver a la respuesta de la Rest y esto es para dar más información y flexibilidad al momento de trabajar con este tipo de estructuras:

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));
    }
}

Creando los métodos de la Restful

Operaciones tipo Get

Para las operaciones Get, que son las más sencillas y podemos emplear probar fácilmente desde nuestro navegador, nos permiten obtener un registro en particular, dado el identificador y todos los registros, para esto no hace falta pasar ningún identificador:

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);    
}

Operaciones tipo Post

Con este tipo de operaciones le interesa es crear un nuevo recurso, en este caso una movie, por lo tanto, tenemos que hacer algunas verificaciones cómo, procesar o validar los datos que vamos a recibir, y su posterior guardado en la base de datos, luego, como respuesta simplemente podemos devolver el recurso creado.

Recuerda que para este tipo de operación, tenemos que crear un método o función llamado 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);   
 }

Aquí como puedes ver, estamos empleando una categoría ya que una movie pertenece a una categoría por lo tanto validamos que la misma exista, y si no existe simplemente devolvemos una respuesta de error.

También aplicamos algunas validaciones mediante la función de validate que esta empleando las validaciones que creamos en en archivo de configuración correspondiente para las validaciones y lucen así:

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

Operaciones tipo PUT o PATCH

Para este tipo de petición que es de actualizar un recurso, en este caso una movie, puede ser el mismo esquema que vimos anteriormente para crear un recurso, ya que necesitamos validar los datos que vamos a recibir pero esta vez también vamos a recibir un identificador por la url que nos permitirá actualizar este recurso, por lo demás es similar al de una petición de tipo post:

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);
    }
}

Operaciones tipo Delete

En este caso, nos interesa desarrollar una sencilla función que simplemente recibe un identificador para poder eliminar un registro, para poder emplearlo, necesitamos enviar una petición de tipo delete de nuestro http.

Esta viene siendo una de las operaciones más sencillas por no decir la más sencilla que podemos realizar:

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

Código final de la Rest

A la final, nuestro código, nuestra clase Restful luce de la siguiente manera:

<?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.