<h2>O que são Transações em PDO?</h2>
<p>Uma transação é um conjunto de operações no banco de dados que deve ser executado completamente ou não ser executado. O PDO (PHP Data Objects) fornece mecanismos simples para gerenciar transações, garantindo que dados inconsistentes nunca sejam salvos. Imagine uma transferência bancária: você precisa debitar de uma conta e creditar em outra. Se apenas uma operação for concluída, os dados ficarão corrompidos. Transações resolvem exatamente isso, funcionando sob o princípio ACID (Atomicidade, Consistência, Isolamento e Durabilidade).</p>
<p>No PDO, você controla transações manualmente usando <code>beginTransaction()</code>, <code>commit()</code> e <code>rollback()</code>. Se algo der errado durante a execução, pode desfazer todas as alterações com um único comando. Isso é essencial para operações críticas onde a integridade dos dados é não-negociável.</p>
<h2>Iniciando e Controlando Transações</h2>
<h3>Estrutura Básica</h3>
<p>O fluxo de uma transação é simples: inicie, execute operações e finalize com confirmação ou desfaça tudo em caso de erro. O PDO oferece três métodos fundamentais: <code>beginTransaction()</code> inicia a transação, <code>commit()</code> confirma as alterações, e <code>rollback()</code> desfaz tudo.</p>
<pre><code class="language-php"><?php
try {
$pdo = new PDO('mysql:host=localhost;dbname=banco', 'usuario', 'senha');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->beginTransaction();
// Operação 1: Debitar da conta A
$stmt = $pdo->prepare('UPDATE contas SET saldo = saldo - ? WHERE id = ?');
$stmt->execute([100, 1]);
// Operação 2: Creditar na conta B
$stmt = $pdo->prepare('UPDATE contas SET saldo = saldo + ? WHERE id = ?');
$stmt->execute([100, 2]);
$pdo->commit();
echo "Transferência realizada com sucesso!";
} catch (Exception $e) {
$pdo->rollBack();
echo "Erro: " . $e->getMessage();
}
?></code></pre>
<h3>Verificando o Estado da Transação</h3>
<p>Às vezes você precisa verificar se uma transação está ativa. O método <code>inTransaction()</code> retorna um booleano indicando o status. Isso é útil em operações encadeadas ou quando múltiplas funções manipulam o banco.</p>
<pre><code class="language-php"><?php
if ($pdo->inTransaction()) {
echo "Uma transação está em andamento";
$pdo->rollBack();
} else {
$pdo->beginTransaction();
// suas operações aqui
$pdo->commit();
}
?></code></pre>
<h2>Tratamento de Erros e Exceções</h2>
<h3>Configurando o Modo de Erro</h3>
<p>A forma como o PDO reporta erros afeta diretamente como suas transações funcionam. O modo <code>PDO::ERRMODE_EXCEPTION</code> é recomendado para trabalhar com transações, pois lança exceções que podem ser capturadas e tratadas elegantemente.</p>
<pre><code class="language-php"><?php
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare('INSERT INTO usuarios (email, nome) VALUES (?, ?)');
$stmt->execute(['teste@email.com', 'João Silva']);
// Simulando um erro deliberado
$stmt = $pdo->prepare('INSERT INTO logs (user_id, acao) VALUES (?, ?)');
$stmt->execute([99999, 'login']); // user_id inexistente causa erro
$pdo->commit();
} catch (PDOException $e) {
$pdo->rollBack();
error_log("Transação falhou: " . $e->getMessage());
echo "Operação não pôde ser concluída. Tente novamente.";
}
?></code></pre>
<h3>Tratamento Granular</h3>
<p>Em operações complexas, você pode precisar de tratamento mais específico. Use diferentes <code>catch</code> blocks ou verifique códigos de erro SQL antes de decidir se vai fazer rollback.</p>
<pre><code class="language-php"><?php
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare('UPDATE produtos SET estoque = estoque - ? WHERE id = ?');
$stmt->execute([5, 10]);
if ($stmt->rowCount() === 0) {
throw new Exception("Produto não encontrado");
}
$pdo->commit();
} catch (PDOException $e) {
$pdo->rollBack();
echo "Erro no banco de dados: " . $e->errorInfo[2];
} catch (Exception $e) {
$pdo->rollBack();
echo "Validação falhou: " . $e->getMessage();
}
?></code></pre>
<h2>Casos de Uso e Boas Práticas</h2>
<h3>Quando Usar Transações</h3>
<p>Transações são essenciais em operações que envolvem múltiplos passos interdependentes. Operações financeiras, criação de pedidos com itens associados, ou atualizações em cascata são cenários clássicos. Porém, não abuse: transações consomem recursos e podem causar deadlocks se mal implementadas.</p>
<pre><code class="language-php"><?php
function criarPedido($pdo, $usuarioId, $itens) {
try {
$pdo->beginTransaction();
// Criar pedido
$stmt = $pdo->prepare('INSERT INTO pedidos (usuario_id, data, total) VALUES (?, NOW(), ?)');
$total = array_sum(array_column($itens, 'preco'));
$stmt->execute([$usuarioId, $total]);
$pedidoId = $pdo->lastInsertId();
// Inserir itens
$stmt = $pdo->prepare('INSERT INTO pedido_itens (pedido_id, produto_id, quantidade, preco) VALUES (?, ?, ?, ?)');
foreach ($itens as $item) {
$stmt->execute([$pedidoId, $item['produto_id'], $item['quantidade'], $item['preco']]);
}
// Atualizar estoque
$stmt = $pdo->prepare('UPDATE produtos SET estoque = estoque - ? WHERE id = ?');
foreach ($itens as $item) {
$stmt->execute([$item['quantidade'], $item['produto_id']]);
}
$pdo->commit();
return ['sucesso' => true, 'pedido_id' => $pedidoId];
} catch (Exception $e) {
$pdo->rollBack();
return ['sucesso' => false, 'erro' => $e->getMessage()];
}
}
?></code></pre>
<h3>Nível de Isolamento</h3>
<p>Diferentes bancos de dados oferecem níveis de isolamento que afetam como transações simultâneas interagem. O padrão geralmente é adequado, mas em casos críticos você pode ajustar:</p>
<pre><code class="language-php"><?php
$pdo->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
$pdo->beginTransaction();
// suas operações com garantias mais fortes
$pdo->commit();
?></code></pre>
<h2>Conclusão</h2>
<p>Transações em PDO são o mecanismo fundamental para garantir integridade de dados em aplicações sérias. O ponto crucial é sempre envolver operações interdependentes em blocos try-catch, iniciando com <code>beginTransaction()</code> e finalizando com <code>commit()</code> ou <code>rollBack()</code>. Segundo, configure seu PDO para modo de exceções (<code>PDO::ERRMODE_EXCEPTION</code>) — isso torna o código mais previsível e fácil de debugar. Terceiro, não use transações indiscriminadamente; reserve-as para operações realmente críticas onde a consistência é não-negociável, evitando gargalos de performance.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.php.net/manual/en/pdo.transactions.php" target="_blank" rel="noopener noreferrer">PHP Manual: PDO Transactions</a></li>
<li><a href="https://www.php.net/manual/en/class.pdo.php" target="_blank" rel="noopener noreferrer">PHP Manual: PDO Methods</a></li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/en/commit.html" target="_blank" rel="noopener noreferrer">MySQL Documentation: ACID Compliance</a></li>
<li><a href="https://www.postgresql.org/docs/current/transaction-iso.html" target="_blank" rel="noopener noreferrer">PostgreSQL: Transaction Isolation</a></li>
<li><a href="https://www.oreilly.com/library/view/learning-php-mysql/9781491918654/" target="_blank" rel="noopener noreferrer">O'Reilly: Learning PHP, MySQL & JavaScript - Capítulo sobre Transações</a></li>
</ul>