Laravel表单输入验证:忽略软删除项实现唯一验证
嘿,这个问题我刚好在项目里处理过好几次,Laravel中要实现「唯一验证时忽略软删除记录」其实有几种挺直观的方案,我给你一步步拆解清楚:
方法一:利用
unique规则的额外条件参数 Laravel自带的unique验证规则支持追加额外的查询条件,我们只需要加上deleted_at is null的限制,就能排除已软删除的记录。
创建场景(无需排除当前记录)
比如在表单请求类的rules()方法里:
public function rules() { return [ 'email' => 'required|email|unique:users,email,NULL,id,deleted_at,NULL', ]; }
这里的参数顺序可以记一下:unique:表名,字段名,排除ID,排除字段名,额外条件字段,额外条件值。我们用deleted_at,NULL来指定只校验未被软删除的记录。
更新场景(需要排除当前记录)
如果是更新操作,要忽略当前编辑的记录,同时排除软删除项,只需要把第三个参数换成当前记录的ID:
public function rules() { // 从路由参数获取当前要更新的用户ID $userId = $this->route('user')->id; return [ 'email' => "required|email|unique:users,email,{$userId},id,deleted_at,NULL", ]; }
方法二:用闭包写自定义验证逻辑(更直观)
如果觉得上面的参数顺序容易记混,用闭包写逻辑会更清晰,也方便后续扩展其他条件:
public function rules() { return [ 'email' => [ 'required', 'email', function ($attribute, $value, $fail) { // 构建查询:找邮箱匹配且未被软删除的记录 $query = \App\Models\User::where('email', $value)->whereNull('deleted_at'); // 如果是更新操作,排除当前记录 if ($this->route('user')) { $query->where('id', '!=', $this->route('user')->id); } // 如果存在匹配记录,抛出错误 if ($query->exists()) { $fail('该邮箱已被其他未删除的账号使用'); } }, ], ]; }
方法三:自定义可复用的验证规则
如果多个地方都需要这个验证逻辑,自定义一个规则类会更高效,避免重复代码:
1. 生成规则类
执行Artisan命令创建规则:
php artisan make:rule UniqueWithoutSoftDeleted
2. 实现规则逻辑
在生成的app/Rules/UniqueWithoutSoftDeleted.php里编写逻辑:
namespace App\Rules; use Illuminate\Contracts\Validation\Rule; use Illuminate\Database\Eloquent\Model; class UniqueWithoutSoftDeleted implements Rule { protected $model; protected $ignoreColumn; protected $ignoreValue; // 构造函数接收模型类、忽略字段、忽略值 public function __construct(string $model, string $ignoreColumn = 'id', $ignoreValue = null) { $this->model = $model; $this->ignoreColumn = $ignoreColumn; $this->ignoreValue = $ignoreValue; } // 验证逻辑 public function passes($attribute, $value) { $query = app($this->model)->where($attribute, $value)->whereNull('deleted_at'); // 如果需要忽略特定记录,追加条件 if ($this->ignoreValue) { $query->where($this->ignoreColumn, '!=', $this->ignoreValue); } return !$query->exists(); } // 错误提示 public function message() { return 'The :attribute has already been taken by an active record.'; } }
3. 使用自定义规则
在验证规则里直接实例化这个类即可:
// 创建场景 'email' => ['required', 'email', new \App\Rules\UniqueWithoutSoftDeleted(\App\Models\User::class)], // 更新场景 'email' => [ 'required', 'email', new \App\Rules\UniqueWithoutSoftDeleted(\App\Models\User::class, 'id', $userId) ],
⚠️ 注意:以上所有方法生效的前提是,你的模型已经引入了SoftDeletes trait:
use Illuminate\Database\Eloquent\SoftDeletes; class User extends Model { use SoftDeletes; }
内容的提问来源于stack exchange,提问作者leaveme_alone




