アプリで使用しているデータベースの整合性を保つために、トランザクションのデータベース関数があります。Laravelでは、現時点で2通りの方法があります。Laravelのバージョン12.32.0において、DB::afterRollBackが登場して、どちらとも機能的に同等になりました。

トランザクション

トランザクションを簡単に説明すると、例えば以下のように複数のデータベース文を実行するとき、


DB::table('points')->insert(['member_id' => 1, 'point' => 1000]);
DB::table('points')->insert(['member_id' => 1, 'point' => 2000]);

最初のデータベース文が成功して、2番目のデータベース文が何等かの理由で失敗したときに、成功したものだけを残すのでなく、両方と何も起こらなかった状態に戻す機能です。

つまり、この2つデータベース文をあたかも1つのデータベース文のように扱い、両方が成功するならコミットとし、失敗が起こるならロールバックします。上の例では、成功なら3000ポイント追加され失敗したならたとえ1つが成功しても0ポイントの追加となります。

DB::beginTransaction

トランザクションをLaravelで実行するには、以下のようにDB::beginTransactionを使います。

DB::beginTransaction();

try {
    DB::table('points')->insert(['member_id' => 1, 'point' => 1000]);
    DB::table('points')->insert(['member_id' => 1, 'point' => 2000]);

    DB::commit();

    echo "ポイント追加成功しました。";
} catch (\Exception $event) {
    DB::rollBack();

    echo "ポイント追加に失敗しました。";
    echo $event->getMessage(); // エラーを表示
}

トランザクションと対象となるデータベース文は、try,catchに入れて、成功ならDB::commitで変更を保存し、エラーがあるなら(例外が発生)、DB::rollbackですべての変更を戻します。DB::endTransactionというものはないのでブロックで囲まれてなく変な感じですが、問題はありません。DB::beginTransactiontryの中の最初の文としても変わりません。

DB::transaction

先の例では、いちいちコミットとロールバック、それぞれDB::commitDB::rollbackを指定する必要がありますが、DB::transactionを使い対象のデータベース文を以下のようにクロージャのなかに入れるなら、結構すっきりします。

DB::transaction(function () {
    DB::table('points')->insert(['member_id' => 1, 'point' => 1000]);
    DB::table('points')->insert(['member_id' => 1, 'point' => 2000]);
})

以下のようにデータを変数で渡すことも可能です。

$data = [
    ['member_id' => 1, 'point' => 1000],
    ['member_id' => 1, 'point' => 2000]
];

DB::transaction(function () use($data) {
    DB::table('points')->insert($data[0]);
    DB::table('points')->insert($data[1]);
})

また、DB::beginTransactionのように、コミットとロールバックの後に必要なプログラム文を追加も以下のように可能です。

$data = [
    ['member_id' => 1, 'point' => 1000],
    ['member_id' => 1, 'point' => 2000]
];

DB::transaction(function () use($data) {
    DB::afterCommit(function() {
        echo "ポイント追加成功しました。";
    });

    DB::afterRollBack(function () {
        echo "ポイント追加に失敗しました。";
    });

    DB::table('points')->insert($data[0]);
    DB::table('points')->insert($data[1]);
})

注意する必要があるのは、対象のデータベース文は、DB::afterCommitDB::afterRollBackの後に必ず置くことです。
また、DB::afterRollBackは、Laravelのバージョン12.32.0においての登場なので、それより前のバージョンでは使えません。

最後に、DB::beginTransactionの例のようにエラーをキャッチしたいなら、同様にtry,catchを使います。

$data = [
    ['member_id' => 1, 'point' => 1000],
    ['member_id' => 1, 'point' => 2000]
];

try {
    DB::transaction(function () use($data) {
        DB::afterCommit(function() {
            echo "ポイント追加成功しました。";
        });

        DB::afterRollBack(function () {
            echo "ポイント追加に失敗しました。";
        });

        DB::table('points')->insert($data[0]);
        DB::table('points')->insert($data[1]);
    });
} catch (\Exception $event) {
    echo $event->getMessage();
}
メルマガ購読の申し込みはこちらから。

By khino