PHP

Dominando Transações em PDO: Garantindo Integridade dos Dados em Projetos Reais

8 min de leitura

Dominando Transações em PDO: Garantindo Integridade dos Dados em Projetos Reais

O que são Transações em PDO? 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). No PDO, você controla transações manualmente usando , e . 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. Iniciando e Controlando Transações Estrutura Básica 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: inicia a transação, confirma as alterações,

<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">&lt;?php

try {

$pdo = new PDO(&#039;mysql:host=localhost;dbname=banco&#039;, &#039;usuario&#039;, &#039;senha&#039;);

$pdo-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo-&gt;beginTransaction();

// Operação 1: Debitar da conta A

$stmt = $pdo-&gt;prepare(&#039;UPDATE contas SET saldo = saldo - ? WHERE id = ?&#039;);

$stmt-&gt;execute([100, 1]);

// Operação 2: Creditar na conta B

$stmt = $pdo-&gt;prepare(&#039;UPDATE contas SET saldo = saldo + ? WHERE id = ?&#039;);

$stmt-&gt;execute([100, 2]);

$pdo-&gt;commit();

echo &quot;Transferência realizada com sucesso!&quot;;

} catch (Exception $e) {

$pdo-&gt;rollBack();

echo &quot;Erro: &quot; . $e-&gt;getMessage();

}

?&gt;</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">&lt;?php

if ($pdo-&gt;inTransaction()) {

echo &quot;Uma transação está em andamento&quot;;

$pdo-&gt;rollBack();

} else {

$pdo-&gt;beginTransaction();

// suas operações aqui

$pdo-&gt;commit();

}

?&gt;</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">&lt;?php

$pdo-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {

$pdo-&gt;beginTransaction();

$stmt = $pdo-&gt;prepare(&#039;INSERT INTO usuarios (email, nome) VALUES (?, ?)&#039;);

$stmt-&gt;execute([&#039;teste@email.com&#039;, &#039;João Silva&#039;]);

// Simulando um erro deliberado

$stmt = $pdo-&gt;prepare(&#039;INSERT INTO logs (user_id, acao) VALUES (?, ?)&#039;);

$stmt-&gt;execute([99999, &#039;login&#039;]); // user_id inexistente causa erro

$pdo-&gt;commit();

} catch (PDOException $e) {

$pdo-&gt;rollBack();

error_log(&quot;Transação falhou: &quot; . $e-&gt;getMessage());

echo &quot;Operação não pôde ser concluída. Tente novamente.&quot;;

}

?&gt;</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">&lt;?php

try {

$pdo-&gt;beginTransaction();

$stmt = $pdo-&gt;prepare(&#039;UPDATE produtos SET estoque = estoque - ? WHERE id = ?&#039;);

$stmt-&gt;execute([5, 10]);

if ($stmt-&gt;rowCount() === 0) {

throw new Exception(&quot;Produto não encontrado&quot;);

}

$pdo-&gt;commit();

} catch (PDOException $e) {

$pdo-&gt;rollBack();

echo &quot;Erro no banco de dados: &quot; . $e-&gt;errorInfo[2];

} catch (Exception $e) {

$pdo-&gt;rollBack();

echo &quot;Validação falhou: &quot; . $e-&gt;getMessage();

}

?&gt;</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">&lt;?php

function criarPedido($pdo, $usuarioId, $itens) {

try {

$pdo-&gt;beginTransaction();

// Criar pedido

$stmt = $pdo-&gt;prepare(&#039;INSERT INTO pedidos (usuario_id, data, total) VALUES (?, NOW(), ?)&#039;);

$total = array_sum(array_column($itens, &#039;preco&#039;));

$stmt-&gt;execute([$usuarioId, $total]);

$pedidoId = $pdo-&gt;lastInsertId();

// Inserir itens

$stmt = $pdo-&gt;prepare(&#039;INSERT INTO pedido_itens (pedido_id, produto_id, quantidade, preco) VALUES (?, ?, ?, ?)&#039;);

foreach ($itens as $item) {

$stmt-&gt;execute([$pedidoId, $item[&#039;produto_id&#039;], $item[&#039;quantidade&#039;], $item[&#039;preco&#039;]]);

}

// Atualizar estoque

$stmt = $pdo-&gt;prepare(&#039;UPDATE produtos SET estoque = estoque - ? WHERE id = ?&#039;);

foreach ($itens as $item) {

$stmt-&gt;execute([$item[&#039;quantidade&#039;], $item[&#039;produto_id&#039;]]);

}

$pdo-&gt;commit();

return [&#039;sucesso&#039; =&gt; true, &#039;pedido_id&#039; =&gt; $pedidoId];

} catch (Exception $e) {

$pdo-&gt;rollBack();

return [&#039;sucesso&#039; =&gt; false, &#039;erro&#039; =&gt; $e-&gt;getMessage()];

}

}

?&gt;</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">&lt;?php

$pdo-&gt;exec(&quot;SET TRANSACTION ISOLATION LEVEL SERIALIZABLE&quot;);

$pdo-&gt;beginTransaction();

// suas operações com garantias mais fortes

$pdo-&gt;commit();

?&gt;</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&#039;Reilly: Learning PHP, MySQL &amp; JavaScript - Capítulo sobre Transações</a></li>

</ul>

Comentários

Mais em PHP

Consumindo APIs Externas com Guzzle em PHP na Prática
Consumindo APIs Externas com Guzzle em PHP na Prática

Introdução ao Guzzle: O Cliente HTTP Essencial Guzzle é a biblioteca HTTP mai...

Boas Práticas de Documentação de APIs PHP com OpenAPI e Swagger para Times Ágeis
Boas Práticas de Documentação de APIs PHP com OpenAPI e Swagger para Times Ágeis

O que é OpenAPI e Swagger? OpenAPI é uma especificação aberta para descrever...

Boas Práticas de Laravel Migrations e Seeders na Prática para Times Ágeis
Boas Práticas de Laravel Migrations e Seeders na Prática para Times Ágeis

Entendendo Migrations: Versionamento do Seu Banco de Dados Migrations no Lara...