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

如何实现带嵌套回复的评论系统Foreach循环渲染

实现Bootstrap 5嵌套评论渲染

我正在开发支持评论回复的系统,需要将嵌套回复正确显示在原评论下。现有包含reactieID、parent_reactieID等字段的评论数组,当前foreach循环仅能区分单条评论、父评论和子评论,但无法生成Bootstrap 5所需的嵌套HTML结构,求修改代码实现该渲染效果。

评论数组

$comments =  Array(
    Array (
        "reactieID" => "1",
        "parent_reactieID" => "-1",
        "reactiePoster" => "User 1",
        "reactieDatum" => "2023-03-27 15:38:25",
        "reactieBericht" => "Single comment 1",
        "HasChild" => "False",
    ),
    Array (
        "reactieID" => "2",
        "parent_reactieID" => "-1",
        "reactiePoster" => "User 2",
        "reactieDatum" => "2023-03-24 15:18:25",
        "reactieBericht" => "Single comment 2",
        "HasChild" => "False",
    ),
    Array (
        "reactieID" => "3",
        "parent_reactieID" => "-1",
        "reactiePoster" => "User 3",
        "reactieDatum" => "2023-03-22 17:09:12",
        "reactieBericht" => "Reaction with replay",
        "HasChild" => "True",
    ),
    Array (
        "reactieID" => "4",
        "parent_reactieID" => "3",
        "reactiePoster" => "Replay 1",
        "reactieDatum" => "2023-03-24 15:18:18",
        "reactieBericht" => "Message 1 for replay on ID 3",
        "HasChild" => "False",
    ),
    Array (
        "reactieID" => "5",
        "parent_reactieID" => "3",
        "reactiePoster" => "Replay 2",
        "reactieDatum" => "2023-03-24 15:19:18",
        "reactieBericht" => "Message 2 for replay on ID 3",
        "HasChild" => "False",
    ),
    Array (
        "reactieID" => "6",
        "parent_reactieID" => "-1",
        "reactiePoster" => "User 4",
        "reactieDatum" => "2023-03-21 15:38:25",
        "reactieBericht" => "Single comment 4",
        "HasChild" => "False",
    )
);

当前循环代码

foreach($comments as $cl) {
    if ($cl["HasChild"] == 'True' && $cl["parent_reactieID"] = -1) {
        echo ' Has Child '.  $cl["reactieID"].PHP_EOL.'';
    } elseif ($cl["HasChild"] == 'False' && $cl["parent_reactieID"]!= -1) {
        echo ' is Child '.  $cl["reactieID"].PHP_EOL.'';
    } elseif ($cl["HasChild"] == 'False' && $cl["parent_reactieID"] = -1) {
        echo ' single comment '.  $cl["reactieID"].PHP_EOL.'';
    }
}

期望的Bootstrap 5嵌套HTML结构

<!-- Comments -->
<section>
    <h4 class="fw-bolder mb-1"><?php echo $db->count_comment($nieuwsid).' '. $db->name($nieuwsid);?></h4>
    <div class="card bg-light mb-4">
        <div class="card-body">
            <!-- Single comment ID 1-->
            <div class="d-flex mb-4">
                <div class="flex-shrink-0"><img class="rounded-circle"
                        src="https://ui-avatars.com/api/?background=random&name=User 1&rounded=true&size=50"
                        alt="..." /></div>
                <div class="ms-3">
                    <div class="fw-bold">User 1 <span class="text-muted fw-normal">2023-03-27 15:38:25</span>
                    </div>
                    Single comment 1
                </div>
            </div>
            <!-- Single comment ID 2-->
            <div class="d-flex mb-4">
                <div class="flex-shrink-0"><img class="rounded-circle"
                        src="https://ui-avatars.com/api/?background=random&name=User 2&rounded=true&size=50"
                        alt="..." /></div>
                <div class="ms-3">
                    <div class="fw-bold">User 2 <span class="text-muted fw-normal">2023-03-24 15:18:25</span>
                    </div>
                    Single comment 2
                </div>
            </div>
            <!-- Comment with nested comments ID 3-->
            <div class="d-flex mb-4">
                <!-- Parent comment-->
                <div class="flex-shrink-0"><img class="rounded-circle"
                        src="https://ui-avatars.com/api/?background=random&name=User 3&rounded=true&size=50"
                        alt="..." /></div>
                <div class="ms-3">
                    <div class="fw-bold">User 3 <span class="text-muted fw-normal">2023-03-22 17:09:12</span>

                    </div>
                    Reaction with replay
                    <!-- Child comment 1 ID 4-->
                    <div class="d-flex mt-4">
                        <div class="flex-shrink-0"><img class="rounded-circle"
                                src="https://ui-avatars.com/api/?background=random&name=Replay 1&rounded=true&size=50"
                                alt="..." /></div>
                        <div class="ms-3">
                            <div class="fw-bold">Replay 1 <span class="text-muted fw-normal">2023-03-24 15:18:18</span>

                            </div>
                            Message 1 for replay on ID 3
                        </div>
                    </div>
                    <!-- Child comment 2 ID 5-->
                    <div class="d-flex mt-4">
                        <div class="flex-shrink-0"><img class="rounded-circle"
                                src="https://ui-avatars.com/api/?background=random&name=Replay 2&rounded=true&size=50"
                                alt="..." /></div>
                        <div class="ms-3">
                            <div class="fw-bold">Replay 2 <span class="text-muted fw-normal">2023-03-24 15:19:18</span>

                            </div>
                            Message 2 for replay on ID 3
                        </div>
                    </div>
                </div>
            </div>
            <!-- Single comment ID 6-->
            <div class="d-flex mb-4">
                <div class="flex-shrink-0"><img class="rounded-circle"
                        src="https://ui-avatars.com/api/?background=random&name=User 4&rounded=true&size=50"
                        alt="..." /></div>
                <div class="ms-3">
                    <div class="fw-bold">User 4 <span class="text-muted fw-normal">2023-03-21 15:38:25</span>
                    </div>
                    Single comment 4
                </div>
            </div>
        </div>
    </div>
</section>

解决方案

要实现嵌套渲染,需先将扁平评论数组转为树形结构,再通过递归函数渲染每一层评论。

步骤1:转换评论数组为树形结构

将所有评论按reactieID索引,再把子评论挂载到对应父评论的children属性下:

// 先将评论数组转为以reactieID为键的数组
$commentMap = [];
foreach ($comments as $comment) {
    $comment['children'] = [];
    $commentMap[$comment['reactieID']] = $comment;
}

// 构建树形结构
$treeComments = [];
foreach ($commentMap as $comment) {
    if ($comment['parent_reactieID'] === '-1') {
        $treeComments[] = $comment;
    } else {
        if (isset($commentMap[$comment['parent_reactieID']])) {
            $commentMap[$comment['parent_reactieID']]['children'][] = $comment;
        }
    }
}

步骤2:编写递归渲染函数

创建函数负责渲染单条评论及其所有子评论:

function renderComment($comment) {
    // 转义内容防止XSS
    $poster = htmlspecialchars($comment['reactiePoster']);
    $date = htmlspecialchars($comment['reactieDatum']);
    $message = htmlspecialchars($comment['reactieBericht']);
    $avatarUrl = "https://ui-avatars.com/api/?background=random&name=".urlencode($poster)."&rounded=true&size=50";
    
    // 渲染当前评论
    $html = '<div class="d-flex mb-4">';
    $html .= '<div class="flex-shrink-0"><img class="rounded-circle" src="'.$avatarUrl.'" alt="..." /></div>';
    $html .= '<div class="ms-3">';
    $html .= '<div class="fw-bold">'.$poster.' <span class="text-muted fw-normal">'.$date.'</span></div>';
    $html .= $message;
    
    // 渲染子评论
    if (!empty($comment['children'])) {
        foreach ($comment['children'] as $child) {
            // 子评论使用mt-4替代mb-4,保持嵌套样式
            $childHtml = renderComment($child);
            $childHtml = str_replace('mb-4', 'mt-4', $childHtml);
            $html .= $childHtml;
        }
    }
    
    $html .= '</div></div>';
    return $html;
}

步骤3:完整渲染代码

整合逻辑生成最终Bootstrap评论区:

<!-- Comments -->
<section>
    <h4 class="fw-bolder mb-1"><?php echo $db->count_comment($nieuwsid).' '. $db->name($nieuwsid);?></h4>
    <div class="card bg-light mb-4">
        <div class="card-body">
            <?php
            // 转换为树形结构
            $commentMap = [];
            foreach ($comments as $comment) {
                $comment['children'] = [];
                $commentMap[$comment['reactieID']] = $comment;
            }

            $treeComments = [];
            foreach ($commentMap as $comment) {
                if ($comment['parent_reactieID'] === '-1') {
                    $treeComments[] = $comment;
                } else {
                    if (isset($commentMap[$comment['parent_reactieID']])) {
                        $commentMap[$comment['parent_reactieID']]['children'][] = $comment;
                    }
                }
            }

            // 渲染所有顶级评论
            foreach ($treeComments as $topComment) {
                echo renderComment($topComment);
            }
           ?>
        </div>
    </div>
</section>

<?php
function renderComment($comment) {
    $poster = htmlspecialchars($comment['reactiePoster']);
    $date = htmlspecialchars($comment['reactieDatum']);
    $message = htmlspecialchars($comment['reactieBericht']);
    $avatarUrl = "https://ui-avatars.com/api/?background=random&name=".urlencode($poster)."&rounded=true&size=50";
    
    $html = '<div class="d-flex mb-4">';
    $html .= '<div class="flex-shrink-0"><img class="rounded-circle" src="'.$avatarUrl.'" alt="..." /></div>';
    $html .= '<div class="ms-3">';
    $html .= '<div class="fw-bold">'.$poster.' <span class="text-muted fw-normal">'.$date.'</span></div>';
    $html .= $message;
    
    if (!empty($comment['children'])) {
        foreach ($comment['children'] as $child) {
            $childHtml = renderComment($child);
            $childHtml = str_replace('mb-4', 'mt-4', $childHtml);
            $html .= $childHtml;
        }
    }
    
    $html .= '</div></div>';
    return $html;
}
?>

说明

  • 树形结构转换:将扁平数组转为层级结构,方便递归遍历
  • 递归渲染:自动处理任意层级的嵌套评论
  • XSS防护:使用htmlspecialchars转义用户输入内容
  • 样式适配:子评论替换mb-4为mt-4,保持嵌套后的间距样式

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 00:37:04