Laravel9+Jenssegers MongoDB3.9使用withCount报Illegal offset type错误
问题场景
在Laravel 9搭配Jenssegers/MongoDB 3.9环境中,执行以下查询代码时触发Illegal offset type错误,报错堆栈指向Jenssegers\Mongodb\Query\Builder.php第373行:
$subscription = Subscription ::getById($subscriptionId) ->withCount('userSubscriptions') ->firstOrFail();
调试确认:$this->columns数组中包含Illuminate\Database\Query\Expression对象,框架遍历该数组转换为MongoDB投影时,直接将对象作为数组键引发类型错误。
此外,尝试添加->select('*','user_subscriptions_count')后无运行时错误,但结果中未出现统计字段,调试发现$columns仅保留了*。
原因分析
Jenssegers/MongoDB 3.9版本的withCount实现存在兼容性缺陷:当查询列中包含Expression类型对象时,转换MongoDB投影的逻辑未做类型处理,直接将对象作为数组索引,触发PHP的非法偏移类型错误。
解决方案
方案1:手动实现关联统计(绕过withCount)
通过原生MongoDB聚合或关联查询手动计算统计值,避免依赖有问题的withCount方法:
方式A:使用关联查询+统计
$subscription = Subscription::getById($subscriptionId) ->leftJoin('user_subscriptions', 'subscriptions._id', '=', 'user_subscriptions.subscription_id') ->select('subscriptions.*', DB::raw('count(user_subscriptions._id) as user_subscriptions_count')) ->groupBy('subscriptions._id') ->firstOrFail();
方式B:使用MongoDB原生聚合管道
$subscription = Subscription::raw(function($collection) use ($subscriptionId) { return $collection->aggregate([ ['$match' => ['_id' => new MongoDB\BSON\ObjectId($subscriptionId)]], [ '$lookup' => [ 'from' => 'user_subscriptions', 'localField' => '_id', 'foreignField' => 'subscription_id', 'as' => 'user_subscriptions' ] ], [ '$addFields' => [ 'user_subscriptions_count' => ['$size' => '$user_subscriptions'] ] ], ['$limit' => 1] ])->toArray()[0]; });
注:若模型主键不是ObjectId类型,需调整$match条件中的主键格式。
方案2:临时修复Jenssegers/MongoDB源码
修改Jenssegers\Mongodb\Query\Builder.php中处理列投影的逻辑,添加对Expression对象的解析:
找到遍历$this->columns转换投影的代码段(对应报错的第373行附近),替换为以下逻辑:
foreach ($this->columns as $key => $column) { // 处理Expression类型的列 if ($column instanceof \Illuminate\Database\Query\Expression) { $column = $column->getValue($this->getConnection()); } // 区分索引类型,正确设置投影 if (is_numeric($key)) { $projection[$column] = 1; } else { $projection[$key] = $column; } }
⚠️ 该方案仅作为临时修复,不建议长期使用,后续需跟进版本升级。
方案3:升级Jenssegers/MongoDB版本
查看Jenssegers/MongoDB官方仓库的issue和更新日志,确认是否有后续版本修复了withCount的兼容性问题。例如升级到4.x版本(需提前确认版本与Laravel 9的兼容性)。
验证
执行修复后的查询,确认user_subscriptions_count字段能正确返回关联数据的统计数量,且无类型错误抛出。
内容的提问来源于stack exchange,提问作者mstdmstd

