You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动