层级评论系统 PHP

8
我希望实现像讨论区/Reddit一样的评论系统,我在我的评论数据库中有一个名为"id_answer"的字段(默认设置为0),当用户回复另一个评论时,此字段是父级评论的"id"。
我已经将该主题的评论放入了一个数组中,但我不知道如何过滤每个循环以获取类似以下内容的信息:
- 评论层级1($array[id_answer]为0) - ------- 评论层级2($array[id_answer]为层级1评论的id) - --------------- 评论层级3...

不,我需要在我的数组中选择id_answer = 0的部分,然后再为每个评论选择id_answer = id_comment的部分。我知道如何使用sql_query来完成,但我不想为每个评论执行一次查询。 - Tahola
https://www.reddit.com/r/PHP/comments/223fzp/how_do_i_render_closure_trees_using_php_for_a/ - namal
1个回答

21

在您的数据库中使用三个字段:

  • "id",每个评论都是唯一的
  • "parent_id",它要么为0(顶级评论),要么为父评论的"id"
  • "thread_id",它是您要发表评论的内容的id

假设您想显示新闻文章"id"为"123"的评论树

在从mysql选择时,选择具有此thread_id的所有内容:

SELECT id, parent FROM comments WHERE thread_id = 123

那么您应该预处理数组,将子评论分配给其父级,并使用递归显示每个评论及其子列表。

例如:

// getting the comments from mysql, I'm obviously not bothering
//   to check the return value, but in your code you should do it
$result = mysqli_query("SELECT id, parent FROM comments WHERE thread_id = 123");

$comments = array();
while ($row = mysqli_fetch_array($result)) {
  $row['childs'] = array();
  $comments[$row['id']] = $row;
}

// This is the array you get after your mysql query
// Order is non important, I put the parents first here simply to make it clearer.
/*
$comments = array(
    // some top level (parent == 0)
    1 => array('id' => 1, 'parent' => 0, 'childs' => array()),
    5 => array('id' => 5, 'parent' => 0, 'childs' => array()),
    2 => array('id' => 2, 'parent' => 0, 'childs' => array()),
    10 => array('id' => 10, 'parent' => 0, 'childs' => array()),
    // and some childs
    3 => array('id' => 3, 'parent' => 1, 'childs' => array()),
    6 => array('id' => 6, 'parent' => 2, 'childs' => array()),
    4 => array('id' => 4, 'parent' => 2, 'childs' => array()),
    7 => array('id' => 7, 'parent' => 3, 'childs' => array()),
    8 => array('id' => 8, 'parent' => 7, 'childs' => array()),
    9 => array('id' => 9, 'parent' => 6, 'childs' => array()),
);
*/

// now loop your comments list, and everytime you find a child, push it 
//   into its parent
foreach ($comments as $k => &$v) {
  if ($v['parent'] != 0) {
    $comments[$v['parent']]['childs'][] =& $v;
  }
}
unset($v);

// delete the childs comments from the top level
foreach ($comments as $k => $v) {
  if ($v['parent'] != 0) {
    unset($comments[$k]);
  }
}

// now we display the comments list, this is a basic recursive function
function display_comments(array $comments, $level = 0) {
  foreach ($comments as $info) {
    echo str_repeat('-', $level + 1).' comment '.$info['id']."\n";
    if (!empty($info['childs'])) {
      display_comments($info['childs'], $level + 1);
    }
  }
}

display_comments($comments);
这将得到以下结果:
- comment 1
-- comment 3
--- comment 7
---- comment 8
- comment 5
- comment 2
-- comment 6
--- comment 9
-- comment 4
- comment 10

我把ORDER BY之类的内容留给你,因为这应该不会有任何问题。


1
提示:您可以将前两个“foreach”缩减为一个(但不是必须的):http://stackoverflow.com/questions/7673044/nested-array-third-level-is-disappearing/7673415#7673415(在您的代码中,子键“0”是“childs”)。 - hakre
@hakre:谢谢,我从我的示例数组开始,忘记了他可以在获取行时将评论ID作为数组的键,进行了编辑和简化。然而,我仍然需要在第二个foreach中取消设置,因为我无法保证在他的排序之后行将以哪种顺序结束。 - Lepidosteus
@hakre:就像我说的那样,我不能保证$comments数组中元素的顺序,您可能需要在将$comments[1]作为$comments[0]的子项之后向$comments[1]添加子项(这也会导致unset($comments[1]))。而且,确保它们按正确顺序排列仍然需要另一个foreach来将它们放回其显示排序顺序。或者我是否误解了您的意思?(如果是这种情况,您能否链接我的代码示例的修改版本) - Lepidosteus
@Lepidosteus:是的,我已经明白了;)。在这种情况下,这将需要地图,而且根据我阅读您的代码,您已经做到了这一点。只需要进行微小的调整,但如果您愿意,可以在codepad上发布一些内容,我可以看一下。我们可以先进行array_shuffle ;) - hakre
@Lepidosteus:在今天的另一个问题中,我用了一个foreach循环来测试一种方法,即使数据没有“正确”排序,也可以完成所有操作。我认为这可能对你有趣;) 滚动到问题的末尾,有一个名为“solution”的链接(或者在这里)。 - hakre
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接