- 初版:2018.12.12
- laravel-5.7, php-7.2
開発中や特別なトランザクションで、rollback時の処理を追加したくなるときがある。laravelのDB::transaction() はrollback時処理を拡張できないし、DB::beginTransaction/commit/rollBack三兄弟をベタに使うとテストしづらいので、ヘルパー的なものを用意する。
単純に、rollback時共通でロギングするだけなら、Laravelのイベント機能を使ってrollbackイベントをListenして実行する方が良いでしょう。
ラッパークラス的な
<?php
/**
* transaction wrapper
*/
namespace App\ORM\Helpers;
use Illuminate\Support\Facades\DB;
class Transaction
{
/**
* DB::transactionはrollbackをハンドリングできないので、処理足す
* @param \Closure $process () no args
* @param \Closure $rollbacker ($rollback, $e) invoke $rollback() means DB::rollBack();
*/
public static function do(\Closure $process, \Closure $rollbacker = null)
{
$result = null;
if (is_callable($rollbacker)) {
// rollback指定あり
DB::beginTransaction();
try {
$result = $process();
DB::commit();
} catch (\Exception $e) {
$rollbacker(function () {
DB::rollBack();
}, $e);
}
} else {
// 指定なし
$result = DB::transaction(function () use ($process) {
return $process();
});
}
return $result;
}
}
呼び出し側
$eloquentInstance = Transaction::do(function () {
// DB::insert とか Eloquent::create とか
// ここのreturnの値がそのまま上での返り値となる
return $createdEloquentInstance;
}, function ($rollback, $e) {
// file_put_contents();でログするとかなにか特別な処理
$rollback();
// throw new \Exception(); とかなにか特別な処理
});
DB::transaction() をtry-catchで囲めばrollback後の処理は対応できるけど、ネストが深くなるのがいや。
ネストが深い
try {
DB::transaction(function () {
// nest fukai
});
} catch (\Exception $e) {
// after rollback
}