今回の記事では、Laravelでトランザクション処理を行う2つの方法について紹介します。
トランザクションはDBのデータの登録や更新を行う上で、必ず必要になる処理です。
簡単にトランザクションの概要もあわせて紹介しますので、ぜひ参考にしてみてください。
トランザクションとは
トランザクション処理を用いると、中途半端なデータが生まれるのを防いでくれて、一意で厳密なデータの管理が可能になります。
「トランザクションの開始から、コミットするまで」を一つの処理として扱われて、エラーが発生した場合はデータをトランザクションの開始前に戻してくれます。
たとえば、よく技術者の間の例え話でよく上がるのですが、以下のような処理を行うとします。
- Aさんの口座から、Bさんの口座に1万円を送金
- Bさんの口座に1万円が入金
この「1」と「2」の処理の間でエラーが起こった場合、どうなるでしょうか?
以下のような結果になるはずですよね。
Aさんの口座:1万円が消える
Bさんの口座:1万円が入金されていない
結果:・・・1万円が行方不明!!
これではシステムとしてありえませんよね。
しかし、トランザクション処理がある場合は、以下のようになります。
Aさんの口座:1万円を送る前の金額から変わらない
Bさんの口座:そのまま金額が変わらない
このように、一つの処理をなかったことにしてくれます!
トランザクション処理はとても重要です。
データの登録・更新を行うときは、必ずトランザクション処理を行うようにしましょう!
Laravelのトランザクションを行う方法は2つあるので、それぞれ紹介します。
【方法1】手動のトランザクション
「手動ってことは書くの大変そう…」と思われるかもしれませんが、とても簡単です!
「try-catch」を記述して、以下3つを書くだけです!
- トランザクションの開始
- コミット
- ロールバック(エラー時)
実際に書いてみます!
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Manager;
use Illuminate\Support\Facades\DB;
class ManagerController extends Controller
{
public function register(Request $request)
{
try {
DB::beginTransaction();
Manager::create([
"login_id" => $request->login_id,
"password" => $request->password,
]);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
Log::error($e);
}
$message = "登録が完了しました。";
return view('register_manager', compact('message'));
}
}
必要な処理を上から順に紹介します。
まずは4行目のuseです。
use Illuminate\Support\Facades\DB;
useでDBを宣言をしないと、トランザクション処理がエラーになってしまうので忘れずに記述しましょう!
トランザクション処理の書き方は以下の通り!
分かりやすいようにコメントを追記してみました!
try {
// トランザクション開始
DB::beginTransaction();
// データの登録
Manager::create([
"login_id" => $request->login_id,
"password" => $request->password,
]);
// コミット
DB::commit();
} catch (\Exception $e) {
// エラーの処理はここに記述
// ロールバック
DB::rollback();
Log::error($e);
}
まずは「try-catch」を記述して、トランザクションの開始である「DB::beginTransaction();」を宣言します。
SQL文に相当する箇所を記述したら、最後にコミット「DB::commit();」します!
コミットの記述を忘れたら、DBにデータの登録や更新ができませんので注意してください。
またエラーで処理を中断して、データを登録や更新前に戻すのが「DB::rollback();」です。
エラーは「try-catch」の「catch」部分がエラー時に走る処理なので、「catch」部分に記述しましょう!
「トランザクション処理、ちゃんとできてるの?」と不安になったら・・・
「DB::commit();」の部分をコメントアウトして動作をチェックしてみましょう!
DBを確認してみると、データが登録されていないことを確認できますよ。
【方法2】クロージャを使用してトランザクション
トランザクションを行う1つ目の方法では、コミットを手動で記述しました。
2つ目の方法は、クロージャを使用して指定したブロックの処理が終わると、勝手にコミットしてくれる書き方です。
記述方法はとても簡単で、「try-catch」を記述して1つの記述を書くだけです!
以下は実際に書いてみたソースです。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Manager;
use Illuminate\Support\Facades\DB;
class ManagerController extends Controller
{
public function register(Request $request)
{
try {
DB::transaction(function () use($request) {
Manager::create([
"login_id" => $request->login_id,
"password" => $request->password,
]);
});
} catch (\Exception $e) {
Log::error($e);
}
$message = "登録が完了しました。";
return view('register_manager', compact('message'));
}
}
必要な処理を上から順に紹介します。
use Illuminate\Support\Facades\DB;
1つ目の方法と同じように、useでDBを宣言をしないと、トランザクション処理がエラーになってしまうので忘れずに記述しましょう!
クロージャを使用したトランザクション処理の書き方は以下の通り!
try {
DB::transaction(function () use($request) {
Manager::create([
"login_id" => $request->login_id,
"password" => $request->password,
]);
});
} catch (\Exception $e) {
Log::error($e);
}
2つ目の方法も、まずは「try-catch」を記述しましょう。
トランザクション内の処理でエラーが発生した場合はエラー画面が表示されてしまうので、必ず「catch」でエラー時の処理を記述します。
さて、肝心のトランザクションです。
DBファザードの「transactionメソッド」にクロージャーを渡して、その中でDBの処理を記述するだけです。
「use()」の引数には、渡したい変数を記述します。
「,」(カンマ)で区切ることで、複数の変数を渡すことも可能です。
2つ目の方法では、コミットやロールバックを記述する必要はありません。
処理中に問題があればロールバックされ、問題がなければコミットしてくれます。
よくやりがちな「コミット記述忘れ」が発生しないのはいいですね!
また、「transactionメソッド」の第2引数に最大試行回数の指定も行えます。
DB::transaction(function () use($request) {
// (略)
}, 2);
2つ目の書き方は、コミットやロールバックの記述忘れを回避できるのでおすすめです!
方法1はDBの登録・更新の際に外部のメソッドを実行してテーブルを更新するなど、別の処理が発生する際に利用するのがおすすめです!
ですが、自分の使いやすい、または覚えやすい方を使うといいですよ!
まとめ
PHPやLaravelなどの枠に限らず、DBの登録・更新時にトランザクション処理は必要になってくる大事な処理です。
最低限、複数のSQLを実行する時は、データに不整合が生まれないように必ずトランザクションを実行するようにしましょう!
書き方は紹介した通り簡単なので、どんどん使ってくださいね。