PHP Laravel中如何使用递归函数插入带parent_id的嵌套数组数据
无限层级嵌套数据递归入库实现方案
核心逻辑
无需提前预知数据嵌套层数,递归处理遵循固定规则即可:
- 处理单个节点时,先写入当前节点数据到数据库,获取插入生成的主键ID
- 若当前节点存在非空的
children子节点数组,遍历所有子节点,将当前节点的主键ID作为子节点的parent_id,复用同一处理逻辑逐个处理子节点 - 子节点完成入库后,会自动处理自身的下一级子节点,直到节点
children为空时,当前分支递归终止
前置准备
首先确保存储表包含必要字段,以MySQL分类表为例,建表语句如下:
CREATE TABLE `fruit_category` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `label` varchar(100) NOT NULL DEFAULT '' COMMENT '分类名称', `parent_id` int unsigned NOT NULL DEFAULT '0' COMMENT '父级ID,顶级节点值为0', PRIMARY KEY (`id`), KEY `idx_parentid` (`parent_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
代码实现(PHP+PDO示例)
<?php // 读取原始JSON数据,转为PHP数组 $rawJson = <<<JSON { "items": [ { "label": "Apricots", "children": [] }, { "label": "Blackberries", "children": [] }, { "label": "Blueberries", "children": [ { "label": "Blackcurrant", "children": [ { "label": "Blackcurrant blanc Petits Grains", "children": [] }, { "label": "Blackcurrant of Alexandria", "children": [ { "label": "Normal", "children": [] }, { "label": "Hanepoot", "children": [] } ] }, { "label": "Blackcurrant of Scanzorosciate", "children": [] } ] }, { "label": "Coconut Meat", "children": [] }, { "label": "Clementine", "children": [] }, { "label": "Cherries", "children": [] }, { "label": "Cranberries", "children": [] } ] }, { "label": "Gooseberries", "children": [] }, { "label": "Honeydew Melon", "children": [ { "label": "Java-Plum", "children": [] }, { "label": "Lemon", "children": [] } ] } ] } JSON; $data = json_decode($rawJson, true); // 初始化数据库连接,替换为实际的数据库配置 $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8mb4', 'root', 'your_db_password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 定义递归处理函数 function processNode(array $node, int $parentId, PDO $pdo): void { // 插入当前节点数据 $stmt = $pdo->prepare("INSERT INTO fruit_category (label, parent_id) VALUES (?, ?)"); $stmt->execute([$node['label'], $parentId]); // 获取当前节点插入后的自增ID,作为下一级子节点的parent_id $currentId = (int)$pdo->lastInsertId(); // 没有子节点直接终止当前分支递归 if (empty($node['children'])) { return; } // 遍历所有子节点递归处理 foreach ($node['children'] as $childNode) { processNode($childNode, $currentId, $pdo); } } // 开启事务(可选,避免中途插入失败产生脏数据) $pdo->beginTransaction(); try { // 从顶级节点开始遍历,顶级节点parent_id固定为0 foreach ($data['items'] as $topNode) { processNode($topNode, 0, $pdo); } $pdo->commit(); echo "所有层级数据入库完成"; } catch (Exception $e) { $pdo->rollBack(); echo "入库失败,已回滚:" . $e->getMessage(); } ?>
注意事项
- 递归深度限制:PHP默认递归深度限制为100层,常规分类场景不会触发该限制,如果存在超深层级需求,可以通过
ini_set('xdebug.max_nesting_level', 1000)调整限制,也可以改用数组模拟栈的非递归实现,完全规避层数限制 - 事务使用:数据量较大或者对数据一致性要求高的场景,必须加事务包裹全量入库逻辑,任意节点插入失败时整体回滚,避免出现孤立的无效数据
- 字段扩展:如果节点存在其他业务字段,只需要在插入语句中补充对应的字段映射即可,递归核心逻辑不需要改动
内容的提问来源于stack exchange,提问作者Joney Spark
相关产品推荐
相关产品推荐

