Preparando o Ambiente: Conexão com PDO
A PDO (PHP Data Objects) é uma camada de abstração que permite trabalhar com múltiplos bancos de dados usando uma interface uniforme. Antes de implementar o CRUD, você precisa estabelecer uma conexão segura e reutilizável.
Crie um arquivo Database.php para centralizar a conexão:
<?php
class Database {
private $host = 'localhost';
private $db = 'loja';
private $user = 'root';
private $pass = '';
private $pdo;
public function connect() {
try {
$this->pdo = new PDO(
'mysql:host=' . $this->host . ';dbname=' . $this->db,
$this->user,
$this->pass,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
return $this->pdo;
} catch (PDOException $e) {
die('Erro na conexão: ' . $e->getMessage());
}
}
}
?>
O atributo ERRMODE_EXCEPTION garante que erros sejam lançados como exceções, facilitando o tratamento. Guarde credenciais sensíveis em variáveis de ambiente em produção.
CREATE: Inserindo Dados no Banco
A operação CREATE adiciona novos registros ao banco. Use prepared statements sempre para evitar SQL Injection. Parâmetros nomeados (:nome) são mais legíveis que placeholders (?).
<?php
class Produto {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function criar($nome, $preco, $estoque) {
$sql = "INSERT INTO produtos (nome, preco, estoque, criado_em)
VALUES (:nome, :preco, :estoque, NOW())";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([
':nome' => $nome,
':preco' => $preco,
':estoque' => $estoque
]);
}
}
// Uso:
$db = new Database();
$pdo = $db->connect();
$produto = new Produto($pdo);
if ($produto->criar('Notebook', 2500.00, 10)) {
echo "Produto inserido com sucesso!";
}
?>
O execute() retorna verdadeiro se bem-sucedido. Use $this->pdo->lastInsertId() se precisar do ID gerado automaticamente.
READ: Recuperando e Exibindo Dados
Read é a operação mais frequente. Implemente métodos para recuperar um registro específico, todos os registros ou aplicar filtros e paginação.
<?php
public function obterTodos($limit = 10, $offset = 0) {
$sql = "SELECT * FROM produtos LIMIT :limit OFFSET :offset";
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':limit', (int)$limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', (int)$offset, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function obterPorId($id) {
$sql = "SELECT * FROM produtos WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':id' => $id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
public function buscar($termo) {
$sql = "SELECT * FROM produtos WHERE nome LIKE :termo";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([':termo' => "%{$termo}%"]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
?>
fetchAll() retorna todos os resultados como array; fetch() retorna apenas um. PDO::FETCH_ASSOC retorna associativo (chaves nomeadas); use PDO::FETCH_OBJ para objetos.
UPDATE: Modificando Registros Existentes
Update altera dados já presentes no banco. Sempre valide e verifique se o registro existe antes de atualizar para evitar operações silenciosas.
<?php
public function atualizar($id, $nome, $preco, $estoque) {
$sql = "UPDATE produtos
SET nome = :nome, preco = :preco, estoque = :estoque, atualizado_em = NOW()
WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
$resultado = $stmt->execute([
':id' => $id,
':nome' => $nome,
':preco' => $preco,
':estoque' => $estoque
]);
return $stmt->rowCount() > 0 ? true : false;
}
public function incrementarEstoque($id, $quantidade) {
$sql = "UPDATE produtos SET estoque = estoque + :qtd WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([
':id' => $id,
':qtd' => $quantidade
]);
}
// Uso:
if ($produto->atualizar(1, 'Notebook Gamer', 3500.00, 5)) {
echo "Atualizado com sucesso!";
} else {
echo "Nenhum registro foi modificado.";
}
?>
O método rowCount() informa quantas linhas foram afetadas. Útil para validar se a atualização realmente ocorreu.
DELETE: Removendo Registros
Delete é irreversível. Implemente confirmações, logs ou soft delete (marcar como inativo) em sistemas críticos.
<?php
public function deletar($id) {
$sql = "DELETE FROM produtos WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([':id' => $id]) && $stmt->rowCount() > 0;
}
// Soft delete (recomendado):
public function desativar($id) {
$sql = "UPDATE produtos SET ativo = 0, deletado_em = NOW() WHERE id = :id";
$stmt = $this->pdo->prepare($sql);
return $stmt->execute([':id' => $id]);
}
// Uso em controlador:
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['deletar_id'])) {
$id = (int)$_POST['deletar_id'];
if ($produto->deletar($id)) {
header('Location: /produtos');
}
}
?>
Soft delete preserva dados para auditoria e recuperação. Sempre valide o ID antes de deletar para evitar exclusões acidentais.
Conclusão
Três pontos essenciais aprendidos: Prepared statements são não-negociáveis para segurança contra SQL Injection; organize o código em classes separando lógica de banco e regras de negócio; valide sempre o número de linhas afetadas para confirmar sucesso real de operações, especialmente em UPDATE e DELETE. Com PDO bem implementada, você tem código seguro, testável e portável entre diferentes bancos de dados.