如何在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
相关产品推荐
相关产品推荐

