本文介绍如何使用 laravel spatie query builder 实现基于关联模型(如 `examinations`)**最新一条记录**(而非任意记录)的精准过滤,解决 `wherehas` 无法限制“最后一条”的常见误区。
在使用 Spatie Query Builder 进行关系过滤时,一个典型误区是误以为 whereHas(...->orderBy()->limit(1)) 能筛选出“拥有最新检查且患病”的动物——但实际上,whereHas 中的 orderBy 和 limit 在子查询中被忽略(Eloquent 不支持在 whereHas 子查询中使用排序与分页),导致它仍会匹配任意一条满足条件的检查记录,而非严格意义上的“最后一次”。
要真正实现“仅当最新一次检查的 disease_id 非空时才视为生病”,需采用两阶段查询策略:先定位每只动物的最新检查记录 ID,再基于这些 ID 精确过滤。
以下是推荐的、高效且可读性强的实现方式(已适配 Laravel 9+ 及 Spatie Query Builder v5+):
select('animals.*'); // 显式选择主表字段,避免后续 pluck 异常
$animalIdsWithLatestExam = $query
->withMax('examinations', 'id')
->get()
->filter(fn ($animal) => $animal->examinations_max_id !== null)
->pluck('examinations_max_id');
// Step 2: 查询这些最新 examination ID 对应的记录,并筛选 disease_id
!= null
$sickAnimalIds = Examination::query()
->whereIn('id', $animalIdsWithLatestExam)
->whereNotNull('disease_id')
->pluck('animal_id');
// Step 3: 主查询仅保留符合条件的 animal.id
$query->whereIn('id', $sickAnimalIds);
}
}✅ 关键说明:
⚠️ 注意事项:
$animals = QueryBuilder::for(Animal::class)
->allowedFilters(Filter::custom('sick', SickAnimalsFilter::class))
->get();通过该方案,你将精准获得“当前生病”的动物列表——即其最后一次检查明确关联了疾病,彻底规避历史病历造成的误判。