CRUD de pacientes
Um mini-sistema de hospital: cadastrar, listar, editar e remover pacientes com 5 características. Tudo com PDO + prepared statements — o caminho seguro e recomendado.
? no lugar dos valores, e os dados são passados separados no execute(). O banco trata tudo como dado, nunca como comando — isso elimina SQL injection. Jamais concatene variável dentro da query (ex.: "... WHERE id = $id").
Criar a tabela
Primeiro a estrutura no banco. O id é a chave primária que se autoincrementa; as outras 5 colunas são as características do paciente. Rode isto uma vez no phpMyAdmin.
CREATE TABLE pacientes (
id INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL, -- 1
idade INT NOT NULL, -- 2
telefone VARCHAR(20), -- 3
sintoma VARCHAR(100), -- 4
data_entrada DATE -- 5
);
Conectar ao banco (PDO)
A conexão fica num arquivo separado (conexao.php) para reaproveitar. Sempre dentro de try/catch e com o modo de exceção ligado, para os erros não passarem em branco.
<?php
$dsn = "mysql:host=localhost;dbname=hospital;charset=utf8mb4";
try {
$pdo = new PDO($dsn, "root", "");
// Erros viram exceções (essencial para o try/catch agir).
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// fetch() devolve array associativo por padrão.
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
exit("Não foi possível conectar ao banco.");
}
Adicionar paciente (INSERT)
Cinco ? na query, cinco valores no execute(), na mesma ordem. Depois de inserir, lastInsertId() devolve o id que o banco gerou.
<?php
require "conexao.php";
$sql = "INSERT INTO pacientes (nome, idade, telefone, sintoma, data_entrada)
VALUES (?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute(["Ana Lima", 34, "11999998888", "Febre", "2026-06-01"]);
echo "Paciente cadastrado! ID: " . $pdo->lastInsertId();
// Paciente cadastrado! ID: 1
Listar pacientes (SELECT)
Para ler, use SELECT e percorra o resultado com foreach. Como não há valor do usuário na query, aqui pode usar query() direto. Toda saída no HTML é escapada com h().
<?php
require "conexao.php";
// Helper anti-XSS: escapa tudo que vai para o HTML.
function h(string $s): string {
return htmlspecialchars($s, ENT_QUOTES, "UTF-8");
}
$pacientes = $pdo->query("SELECT * FROM pacientes ORDER BY nome")->fetchAll();
foreach ($pacientes as $p) {
echo h($p["nome"]) . ", " . $p["idade"] . " anos";
echo " — " . h($p["sintoma"]) . "\n";
}
// Saída: Ana Lima, 34 anos — Febre
Editar paciente (UPDATE)
O UPDATE também usa ? — inclusive no WHERE id = ?, que nunca pode faltar (senão altera todos). rowCount() diz quantas linhas foram afetadas.
<?php
require "conexao.php";
$sql = "UPDATE pacientes SET sintoma = ?, data_entrada = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute(["Tosse", "2026-06-02", 1]);
echo $stmt->rowCount() . " paciente atualizado";
// 1 paciente atualizado
Remover paciente (DELETE)
Mesma ideia: DELETE com WHERE id = ? e o id ligado como parâmetro. Por segurança, faça o cast (int) quando o id vier da URL.
<?php
require "conexao.php";
$id = (int) ($_GET["id"] ?? 0); // cast protege contra lixo na URL
$stmt = $pdo->prepare("DELETE FROM pacientes WHERE id = ?");
$stmt->execute([$id]);
echo "Paciente removido";
// Paciente removido
INSERT, Read = SELECT, Update = UPDATE, Delete = DELETE. Os quatro sempre com prepare() + execute() quando há valor de fora.
Tudo junto: a página completa
Agora os 6 passos num arquivo só, com o HTML do formulário e da listagem. O mesmo formulário cria e edita (o id escondido decide qual). Após salvar, faz POST-Redirect-GET (redireciona para não reenviar no F5) com um ?msg= que vira uma mensagem de aviso (via match). A tabela mostra todas as colunas e há um CSS mínimo no <head>. Toda saída passa por h().
<?php
require "conexao.php"; // traz o $pdo já configurado
function h(string $s): string {
return htmlspecialchars($s, ENT_QUOTES, "UTF-8");
}
// Converte a data do banco (YYYY-MM-DD) para BR (DD/MM/AAAA); vazia/NULL vira "—".
function dataBr(?string $data): string {
if ($data === null || $data === "" || $data === "0000-00-00") {
return "—";
}
return (new DateTime($data))->format("d/m/Y");
}
// ---- CRIAR ou ATUALIZAR (POST) ----
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$id = (int) ($_POST["id"] ?? 0);
$nome = trim($_POST["nome"] ?? "");
$idade = (int) ($_POST["idade"] ?? 0);
$telefone = trim($_POST["telefone"] ?? "");
$sintoma = trim($_POST["sintoma"] ?? "");
// Armadilha: data vazia numa coluna DATE vira 0000-00-00. Por isso vazio = NULL.
$entrada = trim($_POST["data_entrada"] ?? "");
$entrada = $entrada !== "" ? $entrada : null;
if ($nome !== "") {
if ($id > 0) {
// Editar existente.
$stmt = $pdo->prepare("UPDATE pacientes SET nome=?, idade=?, telefone=?, sintoma=?, data_entrada=? WHERE id=?");
$stmt->execute([$nome, $idade, $telefone, $sintoma, $entrada, $id]);
} else {
// Inserir novo.
$stmt = $pdo->prepare("INSERT INTO pacientes (nome, idade, telefone, sintoma, data_entrada) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$nome, $idade, $telefone, $sintoma, $entrada]);
}
}
header("Location: pacientes.php?msg=salvo"); // POST-Redirect-GET + aviso
exit;
}
// ---- EXCLUIR (GET ?excluir=ID) ----
if (isset($_GET["excluir"])) {
$stmt = $pdo->prepare("DELETE FROM pacientes WHERE id = ?");
$stmt->execute([(int) $_GET["excluir"]]);
header("Location: pacientes.php?msg=removido");
exit;
}
// ---- EDITAR: carregar o paciente no formulário (GET ?editar=ID) ----
$edicao = null;
if (isset($_GET["editar"])) {
$stmt = $pdo->prepare("SELECT * FROM pacientes WHERE id = ?");
$stmt->execute([(int) $_GET["editar"]]);
$edicao = $stmt->fetch();
}
// ---- LISTAR ----
$pacientes = $pdo->query("SELECT * FROM pacientes ORDER BY nome")->fetchAll();
// Mensagem de feedback, conforme o ?msg=... que veio do redirect.
$aviso = match ($_GET["msg"] ?? "") {
"salvo" => "Paciente salvo!",
"removido" => "Paciente removido!",
default => "",
};
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<style>
body { font-family: sans-serif; max-width: 640px; margin: 30px auto; }
input, button { padding: 6px; margin: 2px 0; }
table { width: 100%; border-collapse: collapse; margin-top: 16px; }
th, td { border: 1px solid #ccc; padding: 6px 10px; text-align: left; }
.aviso { background: #e7f9ec; color: #1c7c3a; padding: 8px 12px; border-radius: 6px; }
</style>
</head>
<body>
<h1>Pacientes</h1>
<!-- Aparece só quando há mensagem -->
<?php if ($aviso !== ""): ?>
<p class="aviso"><?= h($aviso) ?></p>
<?php endif; ?>
<!-- O mesmo form cria E edita (o id escondido decide) -->
<form method="post">
<input type="hidden" name="id" value="<?= h($edicao["id"] ?? "") ?>">
<input type="text" name="nome" value="<?= h($edicao["nome"] ?? "") ?>" placeholder="Nome">
<input type="number" name="idade" value="<?= h($edicao["idade"] ?? "") ?>" placeholder="Idade">
<input type="text" name="telefone" value="<?= h($edicao["telefone"] ?? "") ?>" placeholder="Telefone">
<input type="text" name="sintoma" value="<?= h($edicao["sintoma"] ?? "") ?>" placeholder="Sintoma">
<input type="date" name="data_entrada" value="<?= h($edicao["data_entrada"] ?? "") ?>">
<button type="submit"><?= $edicao ? "Salvar" : "Adicionar" ?></button>
</form>
<table>
<tr><th>Nome</th><th>Idade</th><th>Telefone</th><th>Entrada</th><th>Sintoma</th><th>Ações</th></tr>
<?php foreach ($pacientes as $p): ?>
<tr>
<td><?= h($p["nome"]) ?></td>
<td><?= h($p["idade"]) ?></td>
<td><?= h($p["telefone"]) ?></td>
<td><?= dataBr($p["data_entrada"]) ?></td>
<td><?= h($p["sintoma"]) ?></td>
<td>
<a href="?editar=<?= (int) $p["id"] ?>">editar</a>
<a href="?excluir=<?= (int) $p["id"] ?>"
onclick="return confirm('Excluir?')">excluir</a>
</td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>
conexao.php e pacientes.php em htdocs/ e acesse http://localhost/pacientes.php pelo Apache. Como usa header(), precisa rodar no servidor — não abra como file://.