You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel的Rule::unique()中使用whereHas实现多列唯一验证

在Laravel的Rule::unique()中实现多对多关联的唯一验证

不能直接在Rule::unique()里使用whereHas方法——因为whereHas是Eloquent模型专属的关联查询方法,而Rule::unique()底层操作的是原生数据库查询构建器,无法直接调用Eloquent的关联方法。但可以通过两种方式实现你需要的验证逻辑:

方法一:在Rule::unique()中用whereExists模拟关联查询

利用whereExists子查询模拟多对多关联的匹配逻辑,直接在Rule::unique()的闭包中构建查询条件:

use Illuminate\Validation\Rule;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;

public function rules()
{
    // 获取提交的关联牛只ID数组
    $cowIds = $this->input('cow_ids');
    // 如果是更新场景,排除当前记录ID
    $ignoreId = $this->route('vaccination')?->id;

    return [
        'vaccination_date' => 'required|date',
        'next_vaccination_date' => 'required|date',
        'vaccine_type' => 'required|string',
        'dose' => 'required|numeric',
        'cow_ids' => 'required|array|exists:cows,id',
        // 自定义唯一验证规则
        'vaccination_date' => Rule::unique('vaccinations')
            ->ignore($ignoreId)
            ->where(function (Builder $query) use ($cowIds) {
                // 匹配字段条件
                $query->where('next_vaccination_date', $this->input('next_vaccination_date'))
                      ->where('vaccine_type', $this->input('vaccine_type'))
                      ->where('dose', $this->input('dose'))
                      // 用whereExists模拟多对多关联查询
                      ->whereExists(function (Builder $subquery) use ($cowIds) {
                          $subquery->select(DB::raw(1))
                                  ->from('cow_vaccination') // 多对多中间表
                                  ->whereColumn('cow_vaccination.vaccination_id', 'vaccinations.id')
                                  ->whereIn('cow_vaccination.cow_id', $cowIds);
                      });
            })
    ];
}

方法二:自定义验证规则(更易维护)

创建独立的验证规则类,直接使用Eloquent的whereHas方法实现关联查询,代码可读性更高:

1. 生成规则类

php artisan make:rule UniqueVaccinationForCows

2. 实现规则逻辑

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use App\Models\Vaccination;

class UniqueVaccinationForCows implements Rule
{
    protected $cowIds;
    protected $ignoreId;

    // 接收关联牛只ID和需要排除的记录ID(更新场景用)
    public function __construct($cowIds, $ignoreId = null)
    {
        $this->cowIds = $cowIds;
        $this->ignoreId = $ignoreId;
    }

    public function passes($attribute, $value)
    {
        $query = Vaccination::where('vaccination_date', request()->input('vaccination_date'))
            ->where('next_vaccination_date', request()->input('next_vaccination_date'))
            ->where('vaccine_type', request()->input('vaccine_type'))
            ->where('dose', request()->input('dose'))
            // 直接使用whereHas匹配多对多关联
            ->whereHas('cows', function ($query) {
                $query->whereIn('id', $this->cowIds);
            });

        // 更新场景排除当前记录
        if ($this->ignoreId) {
            $query->where('id', '!=', $this->ignoreId);
        }

        // 无匹配记录则验证通过
        return $query->doesntExist();
    }

    public function message()
    {
        return '该疫苗记录已存在于指定牛只中。';
    }
}

3. 在表单请求中使用规则

use App\Rules\UniqueVaccinationForCows;

public function rules()
{
    $cowIds = $this->input('cow_ids');
    $ignoreId = $this->route('vaccination')?->id;

    return [
        'vaccination_date' => ['required', 'date', new UniqueVaccinationForCows($cowIds, $ignoreId)],
        'next_vaccination_date' => 'required|date',
        'vaccine_type' => 'required|string',
        'dose' => 'required|numeric',
        'cow_ids' => 'required|array|exists:cows,id',
    ];
}

内容的提问来源于stack exchange,提问作者Shahadat Shuvo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:13:15