You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于id_parent的多级菜单:删除父项时批量删除所有子项

这里有两种靠谱的方式来实现批量删除多级子菜单的需求,递归和循环遍历,我分别给你拆解一下,顺便提一下必须注意的安全问题:

方案一:递归获取所有子项(代码简洁直观)

递归的思路很直接:先把目标ID(Item1的id)加入待删除列表,然后递归查找每个子项的所有子孙,把它们都合并到列表里。

首先,你可以在模型里新增一个方法来收集所有层级的子孙ID:

public function getAllDescendants($parentId) {
    // 先获取当前父ID的直接子项
    $children = $this->searchChildren($parentId);
    // 初始化待删除列表,先把当前父ID加进去
    $descendants = [$parentId];
    
    foreach ($children as $child) {
        // 递归获取每个子项的所有子孙,合并到列表中
        $descendants = array_merge($descendants, $this->getAllDescendants($child['id']));
    }
    
    return $descendants;
}

这个方法会返回包含目标ID和所有层级子项ID的数组,之后你就可以用这个数组执行批量删除了。

方案二:循环(广度优先)获取所有子项(避免递归溢出)

如果你的菜单层级非常深(比如几十层),递归可能会导致PHP栈溢出报错。这时候用广度优先遍历的循环方式更稳定:

public function getAllDescendants($parentId) {
    $descendants = [$parentId];
    // 用队列存储待处理的ID,先把目标ID放进去
    $queue = [$parentId];
    
    while (!empty($queue)) {
        // 取出队列的第一个ID
        $currentId = array_shift($queue);
        // 获取这个ID的直接子项
        $children = $this->searchChildren($currentId);
        
        foreach ($children as $child) {
            $childId = $child['id'];
            // 把子项ID加入待删除列表
            $descendants[] = $childId;
            // 把子项ID加入队列,继续处理它的子项
            $queue[] = $childId;
        }
    }
    
    return $descendants;
}

这种方式用队列来逐层处理所有子项,不会有递归栈的限制,适合复杂的深层级菜单。

关键优化:修复SQL注入风险

你当前的searchChildren方法直接把$id拼进SQL语句里,这是非常危险的SQL注入漏洞!必须改成参数化查询,我给你两种常见数据库扩展的写法:

如果你用PDO:

public function searchChildren($id) {
    $sql = "SELECT id FROM menu WHERE id_parent = ?";
    $stmt = $this->db->prepare($sql);
    // 绑定参数并执行
    $stmt->execute([$id]);
    // 返回所有子项的ID数组
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

如果你用mysqli:

public function searchChildren($id) {
    $sql = "SELECT id FROM menu WHERE id_parent = ?";
    $stmt = $this->db->prepare($sql);
    // 假设id是整数类型,用"i"绑定;如果是字符串用"s"
    $stmt->bind_param("i", $id);
    $stmt->execute();
    $result = $stmt->get_result();
    
    $children = [];
    while ($row = $result->fetch_assoc()) {
        $children[] = $row;
    }
    return $children;
}

执行批量删除的示例

拿到所有待删除的ID数组后,就可以执行批量删除了,同样要用参数化查询避免注入:

// 假设Item1的id是$targetId
$targetId = 1;
// 获取所有要删除的ID
$allDeleteIds = $this->model->getAllDescendants($targetId);

// 生成IN子句的占位符,比如ids有3个的话就是"?, ?, ?"
$placeholders = rtrim(str_repeat('?, ', count($allDeleteIds)), ', ');
$sql = "DELETE FROM menu WHERE id IN ($placeholders)";

// PDO写法
$stmt = $this->db->prepare($sql);
$stmt->execute($allDeleteIds);

// mysqli写法
$stmt = $this->db->prepare($sql);
// 动态绑定参数,这里需要处理类型,假设都是整数
$types = str_repeat('i', count($allDeleteIds));
$stmt->bind_param($types, ...$allDeleteIds);
$stmt->execute();

这样就能一次性删除Item1和它所有层级的子项啦!

内容的提问来源于stack exchange,提问作者KvinT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:14:11