ことれいのもり

失敗しても巻き戻せる!PHP×PDOでトランザクション処理を実装する方法【コード付き】

はじめに

複数のテーブルにデータを挿入するときに、どこか一つでも失敗したら全部取り消したいと思ったことはありませんか?

そんなときに活躍するのが「トランザクション処理」です。

例えば、記事を投稿する処理を作りたいとき、記事データに加えて画像やサムネイルも一括で保存したいとします。

一つでも失敗すればデータの整合性が崩れてしまうでしょう。

このようなリスクを避けるために、PHPのPDO::ATTR_AUTOCOMMITとトランザクション処理を活用する方法を紹介します。

前提

言語

  • PHP
  • MySQL

参考リンク

トランザクションとは?

トランザクションとは、処理をひとまとまりとして扱う仕組みのことです。

途中でエラーが発生したときは、この処理のまとまりを「なかったこと」にすることができます。

「データベースに記事を投稿する」ということを例にすると、以下のような流れです。

トランザクション処理のフローチャート

「トランザクション開始」と明示することで、以降の処理をひとまとまりとして扱うことができます。

その後2つのSQL文を実行し、全てのSQL文がエラーなく成功したらコミット(処理の確定)します。

一つでも失敗すればロールバック(全ての処理を無かったことにする)します。

こうすることで、データの一部だけ保存されてしまった、ということを防ぎます。

ATTR_AUTOCOMMITを使う理由

PDO(PHPでデータベースにアクセスするためのもの)では、基本的に自動でコミットがされます。

つまり、SQL文を実行する度にデータベースに即反映されます。

しかし、複数の処理をまとめて行いたい場合、この機能は邪魔です。

そこで、ATTR_AUTOCOMMITという機能をPHPを使って無効にすることで、自分でcommit()するまでは反映されないようにします。

// setAttributeを使ってATTR_AUTOCOMMITの機能を変更する
// 0: 無効, 1: 有効
$this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);

// トランザクション開始する
$this->db->beginTransaction();

エラー時のロールバック処理

複数の処理の中で、一つでもエラーが発生した場合は、ロールバックして無かったことにします。

これは、例外処理でキャッチします。

try {
    // いくつかSQL文を実行する処理

} catch(PDOException $e) {
    // 例外を検知したらロールバック
    $this->db->rollBack();
    echo "エラー: " . $e->getMessage();
} 

実装例:トランザクション処理の流れ

それでは、実際にトランザクションを使った処理の流れを見てみましょう。

ここでは、複数のINSERT処理をひとまとまりとして扱い、エラーがあればロールバックする基本的な構成を紹介します。

以下のコードは簡略化した例です。

try {
    $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 0);
    $this->db->beginTransaction();

    // 何かのINSERT文
    $stmt = $this->db->prepare("INSERT INTO articles (...) VALUES (...)");
    $stmt->execute();

    // 他のINSERT処理もここでやる

    $this->db->commit();
    $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
} catch (PDOException $e) {
    $this->db->rollBack();
    $this->db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
    echo "エラー: " . $e->getMessage();
}

このように、beginTransactionから始めて、全ての処理が成功したらcommit、エラーがあったらrollBackという流れになります。

より実践的なコードを見たい方は、実際に私が使っている関数を以下にまとめています。

実装例はこちら(GitHub Gist)

insertArticles_with_transaction.phpの完全版を見る

おわりに

トランザクションは、失敗しても壊れないという仕組みを作る上で大切な技術です。

複数のテーブルに挿入するときにトランザクションの処理を入れておくと、バグの発生も防ぐことができます。

「処理の途中で失敗して、データが中途半端に残った・・・」という人はぜひトランザクション処理を使ってみてください!