
本文介绍在 laravel 中遍历对象数组时,如何避免因使用 `next()` 导致的“trying to get property of non-object”错误,并提供更健壮、可读性更强的拆分逻辑(如按唯一 cnpj 分组为两个数组)。
在 Laravel(或纯 PHP)中处理对象数组时,若需基于当前项与下一项的比较进行逻辑分支(例如按 cnpj 是否变化来拆分数组),直接使用 next() 是危险且不可靠的——因为当循环到达末尾时,next() 返回 false 或 null,而代码仍尝试访问 $next->cnpj,从而触发 “Trying to get property 'cnpj' of non-object” 错误。该问题在数组长度为 2 时尤为明显:第一次迭代后 next() 指向第二项,第二次迭代后 next() 已越界,返回非对象值。
更合理、更符合 Laravel 风格的解决方案是分离「识别分组依据」与「分配数据」两个阶段,避免在遍历中依赖不稳定的内部指针。以下是推荐实现:
✅ 推荐方案:两阶段分组(清晰、安全、可扩展)
// 第一阶段:提取所有唯一 CNPJ 并排序(确保顺序确定)
$cnpjs = array_unique(array_column($finances, 'cnpj'));
sort($cnpjs);
// 初始化结果数组
$aiuaEd = []; // 默认组(匹配第一个 CNPJ)
$aiua = []; // 可选组(匹配第二个 CNPJ,仅当存在多个时)
// 第二阶段:按 CNPJ 归类(安全、无边界风险)
foreach ($finances as $finance) {
if (count($cnpjs) > 1 && $finance->cnpj === $cnpjs[1]) {
$aiua[] = $finance;
} else {
$aiuaEd[] = $finance; // 包含所有首个 CNPJ 及唯一 CNPJ 的情况
}
}? 关键改进说明:使用 array_column($finances, 'cnpj') 替代手动 foreach 提取,更简洁高效;array_unique() + sort() 确保 $cnpjs 是有序的数值索引数组(如 [0 => '111', 1 => '222']),避免键名混乱;完全规避 next(),杜绝空对象访问风险;逻辑明确:仅当存在至少两个不同 CNPJ 时才启用双数组逻辑,否则全部归入 $aiuaEd。
⚠️ 注意事项与进阶建议
-
适用前提:此方案假设最多仅存在 2 个不同 CNPJ(如题所述)。若未来需支持 N 组拆分,可升级为关联数组分组:
$grouped = collect($finances)->groupBy('cnpj')->map->all()->all(); // $grouped['111.xxx'] → 所有该 CNPJ 对象 性能考量:两次遍历(提取 + 分配)时间复杂度仍为 O(n),远优于潜在的运行时异常开销;对于数千条以内数据,性能差异可忽略。
-
Laravel 集合增强写法(推荐):
$cnpjs = collect($finances)->pluck('cnpj')->unique()->sort()->values()->all(); [$aiuaEd, $aiua] = collect($finances)->partition(fn($f) => count($cnpjs) > 1 ? $f->cnpj === $cnpjs[1] : false )->map->all()->all(); -
健壮性补充:如数据源不可控,建议增加属性存在性检查:
if (!isset($finance->cnpj)) { throw new InvalidArgumentException("Missing 'cnpj' in finance object"); }
综上,放弃对内部数组指针(next()/prev())的依赖,转而采用声明式分组策略,不仅彻底解决空对象错误,还提升了代码可读性、可维护性与 Laravel 生态兼容性。










