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



Cancelar

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



Cancelar

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

Exercícios

  1. Exercício 1: Crie uma nova tabela chamada produtos com 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);
  2. 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.
  3. 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.'); }.
  4. 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.
  5. 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.