<h2>Preparando o Ambiente: Conexão com PDO</h2>
<p>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.</p>
<p>Crie um arquivo <code>Database.php</code> para centralizar a conexão:</p>
<pre><code class="language-php"><?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());
}
}
}
?></code></pre>
<p>O atributo <code>ERRMODE_EXCEPTION</code> garante que erros sejam lançados como exceções, facilitando o tratamento. Guarde credenciais sensíveis em variáveis de ambiente em produção.</p>
<h2>CREATE: Inserindo Dados no Banco</h2>
<p>A operação CREATE adiciona novos registros ao banco. Use <strong>prepared statements</strong> sempre para evitar SQL Injection. Parâmetros nomeados (<code>:nome</code>) são mais legíveis que placeholders (<code>?</code>).</p>
<pre><code class="language-php"><?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!";
}
?></code></pre>
<p>O <code>execute()</code> retorna verdadeiro se bem-sucedido. Use <code>$this->pdo->lastInsertId()</code> se precisar do ID gerado automaticamente.</p>
<h2>READ: Recuperando e Exibindo Dados</h2>
<p>Read é a operação mais frequente. Implemente métodos para recuperar um registro específico, todos os registros ou aplicar filtros e paginação.</p>
<pre><code class="language-php"><?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);
}
?></code></pre>
<p><strong><code>fetchAll()</code></strong> retorna todos os resultados como array; <strong><code>fetch()</code></strong> retorna apenas um. <code>PDO::FETCH_ASSOC</code> retorna associativo (chaves nomeadas); use <code>PDO::FETCH_OBJ</code> para objetos.</p>
<h2>UPDATE: Modificando Registros Existentes</h2>
<p>Update altera dados já presentes no banco. Sempre valide e verifique se o registro existe antes de atualizar para evitar operações silenciosas.</p>
<pre><code class="language-php"><?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.";
}
?></code></pre>
<p>O método <code>rowCount()</code> informa quantas linhas foram afetadas. Útil para validar se a atualização realmente ocorreu.</p>
<h2>DELETE: Removendo Registros</h2>
<p>Delete é irreversível. Implemente confirmações, logs ou soft delete (marcar como inativo) em sistemas críticos.</p>
<pre><code class="language-php"><?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');
}
}
?></code></pre>
<p>Soft delete preserva dados para auditoria e recuperação. Sempre valide o ID antes de deletar para evitar exclusões acidentais.</p>
<h2>Conclusão</h2>
<p>Três pontos essenciais aprendidos: <strong>Prepared statements são não-negociáveis</strong> para segurança contra SQL Injection; <strong>organize o código em classes</strong> separando lógica de banco e regras de negócio; <strong>valide sempre o número de linhas afetadas</strong> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.php.net/manual/pt_BR/book.pdo.php" target="_blank" rel="noopener noreferrer">Documentação Oficial PHP PDO</a></li>
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer">OWASP: SQL Injection Prevention</a></li>
<li><a href="https://www.php.net/manual/pt_BR/pdo.prepared-statements.php" target="_blank" rel="noopener noreferrer">PHP.NET: Prepared Statements</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/php-crud" target="_blank" rel="noopener noreferrer">Digital Ocean: PHP CRUD com PDO</a></li>
</ul>