
本文介绍在 laravel 中遍历对象数组时,如何避免因使用 `next()` 导致的“trying to get property of non-object”错误,并提供健壮、可读性强的分组拆分方案。
在 Laravel(或纯 PHP)开发中,常需对查询返回的对象集合(如 Eloquent Collection 或原生对象数组)按某个属性(如 cnpj)进行逻辑分组或拆分。一个典型场景是:当相邻对象的 cnpj 值发生变化时,将原数组切分为两个子数组(例如 $aiuaEd 和 $aiua)。初学者容易尝试用 foreach 配合 PHP 内置指针函数 next() 获取“下一个元素”,但这种做法存在严重隐患——next() 会移动内部数组指针,且在循环末尾或单元素数组中必然返回 false 或 null,导致 $next->cnpj 触发致命错误。
你遇到的 Trying to get property 'cnpj' of non-object 正是此问题的直接体现:当 $finances 仅含 2 个元素时,foreach 迭代到第 2 个元素后调用 next() 已越界,$next 为 false,后续访问其属性即报错。
更安全、更符合 Laravel 习惯的解决方案是放弃手动指针操作,转而采用基于键值映射的声明式分组。以下是推荐实现(兼容原生数组与 Laravel Collections):
// ✅ 推荐:使用 Laravel Collection 的 groupBy 方法(最简洁、最语义化)
$grouped = collect($finances)->groupBy('cnpj');
// 转为数值索引数组,确保顺序可控(按首次出现顺序)
$groups = $grouped->values()->toArray();
// 根据业务需求拆分:取前两组(若存在),其余可忽略或合并
$aiuaEd = $groups[0] ?? [];
$aiua = $groups[1] ?? [];
// 若严格要求“仅两类 CNPJ”,可加校验:
if ($grouped->count() > 2) {
throw new InvalidArgumentException('Unexpected: more than 2 distinct CNPJs found.');
}若必须使用原生 PHP 数组(如 $finances 是 array
// ✅ 安全的手动分组(无指针风险,O(n) 时间复杂度)
$cnpjGroups = [];
foreach ($finances as $finance) {
$cnpj = $finance->cnpj;
if (!isset($cnpjGroups[$cnpj])) {
$cnpjGroups[$cnpj] = [];
}
$cnpjGroups[$cnpj][] = $finance;
}
// 提取前两个不同 CNPJ 对应的数组(保持插入顺序)
$keys = array_keys($cnpjGroups);
$aiuaEd = $cnpjGroups[$keys[0]] ?? [];
$aiua = $cnpjGroups[$keys[1]] ?? [];⚠️ 关键注意事项:
- 永远不要在 foreach 中混用 next()/prev() 等指针函数:PHP 的 foreach 使用副本机制,内部指针状态不可预测,极易引发竞态错误;
- 优先使用 Laravel Collection 方法:groupBy()、partition()、split() 等方法经过充分测试,自动处理空值、类型安全及性能优化;
- 明确业务约束:你的原始方案依赖“最多 2 个不同 CNPJ”,代码中应显式校验并抛出异常,而非静默截断,提升可维护性;
- 避免重复遍历:你提供的 array_unique 方案需两次遍历(一次提 CNPJ,一次分组),而 groupBy 或哈希表分组均为单次遍历,效率更高。
总结:解决此类问题的核心思路是从“过程式指针操作”转向“声明式数据分组”。利用 Laravel 强大的集合工具链,不仅能写出更短、更清晰的代码,更能从根本上规避运行时错误,让逻辑意图一目了然。










