如何用Laravel从同表获取含子行的数据并整合到同一集合
问题描述
我有一张包含父行和子行的表,需要使用Laravel将数据获取到同一集合中,即把子行数据嵌套在对应的父行内。表结构如下:
| id | type | price | is_substitute | subsitute_parent |
|---|---|---|---|---|
| 1 | Type A | 12 | 0 | null |
| 2 | Type B | 44 | 1 | 1 |
| 3 | Type C | 23 | 1 | 1 |
| 4 | Type D | 11 | 0 | null |
| 5 | Type E | 45 | 1 | 4 |
| 6 | Type F | 25 | 1 | 4 |
其中subsitute_parent字段表示该行对应的父行ID。我尝试过先获取全表数据,再通过foreach拆分父行和子行,但这种方法效率较低,代码如下:
$returnedData = ItemModel::all(); $structPODetails = []; foreach ($returnedData as $tableRow) { if (!empty($tableRow->subsitute_parent)) { $structPODetails['child_items'][] = $tableRow; } else { $structPODetails['parent_items'][] = $tableRow; } } return $structPODetails;
请问如何用Laravel高效实现将子行数据嵌套到对应父行的需求?
解决方案
1. 定义模型关联
在ItemModel中添加关联方法,用来绑定父项与子项的关系:
class ItemModel extends Model { // 关联当前父项的所有子替代项 public function substitutes() { return $this->hasMany(ItemModel::class, 'subsitute_parent', 'id'); } }
参数说明:依次为关联模型类名、子表外键字段名、父表关联主键字段名。
2. 预加载关联数据(避免N+1查询)
使用Laravel的预加载功能,只需要2次SQL查询就能完成所有父项与对应子项的关联,效率远高于手动循环匹配:
// 获取所有父项(is_substitute=0)并自动嵌套对应的子项 $parentItems = ItemModel::where('is_substitute', 0) ->with('substitutes') ->get(); return $parentItems;
返回的集合中,每个父项对象都会包含substitutes属性,该属性就是对应父行的所有子行数据,完全符合嵌套需求。
3. 可选:处理全量数据的嵌套场景
如果需要保留所有数据并完成嵌套,可借助集合方法处理,但仍优先推荐上面的预加载方式(数据量大时更高效):
$allItems = ItemModel::all(); // 按subsitute_parent分组,null对应父项 $groupedItems = $allItems->groupBy('subsitute_parent'); // 为父项绑定对应的子项 $structuredItems = $groupedItems->get(null)->map(function ($parent) use ($groupedItems) { $parent->substitutes = $groupedItems->get($parent->id) ?? collect(); return $parent; }); return $structuredItems;
效率对比
原方法仅完成了父项与子项的拆分,未实现嵌套,若手动完成嵌套还需额外循环匹配,会增加内存消耗与代码复杂度。而Laravel的关联预加载利用数据库的关联查询能力,一次性完成数据映射,查询次数更少,代码更简洁。
内容的提问来源于stack exchange,提问作者Shasika Madhushan
相关产品推荐
相关产品推荐

