CRUD básico inicial Codeigniter 4 - react
Como fazer um CRUD básico inicial Codeigniter 4 - react - aplicação web com operações CRUD (Create, Read, Update, Delete) integrando o backend em CodeIgniter 4 (um framework PHP) com o frontend em React (uma biblioteca JavaScript).
Resumo dos passos principais:
- Configuração do Backend (CodeIgniter 4):
- Instale o #CodeIgniter 4 via Composer.
- Configure o ambiente, incluindo #banco de dados (ex.: MySQL).
- Crie um modelo e #controlador para gerenciar uma entidade (ex.: "Usuários").
- Desenvolva rotas e métodos para as operações CRUD:
- Create: Inserir dados via POST.
- Read: Listar ou buscar dados via GET.
- Update: Atualizar dados via PUT.
- Delete: Excluir dados via DELETE.
- Implemente uma API RESTful para comunicação com o frontend, retornando dados em JSON.
- Configuração do Frontend (React):
- Crie um projeto React usando create-react-app ou outra ferramenta.
- Instale bibliotecas como axios para requisições HTTP.
- Crie componentes para:
- Listar registros (ex.: tabela de usuários).
- Formulários para criar e editar registros.
- Botões para deletar registros.
- Configure chamadas à API do CodeIgniter para cada operação CRUD.
- Integração Frontend e Backend:
- Conecte o React ao CodeIgniter via requisições HTTP (usando URLs da API).
- Gerencie estados no React (ex.: com useState e useEffect) para exibir e atualizar dados dinamicamente.
- Trate erros e valide dados no frontend e backend.
- Testes e Finalização:
- Teste as operações CRUD (inserir, listar, editar e excluir) no frontend.
- Verifique a comunicação com o backend e a persistência no banco de dados.
- Ajuste detalhes como interface e validações.
O tutorial foca em criar uma aplicação funcional e simples, ideal para iniciantes que querem aprender a integrar um backend PHP com um frontend React,
Crie seu backend com o comando: composer create-project codeigniter4/appstarter backend

Comandos spark úteis:
php spark make:migration Products
php spark migrate
php spark make:model ProductModel
php spark make:migration Products
// CodeIgniter v4.6.3 Command Line Tool - Server Time: 2025-08-09 22:28:36 UTC+00:00
//File created: APPPATH/Database/Migrations/2025-08-09-222836_Products.php
php spark routes
/*
CodeIgniter v4.6.3 Command Line Tool - Server Time: 2025-08-10 01:02:30 UTC+00:00
+--------+--------------------+------+--------------------------------------+----------------+---------------+
| Method | Route | Name | Handler | Before Filters | After Filters |
+--------+--------------------+------+--------------------------------------+----------------+---------------+
| GET | / | » | \App\Controllers\Home::index | cors | |
| GET | products | » | \App\Controllers\Products::index | cors | cors |
| GET | products/new | » | \App\Controllers\Products::new | cors | cors |
| GET | products/(.*)/edit | » | \App\Controllers\Products::edit/$1 | cors | cors |
| GET | products/(.*) | » | \App\Controllers\Products::show/$1 | cors | cors |
| POST | products | » | \App\Controllers\Products::create | cors | cors |
| PATCH | products/(.*) | » | \App\Controllers\Products::update/$1 | cors | cors |
| PUT | products/(.*) | » | \App\Controllers\Products::update/$1 | cors | cors |
| DELETE | products/(.*) | » | \App\Controllers\Products::delete/$1 | cors | cors |
+--------+--------------------+------+--------------------------------------+----------------+---------------+
*/
php spark make:migration CreateCategoriesTable
php spark make:controller Categories --restful
Vamos trabalhar com frontend e backend sendo react e codeigniter 4.
Controller Products (Migration/Controller)
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Products extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 255,
'unique' => true,
],
'price' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
]
]);
$this->forge->addKey('id', true);
$this->forge->createTable('products', true);
}
public function down()
{
$this->forge->dropTable('products');
}
}
//-------------------------------------------------------------------------------------------------
<?php
namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\API\ResponseTrait;
use App\Models\ProductModel;
class Products extends ResourceController
{
use ResponseTrait;
public function index()
{
$model = new ProductModel();
// Buscar produtos com paginação
$products = $model->orderBy('id', 'DESC')
->paginate();
// Preparar resposta com dados e informações de paginação
$data = [
'products' => $products,
'pager' =>[
'currentPage' => $model->pager->getCurrentPage(),
'total_pages'=>$model->pager->getPageCount(),
'perPage' => $model->pager->getPerPage(),
]
];
return $this->respond($data);
}
public function show($id = null)
{
$model = new ProductModel();
$data = $model->find(['id' => $id]);
if (!$data) return $this->failNotFound('No Data Found');
return $this->respond($data[0]);
}
public function create() {
helper(['form']);
$rules = [
'name' => 'required',
'price' => 'required',
'category' => 'required',
'description' => 'required'
];
$data = [
'name' => $this->request->getVar('name'),
'price' => $this->request->getVar('price'),
'category' => $this->request->getVar('category'),
'description' => $this->request->getVar('description')
];
if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new ProductModel();
$model->save($data);
$response = [
'status' => 201,
'error' => null,
'messages' => [
'success' => 'Data Inserted'
]
];
return $this->respondCreated($response);
}
public function update($id=null) {
helper(['form']);
$rules = [
'name' => 'required',
'price' => 'required',
'category' => 'required' ];
$data = [
'name' => $this->request->getVar('name'),
'price' => $this->request->getVar('price'),
'category' => $this->request->getVar('category'),
];
if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new ProductModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->update($id, $data);
$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data updated'
]
];
return $this->respond($response);
}
public function delete($id=null) {
$model = new ProductModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->delete($id);
$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data deleted'
]
];
return $this->respond($response);
}
}
Categories (Migration/Controller)
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCategoriesTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'name' => [
'type' => 'VARCHAR',
'constraint' => '100',
'null' => false,
],
'description' => [
'type' => 'TEXT',
'null' => true,
'default' => '',
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('categories', true);
}
public function down()
{
$this->forge->dropTable('categories');
}
}
// Controller ------------------------------------------------------------------------------------
<?php
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use App\Models\CategoryModel;
class Categories extends ResourceController
{
/**
* Return an array of resource objects, themselves in array format.
*
* @return ResponseInterface
*/
public function index()
{
$model = new CategoryModel();
$data = $model->findAll();
return $this->respond([
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data Found'
],
'categories' => $data
]);
}
/**
* Return the properties of a resource object.
*
* @param int|string|null $id
*
* @return ResponseInterface
*/
public function show($id = null)
{
$model = new CategoryModel();
$data = $model->find(['id' => $id]);
if (!$data) return $this->failNotFound('No Data Found');
return $this->respond([
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data Found'
],
'category' => $data[0]
]);
}
/**
* Return a new resource object, with default properties.
*
* @return ResponseInterface
*/
public function new()
{
helper(['form']);
$rules = [
'name' => 'required',
'description' => 'required'
];
$data = [
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description')
];
if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new CategoryModel();
$model->save($data);
$response = [
'status' => 201,
'error' => null,
'messages' => [
'success' => 'Data Inserted'
]
];
return $this->respondCreated($response);
}
/**
* Create a new resource object, from "posted" parameters.
*
* @return ResponseInterface
*/
public function create()
{
return $this->new();
}
/**
* Return the editable properties of a resource object.
*
* @param int|string|null $id
*
* @return ResponseInterface
*/
public function edit($id = null)
{
$model = new CategoryModel();
$data = $model->find(['id' => $id]);
if (!$data) return $this->failNotFound('No Data Found');
return $this->respond([
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data Found'
],
'category' => $data[0]
]);
}
/**
* Add or update a model resource, from "posted" properties.
*
* @param int|string|null $id
*
* @return ResponseInterface
*/
public function update($id = null)
{
helper(['form']);
$rules = [
'name' => 'required',
'description' => 'required'
];
$data = [
'name' => $this->request->getVar('name'),
'description' => $this->request->getVar('description')
];
if(!$this->validate($rules)) return $this->fail($this->validator->getErrors());
$model = new CategoryModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->update($id, $data);
$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data updated'
]
];
return $this->respond($response);
}
/**
* Delete the designated resource object from the model.
*
* @param int|string|null $id
*
* @return ResponseInterface
*/
public function delete($id = null)
{
$model = new CategoryModel();
$find = $model->find(['id' => $id]);
if(!$find) return $this->failNotFound('No Data Found');
$model->delete($id);
$response = [
'status' => 200,
'error' => null,
'messages' => [
'success' => 'Data deleted'
]
];
return $this->respond($response);
}
}
Routes.
<?php
use CodeIgniter\Router\RouteCollection;
/**
* @var RouteCollection $routes
*/
$routes->get('/', 'Home::index');
$routes->resource('products', ['filter' => 'cors']);
$routes->resource('categories', ['filter' => 'cors']);
Frontend - Git -> https://github.com/GilbertoMG/codeigniter4-react
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { apiService } from '../../services/api';
import CategoriesList from '../Categories/CategoriesSelect';
const ProductAdd = () => {
const navigate = useNavigate();
const [formData, setFormData] = useState({
name: '',
price: '',
category: '',
description: ''
});
const [loading, setLoading] = useState(false);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
await apiService.post('products', formData);
alert('Produto criado com sucesso!');
navigate('/products');
} catch (error) {
alert('Erro ao criar produto');
console.error('Erro:', error);
} finally {
setLoading(false);
}
};
return (
<div className="product-add">
<div className="page-header">
<h1>Adicionar Produto</h1>
<button
onClick={() => navigate('/products')}
className="btn btn-secondary"
>
Voltar
</button>
</div>
<form onSubmit={handleSubmit} className="form">
<div className="form-group">
<label>Nome:</label>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
required
className="form-control"
/>
</div>
<div className="form-group">
<label>Preço:</label>
<input
type="number"
step="0.01"
name="price"
value={formData.price}
onChange={handleChange}
required
className="form-control"
/>
</div>
<div className="mb-3">
<label>Categoria:</label>
<CategoriesList
onSelectCategory={(categoryId) => handleChange({ target: { name: 'category', value: categoryId } })}
/>
</div>
<div className="form-group">
<label>Descrição:</label>
<textarea
name="description"
value={formData.description}
onChange={handleChange}
className="form-control"
rows="4"
/>
</div>
<button
type="submit"
disabled={loading}
className="btn btn-primary"
>
{loading ? 'Salvando...' : 'Salvar Produto'}
</button>
</form>
</div>
);
};
export default ProductAdd;

Compartilhe este artigo: