Laravel の unique ルールとソフトデリート
先月中旬から東京に出張行ってるんですが、通勤のストレス*1から部屋(くっそ狭いマンスリーマンション)で勉強する気力が沸かなかったのですが、1ヶ月弱経ちまして少しは回復したのでまずはブログでリハビリ。
フォーラムに投稿された質問
メールアドレスを他の人と被らないよう、バリデーションを「users,mail_address」としました。
[解決済み] バリデーションで「unique」を指定した際のソフトデリートとの連携について - laravel.jp
usersテーブル自体はソフトデリートを有効にしています。 この場合、削除したユーザのメールアドレスと、新しく登録するユーザのメールアドレスが同じ場合、 バリデーションに引っ掛かって登録することができません。
どうにかソフトデリートとユニークなメールアドレスを両立させる方法はないでしょうか。
Laravel の Validator からはモデルがソフトデリート(論理削除を簡単に実装できる機能)を使ってるかどうかわからないので unique
ルールを適用したら削除したモデルがひっかかって辛いという話です。
再現するとこんな感じです。
<?php // マイグレーションファイル(抜粋) <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class CreateUsersTable extends Migration { public function up() { Schema::create('users', function(Blueprint $table) { $table->increments('id'); $table->string('email'); $table->softDeletes(); $table->timestamps(); }); } } // シードファイル(抜粋) class UserTableSeeder extends Seeder { public function run() { // Model の $unguarded を true にすると楽 // これ豆知識な User::unguard(); User::create([ 'email' => 'test@example.com' ]); } } Route::get('unique', function() { User::destory(1); // 削除した! $email = 'test@example.com'; $v = Validator::make(['email' => $email], ['email' => 'unique:users']); // 削除したけどバリデーションはエラー! var_dump($v->fails()); });
ソフトデリートの場合、削除されるとカラムの deleted_at
に日付を入れるだけなので unique
で発行される SQL ではダメなわけです。ちなみにこんなSQLが発行されます。
select count(*) as aggregate from "users" where "email" = ?
上記 SQL の結果が 0 の場合 unique
バリデーションは通過します。
解決方法
クエリーへWHERE節として追加される条件を追加することも可能です。
<?php 'email' => 'unique:users,email_address,NULL,id,account_id,1'v4.2:バリデーション
なのでこんな感じで書いて上げればOK.
<?php Validator::make(['email' => $email], ['email' => 'unique:users,email,NULL,id,deleted_at,NULL']);
こうするとこんなSQLが発行されます。
select count(*) as aggregate from "users" where "email" = ? and "deleted_at" is null
where 節が付加されているのがわかりますね。
*1:なんか抜け毛が増えた気がする