2015.12.12
0からはじめるPHP#61【セキュリティを学ぶ#2-トランザクション&404-】
今回のテーマは
トランザクションです。
トランザクションっていうのは、まぁ簡単にいえば
データベースの一貫性を守るものです。
よく例に出されるのは、銀行口座です。
AさんがBさんに1万円送金したとしましょう。そこでデータベースはAさんの口座から1万円をマイナスし、Bさんの口座に1万円加えるわけですね。
ただ、そこで不慮の事故・・・・・・システムトラブルとかが発生したとしましょう。Aさんの口座から1万円マイナスするのに成功したが、その直後システムが停止しBさんの口座に1万円加える操作が遂行できなかった、と。
つまり結果として
Aさんの1万円は闇に消えたなんてことになります。
そんなことがあったら大騒動ですよね。(笑)
コンマ1秒に足らないタイミングでしか起こりえないことですが、コンピューターは1度に1つのことしか実行できないので可能性がないとは言い切れません。あるいは処理が重すぎてコンピューター側がパンクしちゃった、なんてこともありうるかも知れませんね。
こういったトラブルに対応するための仕組みが
トランザクションです。
具体的には、
一連の動作がうまく行かなかったら最初の状態に巻き戻す(ロールバック)ということをします。
データベースで扱う内容にも依るんですが、可能であればこのトランザクションの仕組みは用意しておくと、不用意なバグが防げます。壊れたデータが1つ混ざってるだけでシステムが停止することはよくありますしね。ちなみに僕の開発した掲示板も、腐ったデータが混入するとおそらく機能停止します。(笑)
さて、トランザクションはLaravelでどのように実装するかってことですね。
やはり簡単です。
DB::transaction(function () use($inputs){
DB::table(YOUR_TABLE)->insert($inputs);
});
この
DB::transactionのクロージャに渡すだけで、トランザクションを勝手にやってくれるそうです。
やってくれるそうです、としたのは
テスト方法がよく分からないので、テストしようがなかったんですね。sleepさせてその間にサーバーを止めて、ってやったんですが
ただのネットワークエラーになってしまったんで、ちょっとどうしようもないです。まぁ上手く行くと仮定して進める他ありません。
ただ、エラーを検出するのなら、エラー表示もしたいところですよね。
頑張って調べてみたんですが
Laravel: Using try…catch with DB::transaction() (Stack Overflow)というエントリ?いわく、この方法だとトランザクションのエラー分岐はできない模様。
じゃあどうするのかっていうと、手動でやればいいんですね。
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
引用元:Laravel: Using try…catch with DB::transaction() (Stack Overflow)
こうすれば良いみたいです。この
DB::beginTransaction();というのを宣言(というか実行)しておけば、コミットするタイミングを自由にできるみたいですね。
エラーメッセージを表示させたい時は、catchしたところでいろいろいじくれば良さそうです。
これで終わるのはもったいないので、皆さんおなじみの
404(File not found)を表示してみましょう。
マニュアルには
エラーとログという項目の
HTTP例外として載せられています。
abort(404);//404エラーを強制的に発生させる
abort(403, '許可されていないアクション');//説明を付加させることができる
//401,500などのエラーも同様に発生させられる
つまり
どっからでも例外を呼び出せるってことですね。
このabortを実行すると、それに応じたエラーページが生成されることになります。
では、そのエラーページを自前で作るにはどうしたら良いか。
簡単です。
resources/views/errors/404.blade.phpを作れば良いらしいです。
ちなみに403エラーなら403.blade.phpを作らなくてはいけません。
せっかくなので作ってみましょう。
簡単ですね。
今回作る掲示板では、こんな個別のエラーページは
めんどくさいので作らないつもりですが、次に作る作品からはこの辺りもきちんと力を入れて作らないとな、と思う次第です。