Hierarchy commenting system php
Asked Answered
G

1

8

i want to do discus/reddit/ like commenting system, i have an "id_answer" (set to 0 by defaut) field in my comment database and when user answer to another comment this field is the "id" of the parent comment.

I have the comments of the thread in an array but i dont know how to filter filter each loop for get something like this :

comment lvl 1 ($array[id_answer] is 0)

------- comment lvl 2 ($array[id_answer] is id_of_level_one_comment)

--------------- comment lvl 3 ...

Gailey answered 11/10, 2011 at 18:43 Comment(2)
no, i need to select inside my array where id_answer = 0 and then select again for each comment where id_answer = id_comment, i know how to do it with sql_query but i dont want to do one query per comment.Gailey
https://www.reddit.com/r/PHP/comments/223fzp/how_do_i_render_closure_trees_using_php_for_a/Meridethmeridian
F
21

Use three fields in your database;

  • "id", unique for each comment
  • "parent_id", which is either 0 (top level comment) or the "id" of the parent comment
  • "thread_id", which is the id of whatever you are commenting on

Let's say you want to show the comment tree for your news article with id "123"

When selecting from mysql, select everything with this thread_id:

SELECT id, parent FROM comments WHERE thread_id = 123

Then you should pre-parse your array to give the child comments to their parents, and use a recursive display to show each comment and its child list.

For exemple:

// 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);

This give the following result:

- comment 1
-- comment 3
--- comment 7
---- comment 8
- comment 5
- comment 2
-- comment 6
--- comment 9
-- comment 4
- comment 10

I leave the ORDER BY and such to you, as it should not pose any problem.

Foch answered 11/10, 2011 at 19:18 Comment(8)
FYI: You can (but must not, just a hint) reduce the first two foreach as one: #7673544 (subkey 0 there is childs in your code).Homophile
@hakre: Thanks, I started with my example array and forgot that he could put the comment id as key of the array when fetching the row, edited and simplified a lot. However, I still need to unset in a second foreach afterward because I can't guarantee in which order the row will end up being after his sort.Foch
@Tahola: I edited the code in my answer thanks to hakre's comment, the current one should be way easier to readFoch
@Lepidosteus: You can even unset if you have aliased the value earlier, see the linked example, it's a nice move.Homophile
@Lepidosteus: As long as ID's are sorted 0-n and parentids the same as second, this should just work. Otherwise, a reference array &$map[$id] which contains the element can do the job for any order. That's like you do I think.Homophile
@hakre: as I said I can't guarantee in which order the elements will be in the $comments array, you might have to add childs to $comments[1] AFTER addings $comments[1] as a child to $comments[0] (which would have also resulted in unset($comments[1]). And making sure they are in the right order would still require another foreach to put them back in their display sorted order afterward anyway. Or am I misunderstanding what you are saying ? (if that's the case, could you link a modified codepad of my code sample please)Foch
@Lepidosteus: Yeah I have understood that ;). In that case, this would need the map and I as I read your code you already do that. Would be only a minor tweak, but if you like you could put something up on a codepad and I can take a look. We could array_shuffle first ;)Homophile
@Lepidosteus: In another question today I tested a way with one foreach, that does it all even the data is nor "properly" ordered. I thought this might be interesting for you ;) Scroll to the end of the question, there is a link called "solution" (or here)Homophile

© 2022 - 2024 — McMap. All rights reserved.