ゼロから始めるPHP講座Vol.38 トランザクション基礎


ゼロから始めるPHP講座Vol.38 トランザクション基礎
目次
  1. ゼロから始めるPHP講座 トランザクション基礎
  2. トランザクションとは
  3. トランザクションの使用例

ゼロから始めるPHP講座 トランザクション基礎

トランザクションとは

「関連する複数の処理を一つの処理としてまとめたもの」をトランザクションといいます。

例えば銀行にて指定の口座から別の口座へお金を移動されることが多くありますが、このときトランザクションが必要になります。

仮にAさんの口座から10万円をBさんの口座へ移動させる場合

  1. Aさんの口座残高を10万円減らす
  2. Bさんの口座残高を10万円増やす

上記2つの処理を連続して行う必要があります。

もしトランザクションを利用せずこの連続処理を実行し、2のBさんの口座に対する処理が失敗した場合、Aさんの口座から残高が減る一方、Bさんの口座残高が増えず、お金が消えてしまうことになり、データに不整合が発生してしまいます。

処理を行う前のAさんの口座残高が70万、Bさんの口座残高が30万とし、不整合の発生を図で表すと、次のようになります。 php_transaction1-639

このような場合、1と2の処理をトランザクションとしてまとめ、全ての処理が成功した場合のみ処理を確定させ、処理が一部でも失敗した場合はすべての処理を取り消すようにします。これにより、データの整合性が維持されます。

トランザクションの具体的な構文と処理の流れを次に説明します。php_transaction2-639

トランザクションを利用するには、まず開始の宣言を行います。その後連続した処理を行い、全て成功した場合は処理全体を確定(COMMIT)します。

一部でも失敗した場合は処理取消(ROLLBACK)を行い、処理前の状態に戻します。

トランザクションの使用例

実際にMySQLでトランザクションを用い整合性を保つプログラムを書いてみましょう。

ファイル名:transaction.php

[code title="transaction.php"]

<?php
/**
 * トランザクション処理を理解するためのサンプルプログラムです。
 * あくまで理解を助けるためのプログラムなので、トランザクション関連以外の処理はかなり省いています。
 */


// MySQL接続情報
$host   = 'localhost'; // データベースのホスト名又はIPアドレス
$user   = 'username';  // MySQLのユーザ名
$passwd = 'passwd';    // MySQLのパスワード
$dbname = 'dbname';    // データベース名

$customer_id = 1;          // 例題のため顧客は1に固定
$payment     = 'クレジット'; // 例題のため購入方法はクレジットに固定する
$quantity    = 1;          // 例題のため数量は1に固定
$goods_list  = array();
$err_msg     = array();

// コネクション取得
if ($link = mysqli_connect($host, $user, $passwd, $dbname)) {

    // 文字コードセット
    mysqli_set_charset($link, 'UTF8');

    // 購入処理
    if( $_SERVER['REQUEST_METHOD'] === 'POST' ) {

        // 現在時刻を取得
        $date = date('Y-m-d H:i:s');

        // 商品IDを取得
        $goods_id = (int) $_POST['goods_id'];

        // 更新系の処理を行う前にトランザクション開始(オートコミットをオフ)
        mysqli_autocommit($link, false);

        /**
         * 発注情報を挿入
         */

        // 挿入情報をまとめる
        $data = array(
            'customer_id' => $customer_id,
            'order_date'  => $date,
            'payment'     => $payment
        );

        // insertのSQL
        $sql = 'INSERT INTO order_table (customer_id, order_date, payment) VALUES(\'' . implode('\',\'', $data) . '\')';

        // insertを実行する
        if (mysqli_query($link, $sql) === TRUE) {

            // A_Iを取得
            $order_id = mysqli_insert_id($link);

            /**
             * 発注詳細情報を挿入
             */

            // 挿入情報をまとめる
            $data = array(
                'order_id' => $order_id,
                'goods_id' => $goods_id,
                'quantity' => $quantity
            );

            // 注文詳細情報をinsertする
            $sql = 'INSERT INTO order_detail_table (order_id, goods_id, quantity) VALUES(\'' . implode('\',\'', $data) . '\')';

            // insertを実行する
            if (mysqli_query($link, $sql) !== TRUE) {
                $err_msg[] = 'order_detail_table: insertエラー:' . $sql;
            }

        } else {
            $err_msg[] = 'order_table: insertエラー:' . $sql;
        }

        // トランザクション成否判定
        if (count($err_msg) === 0) {
            // 処理確定
            mysqli_commit($link);
        } else {
            // 処理取消
            mysqli_rollback($link);
        }

    }

    /**
     * 商品情報を取得
     */

    // SQL
    $sql = 'SELECT goods_id, goods_name, price FROM goods_table';

    // クエリ実行
    if ($result = mysqli_query($link, $sql)) {

        $i = 0;
        while ($row = mysqli_fetch_assoc($result)) {
            $goods_list[$i]['goods_id']   = htmlspecialchars($row['goods_id'],   ENT_QUOTES, 'UTF-8');
            $goods_list[$i]['goods_name'] = htmlspecialchars($row['goods_name'], ENT_QUOTES, 'UTF-8');
            $goods_list[$i]['price']      = htmlspecialchars($row['price'],      ENT_QUOTES, 'UTF-8');
            $i++;
        }


    } else {
        $err_msg[] = 'SQL失敗:' . $sql;
    }

    mysqli_free_result($result);
    mysqli_close($link);

} else {
    $err_msg[] = 'error: ' . mysqli_connect_error();
}

//var_dump($err_msg); // エラーの確認が必要ならばコメントを外す

?>

<!DOCTYPE HTML>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>トランザクションサンプル</title>
</head>
<body>
    <section>
        <h1>商品購入</h1>
        <ul>
<?php foreach ($goods_list as $goods) { ?>
            <li>
                <span><?php print $goods['goods_name']; ?></span>
                <span><?php print $goods['price']; ?>円</span>

                <form method="post">
                    <input type="hidden" name="goods_id" value="<?php print $goods['goods_id']; ?>">
                    <input type="submit" value="購入する">
                </form>
            </li>
<?php } ?>
        </ul>
        <p>※サンプルのため購入は1商品 & 1個に固定</p>
    </section>
</body>
</html>

php_transaction3-639

ユーザが商品購入を行う際の処理を想定しており、正規化を行った発注(order_table)と発注詳細(order_detail_table)へINSERTを行っています。

NEXT LESSON ☛ トランザクション応用

PREV LESSON ☛ データベースの便利機能


CodeCampus編集部
この記事を書いた人
CodeCampus編集部
まずは7日間お試し!人気プログラミング講座を無料公開中
オンライン・プログラミングレッスンNo.1のCodeCamp