You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:17:39