find()返回null,select()返回空数组;判空应统一用empty(),避免用===null;关联字段需手动判空,字符串和时间查询注意条件误差。

查询结果为空时 find() 和 select() 的返回值差异
ThinkPHP 模型方法对空结果的处理不是统一的:find() 查不到会返回 null,而 select() 返回空数组 []。这直接影响后续判空逻辑——用 empty($result) 能同时覆盖两者,但用 $result === null 就会漏掉 select() 的空数组情况。
常见错误是写成 if ($user->find() == null) 来判断失败,结果在关联查询或子查询里因返回对象(但属性全空)而误判为“有数据”。
- 单条查用
find(),优先检查是否为null,别用isset()或count() - 列表查用
select(),用empty($list)最稳妥,count($list) === 0也可但略冗余 - 如果统一想转成数组再处理,可加
->toArray(),但注意:空find()调用它会报错,得先判null
用 failException 控制异常还是静默返回
ThinkPHP 默认不抛异常,但模型方法支持 failException 参数强制触发异常。比如 UserModel::findOrFail($id) 内部就是设了 failException => true,查不到直接抛 think\exception\DbException。
这适合 API 场景:需要明确区分“资源不存在”和“其他错误”,方便前端统一拦截 404。但若在复杂业务流程中随意启用,可能让本该降级处理的空查询直接中断执行。
立即学习“PHP免费学习笔记(深入)”;
- 对外接口推荐用
findOrFail()/findOrEmpty(),语义清晰,避免手动判空 - 内部服务调用慎用
failException => true,尤其在循环或批量查询中,异常开销大且难捕获 -
findOrEmpty()是 6.0+ 新增方法,返回空数组而非null,适合想统一对待单条/列表空结果的场景
关联查询为空时 with() 的字段陷阱
用 with('profile') 查用户带资料时,即使用户存在、profile 不存在,profile 属性默认是 null,但如果你写了 ->visible(['profile.name']),序列化时会报 Trying to get property 'name' of non-object。
这不是模型层问题,而是访问阶段的空指针。ThinkPHP 不自动对关联结果做空值保护,得自己兜底。
- 模板里别直接写
{$user.profile.name},改用{$user.profile?.name}(PHP 8.0+)或{$user.profile['name'] ?? ''} - API 返回前建议统一处理:在模型的
append或getAttr里对关联字段加判空,例如public function getProfileAttr($value) { return $value ?: []; } - 用
hasWhere()预过滤比查完再过滤更高效,比如只查“有 profile 的用户”:UserModel::has('profile')->select()
where 条件写错导致“假空结果”
最常被忽略的是字符串比较中的空格和大小写。比如 where('status', 'active') 查不到,实际数据库里存的是 ' active ' 或 'ACTIVE';或者用了 whereTime() 但时区没对齐,查当天数据却返回空。
这类“查得到但查不到”的情况,比真为空更难调试,因为日志里 SQL 看着没问题,执行结果却是空。
- 查字符串字段,优先用
whereLike()或显式 trim:whereRaw('TRIM(status) = ?', ['active']) - 时间类查询务必确认数据库时区与 PHP 时区一致,
whereTime('create_time', 'today')在跨时区部署时容易失效 - 开启 SQL 日志(
show_sql => true)后,复制日志里的完整 SQL 到数据库客户端执行,看是否真为空——这是验证“是逻辑空还是条件错”的最快方式
空结果本身不复杂,麻烦在于它常和类型判断、关联访问、条件误差混在一起。越想“一劳永逸”地封装一个通用判空函数,越容易在某个边界场景翻车。盯住具体方法的返回契约,比依赖全局配置更可靠。



















