Se você mexe com PHP e banco de dados, mais cedo ou mais tarde vai precisar de um CRUD:
- Create → Cadastrar
- Read → Listar / Ver
- Update → Editar
- Delete → Excluir
Neste guia, vamos fazer um CRUD completo de usuários usando:
- PHP 8+
- PDO
- Prepared statements (seguro contra SQL Injection)
- Código simples, em um arquivo só, fácil de entender.
Objetivo: no final você terá um arquivo
usuarios.phpque lista, cadastra, edita e exclui usuários.
1. Estrutura da tabela no banco de dados
Vamos usar um exemplo simples de tabela usuarios.
SQL para criar a tabela (MySQL):
CREATE TABLE usuarios (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(150) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
criado_em DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Colunas:
id: identificador único do usuárionome: nome completoemail: e-mail (único)criado_em: data/hora do cadastro
2. Arquivo de conexão (reaproveitando padrão moderno)
Aqui vou usar a mesma ideia do post anterior: PDO + Conexao centralizada.
Se você já tiver a classe Conexao, ótimo, é só incluir.
Se não tiver, pode usar esse exemplo rápido em Conexao.php:
<?php
// Conexao.php
declare(strict_types=1);
use PDO;
use PDOException;
const DB_HOST = 'localhost';
const DB_NOME = 'meu_banco';
const DB_USUARIO = 'root';
const DB_SENHA = '';
const DB_CHARSET = 'utf8mb4';
function obterConexao(): PDO
{
static $pdo;
if ($pdo instanceof PDO) {
return $pdo;
}
$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NOME . ';charset=' . DB_CHARSET;
$opcoes = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, DB_USUARIO, DB_SENHA, $opcoes);
return $pdo;
} catch (PDOException $e) {
die('Erro ao conectar ao banco de dados.');
}
}
Se você já estiver usando
.enve uma classe mais elaborada, pode adaptar.
Aqui a ideia é deixar simples para quem está começando.
3. Estrutura do arquivo usuarios.php
Vamos ter um único arquivo controlado por um parâmetro acao:
acao=listar→ ver todos os usuários (padrão)acao=criar→ exibir formulário e salvar novo usuárioacao=editar&id=...→ carregar dados e salvar ediçãoacao=excluir&id=...→ excluir usuário e redirecionar
Arquivo: usuarios.php
<?php
declare(strict_types=1);
require __DIR__ . '/Conexao.php';
$acao = $_GET['acao'] ?? 'listar';
switch ($acao) {
case 'criar':
processarCriacao();
break;
case 'editar':
processarEdicao();
break;
case 'excluir':
processarExclusao();
break;
default:
listarUsuarios();
break;
}
/**
* Lista todos os usuários.
*/
function listarUsuarios(): void
{
$pdo = obterConexao();
$sql = "SELECT id, nome, email, criado_em FROM usuarios ORDER BY id DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$usuarios = $stmt->fetchAll();
?>
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>CRUD de Usuários</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background: #f0f0f0; }
a.botao { display: inline-block; padding: 6px 10px; background: #2563eb; color: #fff; text-decoration: none; border-radius: 4px; font-size: 14px; }
a.botao-vermelho { background: #b91c1c; }
.topo { display: flex; justify-content: space-between; align-items: center; }
</style>
</head>
<body>
<div class="topo">
<h1>Usuários</h1>
<a href="?acao=criar" class="botao">+ Novo usuário</a>
</div>
<?php if (empty($usuarios)): ?>
<p>Nenhum usuário cadastrado.</p>
<?php else: ?>
<table>
<thead>
<tr>
<th>ID</th>
<th>Nome</th>
<th>E-mail</th>
<th>Cadastrado em</th>
<th>Ações</th>
</tr>
</thead>
<tbody>
<?php foreach ($usuarios as $usuario): ?>
<tr>
<td><?= (int)$usuario['id'] ?></td>
<td><?= htmlspecialchars($usuario['nome']) ?></td>
<td><?= htmlspecialchars($usuario['email']) ?></td>
<td><?= htmlspecialchars($usuario['criado_em']) ?></td>
<td>
<a href="?acao=editar&id=<?= (int)$usuario['id'] ?>" class="botao">Editar</a>
<a href="?acao=excluir&id=<?= (int)$usuario['id'] ?>"
class="botao botao-vermelho"
onclick="return confirm('Tem certeza que deseja excluir este usuário?');">
Excluir
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</body>
</html>
<?php
}
/**
* Processa criação de usuário (exibe formulário e salva).
*/
function processarCriacao(): void
{
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nome = trim($_POST['nome'] ?? '');
$email = trim($_POST['email'] ?? '');
// Validação simples
$erros = [];
if ($nome === '') {
$erros[] = 'O nome é obrigatório.';
}
if ($email === '') {
$erros[] = 'O e-mail é obrigatório.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$erros[] = 'E-mail inválido.';
}
if (empty($erros)) {
try {
$pdo = obterConexao();
$sql = "INSERT INTO usuarios (nome, email) VALUES (:nome, :email)";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':nome', $nome);
$stmt->bindValue(':email', $email);
$stmt->execute();
// Redireciona de volta para a listagem
header('Location: usuarios.php');
exit;
} catch (PDOException $e) {
$erros[] = 'Erro ao salvar no banco de dados (e-mail duplicado ou outro problema).';
}
}
}
// Exibe o formulário (se GET ou se houve erro)
exibirFormularioUsuario('criar', $erros ?? [], [
'nome' => $nome ?? '',
'email' => $email ?? '',
]);
}
/**
* Processa edição de usuário (carrega, exibe formulário e salva).
*/
function processarEdicao(): void
{
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($id <= 0) {
header('Location: usuarios.php');
exit;
}
$pdo = obterConexao();
// Busca dados atuais do usuário
$sql = "SELECT id, nome, email FROM usuarios WHERE id = :id LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$usuario = $stmt->fetch();
if (!$usuario) {
header('Location: usuarios.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$nome = trim($_POST['nome'] ?? '');
$email = trim($_POST['email'] ?? '');
$erros = [];
if ($nome === '') {
$erros[] = 'O nome é obrigatório.';
}
if ($email === '') {
$erros[] = 'O e-mail é obrigatório.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$erros[] = 'E-mail inválido.';
}
if (empty($erros)) {
try {
$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: usuarios.php');
exit;
} catch (PDOException $e) {
$erros[] = 'Erro ao atualizar o usuário. Talvez o e-mail já esteja em uso.';
}
}
$usuario['nome'] = $nome;
$usuario['email'] = $email;
}
exibirFormularioUsuario('editar', $erros ?? [], $usuario);
}
/**
* Exclui um usuário e redireciona.
*/
function processarExclusao(): void
{
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
header('Location: usuarios.php');
exit;
}
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
if ($id <= 0) {
header('Location: usuarios.php');
exit;
}
$pdo = obterConexao();
$sql = "DELETE FROM usuarios WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
header('Location: usuarios.php');
exit;
}
/**
* Exibe o formulário de criação/edição.
*
* @param string $modo 'criar' ou 'editar'
* @param array $erros
* @param array $dados
*/
function exibirFormularioUsuario(string $modo, array $erros, array $dados): void
{
$tituloPagina = $modo === 'criar' ? 'Novo usuário' : 'Editar usuário';
$acaoForm = $modo === 'criar' ? 'criar' : 'editar&id=' . (int)$dados['id'];
?>
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title><?= htmlspecialchars($tituloPagina) ?></title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
form { max-width: 400px; }
label { display: block; margin-top: 10px; }
input[type="text"], input[type="email"] {
width: 100%; padding: 8px; margin-top: 4px; box-sizing: border-box;
}
button { margin-top: 15px; padding: 8px 12px; }
.erros { background: #fee2e2; color: #b91c1c; padding: 10px; border-radius: 4px; margin-bottom: 10px; }
a { color: #2563eb; text-decoration: none; }
</style>
</head>
<body>
<h1><?= htmlspecialchars($tituloPagina) ?></h1>
<p><a href="usuarios.php">← Voltar para a lista</a></p>
<?php if (!empty($erros)): ?>
<div class="erros">
<ul>
<?php foreach ($erros as $erro): ?>
<li><?= htmlspecialchars($erro) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="post" action="usuarios.php?acao=<?= htmlspecialchars($acaoForm) ?>">
<label for="nome">Nome</label>
<input type="text" name="nome" id="nome"
value="<?= htmlspecialchars($dados['nome'] ?? '') ?>" required>
<label for="email">E-mail</label>
<input type="email" name="email" id="email"
value="<?= htmlspecialchars($dados['email'] ?? '') ?>" required>
<button type="submit">Salvar</button>
</form>
</body>
</html>
<?php
}
4. O que esse CRUD já faz corretamente
- Usa PDO com prepared statements (seguro contra SQL Injection);
- Valida nome e e-mail antes de salvar;
- Trata e-mails duplicados com mensagem amigável;
- Usa
htmlspecialchars()para exibir texto (proteção contra XSS); - Usa
switchsimples para separar ações:listarcriareditarexcluir
5. Próximos passos para deixar mais profissional
Se você quiser evoluir esse código:
- Separar em Controller/Model/View (como no post anterior de estrutura de pastas);
- Trocar a URL de
usuarios.php?acao=editar&id=1para algo mais amigável (via HTACCESS / router); - Criar paginação na listagem de usuários;
- Adicionar busca por nome/e-mail;
- Adicionar autenticação (permitir CRUD só para usuários logados).