2018.12.23

Laravel Tips 🍬 transactionメソッドは値を返す


Laravel の小ネタです。
意外と知られていない?transaction メソッドの使い方を紹介します。

先に結論を言っておくと、transaction メソッドはこのように値を返すことができます。

$article = DB::transaction(function () use ($request) {
    $article = new Article();
    $article->title = $request->title;
    $article->body = $request->body;
    $article->save();

    $article->tags()->save($request->tags);

    return $article;
});

return view('articles.show', compact('article'));

transactionメソッドって何?

トランザクションとは

トランザクションとは、データベース操作において必ずまとめて実行されるべき一連の処理ですね。例えばテーブルAとBに対して順番にデータ追加を行う処理で、テーブルAにデータを追加してからエラーが発生した場合、「AにはデータがあるがBにはない」状態になります。この状態がシステム上好ましくない、不整合な状態であるならば、上記の処理はトランザクションとして扱う必要があります。つまり、テーブルAにデータを追加してからエラーが発生した場合は直前のAに対するデータ操作をロールバック(巻き戻し)して「AにもBにもデータがない」不整合のない状態に戻す必要があります。

トランザクションのためには、一連の処理が終わるまではデータ操作の結果は仮確定の状態にしておいて、エラーがあったら巻き戻す、ある地点までエラーなく処理できたら結果を確定させる、という機能が必要です。DB 製品にはこのトランザクション機能が備わっていますし、たいていの Web アプリケーションフレームワークも DB のトランザクション機能を抽象化したメソッドが用意されていることでしょう。

Laravel におけるトランザクション

さて、Laravel におけるトランザクションの実現方法は2通りあります。

まずは try catch を用いた記述方法です。

// トランザクション開始
DB::beginTransaction();

try {
    $article = new Article();
    $article->body = $request->body;
    $artile->save();
    $article->tags()->save($request->tags);
    // データ操作を確定させる
    DB::commit();
} catch(Exception $exception) {
    // データ操作を巻き戻す
    DB::rollBack();
    throw $exception;
}

この記述方法はメソッド名で何をしているか判別しやすい反面、毎回同じような catch 句を書かなければいけないぶん冗長です。

もうひとつは transaction メソッドを用いた記述方法です。

DB::transaction(function () use ($request) {
    $article = new Article();
    $article->body = $request->body;
    $artile->save();
    $article->tags()->save($request->tags);
});

引数に「成功したらコミットして失敗したらロールバックしたい処理」を無名関数として渡します。冗長な記述が削られスッキリしましたね。

transactionメソッドから値を返す

さらに transaction メソッドは値を返すこともできます。

Laravel の作者 Taylor Otwell さんのツイートで知りました。

このコードどうやったらもっと綺麗になるかな?
「&」でリファレンス渡ししてるのが美しくないと思うんだけど。

(作者降臨)transaction メソッドから値を返せるよ。

つまり例えばこの記述は....

$article = null;

DB::transaction(function () use (&$article, $request) {
    $article = new Article();
    $article->title = $request->title;
    $article->body = $request->body;
    $article->save();

    $article->tags()->save($request->tags);
});

return view('articles.show', compact('article'));

このように書き換えることができます。

$article = DB::transaction(function () use ($request) {
    $article = new Article();
    $article->title = $request->title;
    $article->body = $request->body;
    $article->save();

    $article->tags()->save($request->tags);

    return $article;
});

return view('articles.show', compact('article'));

引数の無名関数の戻り値が transaction メソッドの戻り値になるんですね。
確かにこっちの方がちょっと美しい。

以上、Laravel の transaction メソッドについての小ネタでした。