Laravelで二重登録を阻止する3つの方法

ご挨拶

タンバリンの若林と申します。

データをPOSTする際、何らかの不具合でデータベースに同じデータが複数登録されてしまう…そんな過ちを最近犯してしまったので、この記事で懺悔します。

前提

  • 言語:PHP
  • FW:Laravelの6系
  • lib:jQuery
  • 次のプロセスを想定:入力画面 → 確認画面 → 完了画面

1. 送信ボタンの連続クリックを阻止

確認画面 → 完了画面 の順に遷移する際、送信ボタンを複数回クリックすると、その回数分データが送信されてしまいます。

これは確認画面に下記の処理を書くことで阻止できます。 formからデータを送信時、送信ボタンを非活性にしてるんですね。

【View】

<script>
    $(function () {
        $('form').submit(function () {
            $(this).find(':submit').prop('disabled', 'true');
        });
    });
</script>

2. ブラウザバックによる再度送信を阻止

完了画面 → 確認画面 の順にブラウザバック後、 確認画面 → 完了画面 の順に遷移する際、再度送信ボタンをクリックすると、また同じデータが送信されてしまいます。

これは完了画面へ遷移する際のメソッドに下記の処理を書くことで阻止できます。 送信後にCSRFトークンを再生成してるんですね。 これでブラウザバック後に再度送信しても元々のトークンと値が違うため、419のエラーが出てきて送信ができなくなります。

【Controller】

$hoge->createUser($request) // テキトーに登録系の処理
$request->session()->regenerateToken(); // これを書く。トークンを再生成
return view('complete');

3. 完了画面のリロードによる再度送信を阻止

確認画面 → 完了画面 の順に遷移後、完了画面のまま画面をリロードすると、また同じデータが送信されてしまいます。ブラウザのリロードは直前のリクエストを再度実行することができるので、直前に登録系の処理があった場合は二重登録の原因になります。

これは完了画面へ遷移する際に別のメソッドを挟む下記の処理を書くことで阻止できます。 登録系の処理完了画面を見せる処理 の2つに分けてるんですね。 これで完了画面をいくらリロードしようが、直前のリクエストは画面を見せるだけなので問題ありません。

【Controller】

public function register(Request $request)
{
    $hoge->createUser($request) // テキトーに登録系の処理
    return redirect()->route('complete'); // web.phpでルーティングを定義しておく
}

public function complete()
{
    return view('workshop.complete'); // 完了画面を見せるだけの処理
}

以上になります。 いかがでしたでしょうか。割と簡単にできる再現性の高い内容かと思います。 皆様の開発業務に貢献できていましたら幸いです。

参考文献

https://qiita.com/syobochim/items/120109315f671918f28d https://www.bnote.net/blog/laravel_double_submit.html