CRUD completo
Nesta aula, você aprenderá a construir um sistema CRUD (Create, Read, Update, Delete) completo em PHP, cobrindo desde a estrutura básica até validações e um exemplo prático. Serão abordados conceitos fundamentais como operações com banco de dados MySQL, tratamento de formulários e boas práticas de segurança.
Nesta aula, vamos construir um sistema CRUD completo em PHP. CRUD é um acrônimo para as quatro operações básicas de armazenamento persistente: Create (criar), Read (ler), Update (atualizar) e Delete (excluir). Essas operações são a espinha dorsal de muitas aplicações web, desde blogs até sistemas de gerenciamento de conteúdo. Utilizaremos PHP com MySQL e a extensão PDO (PHP Data Objects) para interagir com o banco de dados de forma segura e eficiente.
O objetivo é que, ao final desta aula, você seja capaz de implementar um CRUD funcional, entendendo cada etapa do processo, desde a criação da tabela até a validação dos dados inseridos. Também discutiremos boas práticas como prepared statements para evitar SQL injection e validação de dados no servidor.
Create, Read, Update, Delete
As quatro operações do CRUD correspondem às instruções SQL básicas: INSERT, SELECT, UPDATE e DELETE. Cada uma delas é responsável por uma ação específica no banco de dados. No contexto de uma aplicação web, essas operações são geralmente associadas a formulários e páginas que permitem ao usuário gerenciar dados.
Create (Criar): Insere novos registros no banco de dados. Em PHP, isso é feito através de um formulário que envia dados via POST para um script que executa um INSERT. Exemplo de SQL: INSERT INTO usuarios (nome, email) VALUES (:nome, :email).
Read (Ler): Recupera e exibe registros do banco de dados. Pode ser uma listagem (SELECT com ou sem filtros) ou a exibição de um único registro. Exemplo: SELECT * FROM usuarios WHERE id = :id.
Update (Atualizar): Modifica registros existentes. Geralmente, um formulário pré-preenchido com os dados atuais permite ao usuário alterá-los e enviar um UPDATE. Exemplo: UPDATE usuarios SET nome = :nome, email = :email WHERE id = :id.
Delete (Excluir): Remove registros do banco de dados. Normalmente, é acionado por um link ou botão que envia uma requisição (muitas vezes GET ou POST) para um script que executa DELETE. Exemplo: DELETE FROM usuarios WHERE id = :id.
Estrutura
Um sistema CRUD bem estruturado geralmente segue o padrão MVC (Model-View-Controller) ou, pelo menos, separa a lógica de apresentação da lógica de negócios. Para este exemplo, utilizaremos uma estrutura simples com arquivos separados para cada operação, mas todos compartilhando uma conexão com o banco de dados via PDO.
Vamos organizar os arquivos da seguinte forma:
/crud
index.php (listagem - Read)
create.php (formulário de criação - Create)
store.php (processa o formulário de criação)
edit.php (formulário de edição - Update)
update.php (processa o formulário de edição)
delete.php (processa exclusão - Delete)
config.php (configuração do banco de dados)
style.css (estilos básicos, opcional)
O arquivo config.php conterá a conexão com o banco de dados usando PDO. Exemplo:
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Erro na conexão: " . $e->getMessage());
}
?>
Para este exemplo, criaremos uma tabela chamada usuarios com os campos: id (auto incremento), nome, email e data_criacao (timestamp). SQL de criação:
CREATE TABLE usuarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
data_criacao TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Validação
A validação de dados é crucial para garantir a integridade e segurança da aplicação. Devemos validar tanto no cliente (JavaScript) quanto no servidor (PHP). No servidor, a validação é indispensável, pois o cliente pode ser burlado. Aqui estão algumas validações comuns para um CRUD de usuários:
- Campos obrigatórios: Verificar se os campos nome e email não estão vazios.
- Formato do email: Usar
filter_var($email, FILTER_VALIDATE_EMAIL)para validar o email. - Comprimento: Limitar o tamanho dos campos (ex: nome máximo 100 caracteres).
- Unicidade: Verificar se o email já existe no banco (especialmente no Create e Update).
- Proteção contra SQL injection: Usar prepared statements com PDO.
Exemplo de validação no PHP:
$erros = [];
if (empty($_POST['nome'])) {
$erros[] = 'O campo nome é obrigatório.';
} elseif (strlen($_POST['nome']) > 100) {
$erros[] = 'O nome deve ter no máximo 100 caracteres.';
}
if (empty($_POST['email'])) {
$erros[] = 'O campo email é obrigatório.';
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
$erros[] = 'Formato de email inválido.';
} else {
// Verificar unicidade (excluindo o próprio ID em caso de update)
$sql = "SELECT COUNT(*) FROM usuarios WHERE email = :email";
if ($id) { // se for update, ignorar o próprio registro
$sql .= " AND id != :id";
}
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':email', $_POST['email']);
if ($id) $stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->fetchColumn() > 0) {
$erros[] = 'Este email já está cadastrado.';
}
}
if (!empty($erros)) {
// Exibir erros e interromper
foreach ($erros as $erro) {
echo "$erro
";
}
exit;
}
Exemplo prático
Agora, vamos construir um CRUD completo passo a passo. Assumiremos que você já criou a tabela usuarios e o arquivo config.php com a conexão PDO.
1. Listagem (Read) - index.php
Este arquivo exibe todos os usuários em uma tabela, com links para editar e excluir.
query("SELECT * FROM usuarios ORDER BY data_criacao DESC");
$usuarios = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
CRUD - Listagem
Usuários
Novo Usuário
ID
Nome
Email
Data de Criação
Ações
= $usuario['id'] ?>
= htmlspecialchars($usuario['nome']) ?>
= htmlspecialchars($usuario['email']) ?>
= $usuario['data_criacao'] ?>
Editar
Excluir
2. Formulário de Criação (Create) - create.php
Exibe um formulário para inserir um novo usuário.
Novo Usuário
Novo Usuário
3. Processamento de Criação (Create) - store.php
Recebe os dados do formulário, valida e insere no banco.
100) {
$erros[] = 'Nome deve ter no máximo 100 caracteres.';
}
if (empty($email)) {
$erros[] = 'Email é obrigatório.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$erros[] = 'Email inválido.';
} else {
// Verificar unicidade
$stmt = $pdo->prepare("SELECT COUNT(*) FROM usuarios WHERE email = :email");
$stmt->bindValue(':email', $email);
$stmt->execute();
if ($stmt->fetchColumn() > 0) {
$erros[] = 'Email já cadastrado.';
}
}
if (!empty($erros)) {
foreach ($erros as $erro) {
echo "$erro
";
}
echo 'Voltar';
exit;
}
// Inserir
$sql = "INSERT INTO usuarios (nome, email) VALUES (:nome, :email)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':nome', $nome);
$stmt->bindValue(':email', $email);
$stmt->execute();
header('Location: index.php');
exit;
4. Formulário de Edição (Update) - edit.php
Exibe formulário pré-preenchido com os dados do usuário a ser editado.
prepare("SELECT * FROM usuarios WHERE id = :id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$usuario = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$usuario) {
die('Usuário não encontrado.');
}
?>
Editar Usuário
Editar Usuário
5. Processamento de Atualização (Update) - update.php
Recebe os dados do formulário de edição, valida e atualiza no banco.
100) {
$erros[] = 'Nome deve ter no máximo 100 caracteres.';
}
if (empty($email)) {
$erros[] = 'Email é obrigatório.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$erros[] = 'Email inválido.';
} else {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM usuarios WHERE email = :email AND id != :id");
$stmt->bindValue(':email', $email);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->fetchColumn() > 0) {
$erros[] = 'Email já cadastrado por outro usuário.';
}
}
if (!empty($erros)) {
foreach ($erros as $erro) {
echo "$erro
";
}
echo 'Voltar';
exit;
}
// Atualizar
$sql = "UPDATE usuarios SET nome = :nome, email = :email WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':nome', $nome);
$stmt->bindValue(':email', $email);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
header('Location: index.php');
exit;
6. Exclusão (Delete) - delete.php
Recebe o ID via GET e exclui o registro.
prepare("SELECT id FROM usuarios WHERE id = :id");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
if (!$stmt->fetch()) {
die('Usuário não encontrado.');
}
// Excluir
$sql = "DELETE FROM usuarios WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
header('Location: index.php');
exit;
Boas práticas e observações finais
Algumas recomendações importantes para sistemas CRUD em PHP:
- Use prepared statements sempre que possível para evitar SQL injection.
- Valide e sanitize todos os dados recebidos do usuário, tanto no cliente quanto no servidor.
- Utilize htmlspecialchars ao exibir dados vindos do banco para prevenir XSS.
- Implemente confirmação antes de excluir registros (via JavaScript ou formulário POST).
- Considere usar CSRF tokens em formulários para proteção contra ataques cross-site request forgery.
- Organize o código em arquivos separados e, se possível, utilize um padrão como MVC.
Referências
- PHP Manual: PDO
- PHP Manual: Prepared Statements
- PHP Manual: filter_var
- PHP Manual: htmlspecialchars
- MDN: SQL Injection
- W3Schools: PHP MySQL Introduction
Exercícios
-
Exercício 1: Crie uma nova tabela chamada
produtoscom os campos id, nome, preco e quantidade. Implemente o CRUD completo para esta tabela, seguindo a estrutura apresentada.✓ Resposta: A resposta consiste em criar os arquivos análogos aos do exemplo, alterando os nomes das colunas e da tabela. Por exemplo, no create.php, os campos seriam nome, preco e quantidade; no store.php, a validação incluiria verificar se preco é numérico, etc. O código SQL para criar a tabela:CREATE TABLE produtos (id INT AUTO_INCREMENT PRIMARY KEY, nome VARCHAR(100) NOT NULL, preco DECIMAL(10,2) NOT NULL, quantidade INT NOT NULL); -
Exercício 2: Adicione uma funcionalidade de busca na listagem do CRUD de usuários. Crie um campo de texto e um botão que filtra os usuários pelo nome (LIKE).
✓ Resposta: No index.php, adicione um formulário GET com input text name="busca". Modifique a consulta:
$busca = $_GET['busca'] ?? ''; $stmt = $pdo->prepare("SELECT * FROM usuarios WHERE nome LIKE :busca ORDER BY data_criacao DESC"); $stmt->bindValue(':busca', "%$busca%"); $stmt->execute();. Exiba o valor no input para manter a busca. -
Exercício 3: Implemente proteção CSRF nos formulários de criação e edição. Gere um token único por sessão e valide-o no processamento.
✓ Resposta: No início de cada página, inicie a sessão:
session_start();. Gere o token:$_SESSION['csrf_token'] = bin2hex(random_bytes(32));. No formulário, adicione um campo hidden:. No processamento, valide:if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { die('CSRF token inválido.'); }. -
Exercício 4: Modifique o delete para usar o método POST em vez de GET, evitando exclusões acidentais via link.
✓ Resposta: Na listagem, substitua o link por um formulário POST:
. No delete.php, leia o ID de $_POST. Mantenha a validação. -
Exercício 5: Crie uma página de detalhes do usuário (show.php) que exibe todas as informações de um único registro, com um link para voltar à listagem.
✓ Resposta: Crie show.php:
prepare("SELECT * FROM usuarios WHERE id = :id"); $stmt->bindValue(':id', $id, PDO::PARAM_INT); $stmt->execute(); $usuario = $stmt->fetch(PDO::FETCH_ASSOC); if (!$usuario) die('Usuário não encontrado.'); ?>e exiba os dados. Na listagem, adicione um link para show.php?id=... em cada linha.