如何实现带嵌套回复的评论系统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
相关产品推荐
相关产品推荐

