Laravel Spatie 自定义过滤器:按关联模型最新记录筛选数据
技术百科
花韻仙語
发布时间:2025-12-27
浏览: 次 本文介绍如何使用 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);
}
}✅ 关键说明:
- withMax('examinations', 'id') 利用 Eloquent 的聚合关系,为每个 Animal 附加 examinations_max_id 字段(即其最新检查 ID),无需 N+1 查询,性能优秀;
- filter(...->examinations_max_id !== null) 排除从未做过检查的动物,避免空值干扰;
- whereNotNull('disease_id') 比 != null 更语义清晰且兼容 SQL 标准(尤其在 PostgreSQL 中更可靠);
- 整个流程逻辑清晰、可测试、易维护,不依赖原始 SQL,保持 Laravel 生态一致性。
⚠️ 注意事项:
- 确保 examinations 表的 animal_id 字段已建立索引(INDEX animal_id),否则 whereIn + 子查询可能影响性能;
- 若数据量极大(>100k 动物),建议将此逻辑移至数据库视图或使用原生 JOIN 优化(例如通过 ROW_NUMBER() OVER (PARTITION BY animal_id ORDER BY created_at DESC));
- 此过滤器默认启用,需在控制器中显式注册:
$animals = QueryBuilder::for(Animal::class) ->allowedFilters(Filter::custom('sick', SickAnimalsFilter::class)) ->get();
通过该方案,你将精准获得“当前生病”的动物列表——即其最后一次检查明确关联了疾病,彻底规避历史病历造成的误判。
# 做过
# 分页
# 符合条件
# 而非
# 你将
# app
# 不支持
# 时才
# 数据库
# NULL
# php
# sql
# postgresql
# 如何使用
# Filter
# laravel
# 将此
# 每只
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何在Golang中使用encoding/gob序
- Win11怎么关闭防火墙通知_屏蔽Win11安全中
- 如何使用Golang实现容器健康检查_监控和自动重
- 本地php环境出现502错误_nginx或apac
- Win11怎么设置默认PDF阅读器 Win11修改
- Python集合操作技巧_高效去重解析【教程】
- Win11怎么修复系统文件_使用sfc命令修复Wi
- Win11怎么关闭通知中心_Windows11系统
- Win11怎么开启远程桌面_Win11系统远程桌面
- Win11怎么恢复旧版开始菜单_通过软件还原Win
- 如何使用Golang读取日志文件_Golang b
- Win10怎样卸载自带Edge_Win10卸载Ed
- MAC如何设置网卡MAC地址克隆_MAC终端修改物
- Win11怎么关闭触摸屏_禁用Win11笔记本触摸
- Win11怎么关闭边缘滑动手势_Windows11
- Win10如何更改电脑休眠时间_Windows10
- c++怎么操作redis数据库_c++ hired
- Go 中 := 短变量声明的类型推导机制详解
- Python音视频处理高级项目教程_FFmpegP
- MAC怎么在照片中添加水印_MAC自带编辑工具文字
- php8.4如何实现队列任务_php8.4redi
- Windows10电脑怎么设置虚拟光驱_Win10
- Win11怎么调整屏幕亮度_Windows 11调
- Win11怎么清理C盘下载文件夹_Win11清理下
- Windows 10怎么隐藏特定更新补丁_Wind
- Win11怎么禁用键盘自带键盘_Win11笔记本禁
- php8.4新语法match怎么用_php8.4m
- 如何使用Golang构建基础消息队列模拟_Gola
- Python包结构设计_大型项目组织解析【指导】
- Python代码测试策略_质量保障解析【教程】
- 如何在 Go 中比较自定义的数组类型(如 [20]
- 如何更改Windows资源管理器的默认启动位置?(
- Win11怎么更改电脑名称_Windows 11修
- c++23 std::expected怎么用 c+
- 如何使用Golang log记录不同级别日志_Go
- Python迭代器生成器进阶教程_节省内存与懒加载
- Mac电脑如何恢复出厂设置_Mac抹掉数据并重装系
- Drupal 中 HTML 链接被重复转义导致渲染
- Win11输入法切换快捷键怎么改_Windows
- Win11怎么更改输入法顺序_Win11调整语言首
- Python函数缓存机制_lru_cache解析【
- Win10系统怎么查看网络连接状态_Windows
- c++怎么实现大文件的分块读写_c++ 文件指针s
- php怎么操作Redis_Redis扩展连接与基本
- 如何优化Golang内存分配与GC调度_Golan
- Win11怎么开启窗口对齐助手_Windows11
- Win11怎么关闭任务栏小组件_Windows11
- VSC怎么配置PHP的Xdebug_远程调试设置步
- Win11讲述人怎么关闭_Win11误触开启语音朗
- Win11怎么关闭触控板_Win11笔记本禁用触摸

!= null
$sickAnimalIds = Examination::query()
->whereIn('id', $animalIdsWithLatestExam)
->whereNotNull('disease_id')
->pluck('animal_id');
// Step 3: 主查询仅保留符合条件的 animal.id
$query->whereIn('id', $sickAnimalIds);
}
}
QQ客服