ThinkPHP中find()返回null、select()返回空数组[],判空须用is_null()或empty(),禁用if($data)等真假值判断,否则会因0、''等合法值误判;关联字段和聚合结果也需手动判空。

ThinkPHP新手常在控制器里写if ($user->find())或foreach ($list as $item)后报错,根本原因是没搞清find()返回null、select()返回空数组[],而PHP对null和[]的判断逻辑完全不同——直接用真假值判断会漏掉0、空字符串等合法数据,用count()又会在null上致命报错。
查单条时find()返回null,别用if($data)判空
第一步:确认你调用的是find()而非select()。模型主键查询如UserModel::find(123)、带条件的UserModel::where('email', $email)->find()都走这条路径。
第二步:用is_null()或严格比较=== null判断结果是否存在。例如:$user = UserModel::find(999); if (is_null($user)) { // 处理不存在 }。
第三步:绝对不要写if ($user->find())或if ($user)——因为find()成功时返回对象,但对象属性含status => 0或name => ''时,整个表达式会被PHP当成false,误判为“查不到”。【这是新手最高频的静默逻辑错误】
立即学习“PHP免费学习笔记(深入)”;
第四步:需要数组格式时,先判空再转:$user = UserModel::find(123); return $user ? $user->toArray() : null;。切记不能连写UserModel::find(123)->toArray(),null->toArray()会直接抛出Fatal Error。
查列表时select()返回空数组,foreach可用但需防假空
方法一:直接用empty()判断。例如:$users = UserModel::where('status', 1)->select(); if (empty($users)) { // 无启用用户 }。这比count($users) === 0更简洁,且对空数组[]和非空数组都安全。
方法二:用is_array()兜底防错。当不确定上游是否真的返回了Collection或数组时(比如表名拼错、中间件拦截),加一层类型校验:if (is_array($users) && !empty($users)) { foreach ($users as $u) { ... } }。否则foreach(false as ...)会触发Warning。
注意:select()返回的是think\model\Collection对象,不是原生数组,但PHP允许直接foreach它。若需原生数组,必须显式调用->toArray()。
聚合函数sum/avg返回NULL,别当0用
MySQL聚合函数天然对空结果集返回NULL,ThinkPHP不做转换。写$total = OrderModel::where('status', 2)->sum('price');时,若无订单,$total就是null,不是0。
方法一:数据库层补零,用Db::raw()封装IFNULL。例如:Db::table('order')->where('status', 2)->sum(Db::raw('IFNULL(sum(price), 0)'));。这确保返回值恒为数字,避免PHP层反复判断。
方法二:业务层显式处理,用空合并操作符??。例如:$total = OrderModel::sum('price') ?? 0;。但要注意:??只判null和undefined,不处理0或'',语义清晰。
【聚合结果为NULL时,intval($total)会得0,但$total + 10却得10——两种行为矛盾,暴露了未明确定义空值语义】
关联查询字段为空,手动判空不能省
模型关联如hasOne、belongsTo查出来的是嵌套结构,外键匹配失败时,关联模型字段是null,但主模型本身可能有数据。例如:$order = OrderModel::with('user')->find(100);,若该订单user_id指向不存在的用户,则$order->user为null。
必须单独检查关联字段:if ($order && $order->user) { echo $order->user->name; }。不能假设$order存在就代表$order->user也存在。
更稳妥写法是用访问器统一兜底:public function getUserAttr($value) { return $value ?? new User(); },让$order->user永远不是null,而是空对象。
用failException强制异常,要配try/catch
当你调用findOrFail()或手动设置failException(true)时,查不到数据会直接抛出think\db\exception\DataNotFoundException,而不是返回null。
第一步:确认场景是否适合中断流程。API接口返回404时用它很合适;但后台批量处理中某条记录缺失,用它会导致整个任务崩溃。
第二步:必须包裹在try/catch里,否则异常未捕获会终止脚本。例如:try { $user = UserModel::findOrFail($id); } catch (DataNotFoundException $e) { return json(['code'=>404, 'msg'=>'用户不存在']); }。
第三步:别在全局配置里盲目开启failException。ThinkPHP默认静默返回,就是为了兼容各种降级逻辑——强行改默认值会让老代码集体失效。



















