論理削除済み&自分自身を除外するuniqueバリデーション

..

メールアドレスがログインID代わりに使用される場合などは、users内のemailカラムにユニーク制約を付加する必要があります。

加えて登録・更新時のバリデーションにおいても重複する値を弾く必要があるわけですが、Userモデルが論理削除に設定されている場合は、論理削除されたレコードの値は除外するようにルール側で対応する必要があります。

論理削除済みのレコードを除外する

新規ユーザー登録のバリデーションはこう。

'email' => 'required|unique:users,email,,,deleted_at,NULL|max:255|email:rfc,dns',

論理削除に設定する場合は、基本的にdeleted_atカラムがNULLであることや、existカラムに1が入っていることで存在していることを判断することになります。

create_users_table.php


$table->boolean('exist')->nullable()->storedAs('case when deleted_at is null then 1 else null end');


$table->unique(['email', 'exist']);

ルールにもその条件を書くことで、バリデーション時に「存在しているユーザーのカラム」だけをチェックするようになります。

uniqueルールのパラメータは以下のような構成となっています。

  • テーブル名(必須)
  • カラム名(必須)
  • 除外する値
  • 除外するカラム
  • 追加でチェックするカラム(例ではdeleted_atカラム)
  • 値の条件(例ではNULL

新規登録時は3,4番目を空欄にし、追加条件の部分で「deleted_atNULLであること」を指定しています。

同一ユーザーのレコードを除外する

ユーザー情報更新画面があったとして、email以外の部分を更新しようとした場合。
そのままだと自分自身のemailと重複していると見做され、バリデーションエラーとなって更新できません。

ログイン後にアクセスするそうした画面においては、Authファサード等でユーザーIDを取得し、それを除外します。

UpdateRequest.php
use Illuminate\Support\Facades\Auth;



    $id = Auth::id();
    return [
        'email' => 'required|unique:users,email,'. $id .',id,deleted_at,NULL|max:255|email',

user/3/editなどのURLであれば、ルートでID部分を変数化し、Requestクラス内で受け取ることも可能です。

web.php
Route::post('/user/{id}/edit', [UserController::class, 'update']);
UpdateRequest.php
use Illuminate\Support\Facades\Auth;



    return [
        'email' => 'required|unique:users,email,'. $this->id .',id,deleted_at,NULL|max:255|email',

これによって、「更新対象ユーザーを除いたユーザー」かつ「有効なユーザー」のレコードとのみ比較するように設定できます。