Parte 03

Banco de dados

De SQL puro ao CRUD completo em PHP. A regra inegociável: sempre PDO com prepared statements — variável de usuário nunca entra concatenada na query.

01

Introdução

SQL é a linguagem para conversar com o banco de dados (MySQL/MariaDB no XAMPP). O PHP não guarda dados sozinho: ele conecta ao MySQL, envia comandos SQL e recebe os resultados de volta como arrays.

📌 Fluxo: navegador → PHP → (SQL) → MySQL → (linhas) → PHP → HTML. O PDO é a "ponte" do PHP para o banco.
02

Tipos de dados SQL

Cada coluna tem um tipo fixo. Escolher certo economiza espaço e evita erro.

TipoGuardaExemplo
INTNúmero inteiroidade, id
VARCHAR(n)Texto curto até n caracteresnome, email
TEXTTexto longo (sem limite prático)comentário, post
DATEData (AAAA-MM-DD)2026-06-01
DATETIMEData + hora2026-06-01 09:30:00
DECIMAL(m,d)Número exato (dinheiro!)DECIMAL(10,2) → 19.90
BOOLEANVerdadeiro/falso (na prática um TINYINT 0/1)ativo
⚠️ Dinheiro: use DECIMAL, nunca FLOAT. Float tem erro de arredondamento e some centavos.
03

DDL: criar e alterar tabelas

DDL (Data Definition Language) define a estrutura: criar banco, criar/alterar/remover tabelas.

schema.sql
-- Criar o banco (charset utf8mb4 = suporte total a acentos e emoji)
CREATE DATABASE agenda CHARACTER SET utf8mb4;

-- Criar a tabela
CREATE TABLE contatos (
    id        INT AUTO_INCREMENT PRIMARY KEY,
    nome      VARCHAR(100) NOT NULL,
    email     VARCHAR(150) UNIQUE,
    criado_em DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Adicionar uma coluna
ALTER TABLE contatos ADD telefone VARCHAR(20);

-- Modificar uma coluna existente
ALTER TABLE contatos MODIFY telefone VARCHAR(30);

-- Remover a tabela inteira (cuidado!)
DROP TABLE contatos;
04

Constraints

Regras que o banco garante por você. Definidas na criação da coluna.

ConstraintO que faz
PRIMARY KEYIdentificador único da linha (não repete, não nulo)
AUTO_INCREMENTO banco gera o próximo número sozinho
FOREIGN KEYLiga uma coluna ao id de outra tabela (relacionamento)
NOT NULLCampo obrigatório
UNIQUEValor não pode se repetir (ex.: email)
DEFAULTValor automático se nada for informado
foreign-key.sql
-- Cada pedido pertence a um contato (FOREIGN KEY)
CREATE TABLE pedidos (
    id         INT AUTO_INCREMENT PRIMARY KEY,
    contato_id INT NOT NULL,
    valor      DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (contato_id) REFERENCES contatos(id)
);
05

DML: manipular dados

DML (Data Manipulation Language) mexe nos dados: inserir, ler, atualizar, apagar. Estes exemplos são SQL "puro" (no phpMyAdmin); já já fazemos o mesmo via PHP.

dml.sql
-- INSERT: inserir uma linha
INSERT INTO contatos (nome, email) VALUES ('Ana', 'ana@site.com');

-- SELECT: ler com filtro, ordenação e limite
SELECT nome, email FROM contatos
WHERE nome LIKE 'A%'      -- começa com A
ORDER BY nome ASC          -- A→Z (DESC = Z→A)
LIMIT 10;                   -- no máximo 10 linhas

-- UPDATE: sempre com WHERE! (sem ele, altera TODAS as linhas)
UPDATE contatos SET email = 'nova@site.com' WHERE id = 1;

-- DELETE: idem, sempre com WHERE
DELETE FROM contatos WHERE id = 1;
⚠️ WHERE no UPDATE/DELETE: esquecer o WHERE aplica a operação em todas as linhas. Um DELETE FROM contatos; apaga a tabela inteira.
06

Conexão PDO

O PDO conecta via uma DSN (string que diz driver, host, banco e charset) + usuário + senha. Envolva em try/catch e ative o modo de exceção para os erros não passarem despercebidos.

conexao.php
<?php
// DSN: driver:host;banco;charset
$dsn = "mysql:host=localhost;dbname=agenda;charset=utf8mb4";
$usuario = "root";   // no XAMPP, root sem senha (em produção: usuário restrito!)
$senha   = "";

try {
    $pdo = new PDO($dsn, $usuario, $senha);

    // Erros viram exceções (essencial para o try/catch funcionar).
    $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) {
    // Mensagem amigável. NUNCA exiba $e->getMessage() em produção.
    exit("Não foi possível conectar ao banco. Tente mais tarde.");
}
07

CRUD com prepared statements

O coração da segurança: a query vai com placeholders (? ou :nome) e os valores são passados separados, no execute(). O banco trata os valores como dados, nunca como código — isso elimina SQL injection.

crud.php
<?php
// CREATE — inserir (placeholders posicionais ?)
$stmt = $pdo->prepare("INSERT INTO contatos (nome, email) VALUES (?, ?)");
$stmt->execute(["Ana", "ana@site.com"]);
echo $pdo->lastInsertId();   // id gerado, ex.: 1

// READ (vários) — fetchAll devolve um array de linhas
$contatos = $pdo->query("SELECT id, nome FROM contatos ORDER BY nome")->fetchAll();

// READ (um) — placeholder nomeado :id
$stmt = $pdo->prepare("SELECT * FROM contatos WHERE id = :id");
$stmt->execute([":id" => 1]);
$contato = $stmt->fetch();   // uma linha (ou false se não achar)

// UPDATE
$stmt = $pdo->prepare("UPDATE contatos SET email = ? WHERE id = ?");
$stmt->execute(["novo@site.com", 1]);

// DELETE
$stmt = $pdo->prepare("DELETE FROM contatos WHERE id = ?");
$stmt->execute([1]);
⚠️ NUNCA faça isto: "... WHERE id = $id" (concatenar variável na query). É a porta da SQL injection. Sempre placeholder + execute().
08

MySQLi vs PDO

Existem duas APIs no PHP: MySQLi (só MySQL) e PDO (funciona com MySQL, PostgreSQL, SQLite… — é portável). Prefira PDO: mesma sintaxe para qualquer banco e prepared statements mais limpos. O exemplo abaixo é só para comparação.

mysqli-comparacao.php
<?php
// MySQLi — apenas para comparação. Na prática, use PDO.
$mysqli = new mysqli("localhost", "root", "", "agenda");

$resultado = $mysqli->query("SELECT nome FROM contatos");
while ($linha = $resultado->fetch_assoc()) {
    echo $linha["nome"];
}
09

Mini-projeto: agenda de contatos

CRUD completo num arquivo só, usando o conexao.php da seção 06. O mesmo formulário serve para criar e editar; a listagem traz links de editar/excluir. Tudo com prepared statements e saída escapada com h().

agenda.php
<?php
require "conexao.php";   // traz o $pdo já configurado

// Helper: escapa toda saída (anti-XSS).
function h(string $s): string {
    return htmlspecialchars($s, ENT_QUOTES, "UTF-8");
}

// ---- CRIAR ou ATUALIZAR (POST) ----
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $id    = (int) ($_POST["id"] ?? 0);
    $nome  = trim($_POST["nome"] ?? "");
    $email = trim($_POST["email"] ?? "");

    if ($nome !== "") {
        if ($id > 0) {
            // Editar existente.
            $stmt = $pdo->prepare("UPDATE contatos SET nome = ?, email = ? WHERE id = ?");
            $stmt->execute([$nome, $email, $id]);
        } else {
            // Inserir novo.
            $stmt = $pdo->prepare("INSERT INTO contatos (nome, email) VALUES (?, ?)");
            $stmt->execute([$nome, $email]);
        }
    }
    header("Location: agenda.php");   // POST-Redirect-GET
    exit;
}

// ---- EXCLUIR (GET ?excluir=ID) ----
if (isset($_GET["excluir"])) {
    $stmt = $pdo->prepare("DELETE FROM contatos WHERE id = ?");
    $stmt->execute([(int) $_GET["excluir"]]);
    header("Location: agenda.php");
    exit;
}

// ---- EDITAR: carregar o contato no formulário (GET ?editar=ID) ----
$edicao = null;
if (isset($_GET["editar"])) {
    $stmt = $pdo->prepare("SELECT * FROM contatos WHERE id = ?");
    $stmt->execute([(int) $_GET["editar"]]);
    $edicao = $stmt->fetch();
}

// ---- LISTAR ----
$contatos = $pdo->query("SELECT id, nome, email FROM contatos ORDER BY nome")->fetchAll();
?>
<!DOCTYPE html>
<html lang="pt-BR">
<body>
    <h1>Agenda de contatos</h1>

    <!-- 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="email" name="email" value="<?= h($edicao["email"] ?? "") ?>" placeholder="E-mail">
        <button type="submit"><?= $edicao ? "Salvar" : "Adicionar" ?></button>
    </form>

    <table>
    <?php foreach ($contatos as $c): ?>
        <tr>
            <td><?= h($c["nome"]) ?></td>
            <td><?= h($c["email"]) ?></td>
            <td>
                <a href="?editar=<?= (int) $c["id"] ?>">editar</a>
                <a href="?excluir=<?= (int) $c["id"] ?>"
                   onclick="return confirm('Excluir?')">excluir</a>
            </td>
        </tr>
    <?php endforeach; ?>
    </table>
</body>
</html>
📌 Como testar: rode o schema.sql (seção 03) no phpMyAdmin para criar o banco e a tabela, salve conexao.php e agenda.php em htdocs/, e acesse http://localhost/agenda.php pelo Apache.
← Parte 2 · PHP web Voltar à capa →