
本文详解如何通过递归函数将无限层级的嵌套 json 数据(如菜单、分类树)批量插入数据库,并自动建立 parent_id 关系,避免多层 foreach 带来的可维护性与扩展性问题。
本文详解如何通过递归函数将无限层级的嵌套 json 数据(如菜单、分类树)批量插入数据库,并自动建立 parent_id 关系,避免多层 foreach 带来的可维护性与扩展性问题。
在 Laravel 开发中,处理具有无限嵌套结构的数据(例如分类树、导航菜单、组织架构)时,若依赖硬编码的多层 foreach 循环,不仅代码冗长、难以维护,更无法应对动态深度的层级结构。理想方案是采用递归插入策略:对每一层级节点执行插入操作,并将其父级 ID 作为外键写入下一层子节点,从而构建完整的树形关系。
以下是一个完整、健壮且符合 Laravel 最佳实践的实现方案:
✅ 步骤一:定义模型与迁移
确保你的数据表包含必要字段(以 categories 表为例):
// 迁移文件示例
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('label');
$table->unsignedBigInteger('parent_id')->nullable();
$table->timestamps();
$table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade');
});对应模型需启用批量赋值白名单:
// app/Models/Category.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $fillable = ['label', 'parent_id'];
}✅ 步骤二:编写递归插入函数(推荐封装为 Service 类)
为提升复用性与测试性,建议将逻辑抽离至服务类(如 CategoryTreeService):
// app/Services/CategoryTreeService.php
<?php
namespace App\Services;
use App\Models\Category;
use Illuminate\Support\Facades\DB;
class CategoryTreeService
{
/**
* 递归插入嵌套数组数据到 categories 表
* @param array $items 原始嵌套数据(如 JSON 解析后的 items 数组)
* @param int|null $parentId 父级 ID,根节点传 null
* @return array 插入后生成的 ID 映射(可用于后续关联)
*/
public function insertRecursively(array $items, ?int $parentId = null): array
{
$insertedIds = [];
foreach ($items as $item) {
// 插入当前节点
$category = Category::create([
'label' => $item['label'],
'parent_id' => $parentId,
]);
$insertedIds[] = $category->id;
// 若存在子节点,递归插入
if (!empty($item['children']) && is_array($item['children'])) {
$this->insertRecursively($item['children'], $category->id);
}
}
return $insertedIds;
}
}✅ 步骤三:在控制器中调用
// app/Http/Controllers/CategoryController.php
use App\Services\CategoryTreeService;
use Illuminate\Http\Request;
public function store(Request $request)
{
$data = $request->validate(['items' => 'required|array']);
$service = new CategoryTreeService();
$service->insertRecursively($data['items']);
return response()->json(['message' => 'Categories inserted successfully']);
}? 关键优势说明:
- ✅ 自动适配任意嵌套深度,无需预估循环层数;
- ✅ 每次插入均明确指定 parent_id,保证树形结构完整性;
- ✅ 使用 Eloquent create() 方法,支持模型事件(如 creating/created)、强制类型转换与验证;
- ✅ 可轻松扩展(如添加 slug、order_sort、软删除支持等)。
⚠️ 注意事项
- 性能提醒:对于超大规模数据(如 >1000 节点),建议改用事务 + 批量插入(DB::transaction() + Category::upsert()),避免高频单条查询;
- 数据校验:生产环境务必对 label 字段做非空与长度校验,防止脏数据;
- JSON 输入安全:使用 $request->validate() 或 json_validate() 预检结构合法性,避免 children 字段缺失或类型错误导致递归中断;
- 内存限制:极深嵌套(>100 层)可能触发 PHP 递归栈溢出,此时应考虑迭代式 DFS/BFS 实现(使用栈模拟递归)。
通过该递归方案,你不仅能优雅处理题设中的水果分类树,还可无缝复用于权限菜单、商品类目、评论回复链等所有树形结构场景——真正实现“一次编写,无限嵌套”。


















