Laravel 5.2适配PHP7.3:count()可数类型错误解决问询
问题:PHP7.3升级后Laravel5.2关联查询触发count()报错
场景还原
原运行环境为PHP7.1 + Laravel5.2,通过ORM的scope函数编写了如下查询逻辑:
public function scopeFreeTime($query, $month, $start_time, $end_time){ return $query->whereHas('shifts' ,function ($query) use ($month) { $query->where('month', $month); })->with(['shifts' => function ($query) use ($month) { $query->where('month', $month); }])->whereDoesntHave('leaves' ,function ($query) use ($start_time, $end_time) { $query->where('start_time', '<', $end_time); $query->where('end_time', '>', $start_time); })->whereDoesntHave('orders' ,function ($query) use ($start_time, $end_time) { $query->where('start_time', '<', $end_time); $query->where('end_time', '>', $start_time); $query->where('is_finished', false); }); }
升级PHP至7.3后,出现错误:count(): Parameter must be an array or an object that implements Countable,已知是PHP7.2及以上版本对count()参数的严格限制导致,需在不升级Laravel的前提下解决该问题。
问题原因
Laravel5.2的底层关联查询逻辑中,部分场景会返回null或非Countable类型的变量给count()函数,而PHP7.2+版本开始强制要求count()的参数必须是数组或实现Countable接口的对象,因此触发报错。
解决方案
1. 调整查询结果的计数方式
调用scope时,不要直接对查询构建器调用count(),先通过get()获取集合后再计数:
// 原错误写法 // $count = Model::freeTime($month, $start_time, $end_time)->count(); // 修正后 $records = Model::freeTime($month, $start_time, $end_time)->get(); $count = count($records);
2. 替换whereDoesntHave为手动子查询
绕过Laravel自带的whereDoesntHave可能触发的底层count()逻辑,改用whereNotExists手动编写关联子查询:
public function scopeFreeTime($query, $month, $start_time, $end_time){ return $query->whereHas('shifts' ,function ($query) use ($month) { $query->where('month', $month); })->with(['shifts' => function ($query) use ($month) { $query->where('month', $month); }])->whereNotExists(function ($query) use ($start_time, $end_time) { $query->select(\DB::raw(1)) ->from('leaves') ->whereRaw('leaves.user_id = users.id') // 替换为你的关联外键逻辑 ->where('start_time', '<', $end_time) ->where('end_time', '>', $start_time); })->whereNotExists(function ($query) use ($start_time, $end_time) { $query->select(\DB::raw(1)) ->from('orders') ->whereRaw('orders.user_id = users.id') // 替换为你的关联外键逻辑 ->where('start_time', '<', $end_time) ->where('end_time', '>', $start_time) ->where('is_finished', false); }); }
3. 全局兼容count()函数
在项目入口文件(如public/index.php)顶部添加兼容代码,对count()进行包装:
if (!function_exists('count')) { function count($value, $mode = COUNT_NORMAL) { // 对非可计数类型返回0或1,根据业务场景调整 if (!is_countable($value)) { return is_null($value) ? 0 : 1; } return \count($value, $mode); } }
注意:此方法会全局修改count()的行为,需测试确保不会影响其他业务逻辑。
4. 自定义Builder类替换框架默认实现
创建自定义Builder类继承Laravel原Builder,重写count方法添加兼容逻辑:
namespace App\Database\Eloquent; use Illuminate\Database\Eloquent\Builder as BaseBuilder; class Builder extends BaseBuilder { public function count($columns = '*') { $result = parent::count($columns); return is_countable($result) ? count($result) : 0; } }
然后在模型中指定使用该自定义Builder:
namespace App; use App\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class YourModel extends Model { public function newEloquentBuilder($query) { return new Builder($query); } }
此方法避免直接修改框架文件,同时针对性修复count()问题。
内容的提问来源于stack exchange,提问作者kevin steve
相关产品推荐
相关产品推荐

