I am creating nested comment system with reply. comments goes longer and longer in page that is why I wanted to toggle replies.
I already do that auto adding class margins etc.
I have problem with displaying button, show replies button displays under all comments which has parent-id 0, even if it doesn’t have the replies.
because of this :
if($parent_id == 0){ $marginleft = 0; $adclass = "parent"; $adbtn = '<button type="button" class="btn btn-primary btn-xs show_reply">show replies</button>'; }else{ $marginleft = $marginleft + 15; $adclass = "child"; $adbtn = ""; }
Which is working correct way for the class and margin etc.
Here is the php code :
$post_id = intval($_POST["comment_post_id"]); $parent = intval('0'); $active = 'Y'; $sth = $pdo->prepare( "SELECT * FROM comments JOIN profiles ON comments.com_uid = profiles.ik_uid WHERE comments.comment_post_id = ? AND comments.comment_parent_id = ? AND comments.active = ? ORDER BY comment_id DESC "); $sth->execute([$post_id, $parent, $active]); $count = $sth->rowCount(); $output = ''; if($count > 0){ while($row = $sth->fetch()){ if($row['ik_img'] !== ''){ $image = explode('.',$row['ik_img']); $ik_img = $image[0].".webp"; $ik = $row['ik_img']; }else{ $ik_img = 'avatar.jpg'; $ik = 'avatar.jpg'; } if($row['comment_parent_id'] !== $row['comment_id']){ $adclass = "parent"; $adbtn = '<button type="button" class="btn btn-primary btn-xs show_reply">show replies</button>'; }else{ $adclass = "child"; $adbtn = ""; } $output .= ' <div class="form-group border-bottom '.$adclass.'"> <div class="row"> <div class="col-12"><b>'.htmlspecialchars(ucfirst($row["comment_sender_name"])).'</b> said!</div> <div class="row"> <div class="col-2 stimg"> <picture> <source type="image/webp" srcset="uploads/small/'.$ik_img.'"> <img src="uploads/small/'.$ik.'" alt="'.htmlspecialchars($row['comment_sender_name']).'" class="img-fluid"> </picture> </div> <div class="col-10 sttext">'.htmlspecialchars($row['comment']).'</div> </div> <div class="col-12 sttime">'.htmlspecialchars($row["comment_date"]).' <button type="button" class="btn btn-primary btn-xs reply" id="'.intval($row["comment_id"]).'">Reply <i class="fas fa-share"></i></button> </div> <div class="col-12">'.$adbtn.'</div> </div> </div> '; $output .= get_comments($pdo, intval($row["comment_id"]), intval($row["comment_post_id"])); } } echo $output; function get_comments($pdo, $parent_id = 0,$post_id, $active = 'Y', $marginleft = 0){ $stmt = $pdo->prepare( "SELECT * FROM comments JOIN profiles ON comments.com_uid = profiles.ik_uid WHERE comments.comment_post_id = ? AND comments.comment_parent_id = ? AND comments.active = ? ORDER BY comment_id DESC "); $stmt->execute([$post_id, $parent_id, $active]); $count = $stmt->rowCount(); $output = ''; if($count > 0){ if($parent_id == 0){ $marginleft = 0; $adclass = "parent"; $adbtn = '<button type="button" class="btn btn-primary btn-xs show_reply">show replies</button>'; }else{ $marginleft = $marginleft + 15; $adclass = "child"; $adbtn = ""; } while($row = $stmt->fetch()){ if($row['ik_img'] !== ''){ $image = explode('.',$row['ik_img']); $ik_img = $image[0].".webp"; $ik = $row['ik_img']; }else{ $ik_img = 'avatar.jpg'; $ik = 'avatar.jpg'; } $output .= ' <div class="form-group border-bottom '.$adclass.'" style="padding-left:'.$marginleft.'px;"> <div class="row"> <div class="col-12"><b>'.htmlspecialchars(ucfirst($row["comment_sender_name"])).'</b> said!</div> <div class="row"> <div class="col-2 stimg"> <picture> <source srcset="uploads/small/'.$ik_img.'" type="image/webp"> <img src="uploads/small/'.$ik.'" alt="'.htmlspecialchars($row['comment_sender_name']).'" class="img-fluid"> </picture> </div> <div class="col-10 sttext">'.htmlspecialchars($row['comment']).'</div> </div> <div class="col-12 sttime">'.htmlspecialchars($row["comment_date"]).' <button type="button" class="btn btn-primary btn-xs reply" id="'.intval($row["comment_id"]).'">Reply <i class="fas fa-share"></i></button> </div> <div class="col-12">'.$adbtn.'</div> </div> </div> '; $output .= get_comments($pdo, intval($row["comment_id"]), $marginleft); } } return $output; }
Solved I used multilevel recursive function (Like a multilevel menu) it works fine.
Advertisement
Answer
First of all I’ve saw few mistakes in your code:
- Your code makes too many requests to your database – for each comment you will make a new request your database to get it’s sub comments – soon this will cause slow load speed of your pages
- Your database structure doesn’t use indexes – with many records this will cause slow requests to this table
- Your html code is not good
Database structure for my example:
CREATE TABLE `comments` ( `comment_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `com_uid` int(10) unsigned DEFAULT NULL, `comment_parent_id` int(10) unsigned DEFAULT NULL, `comment_post_id` int(10) unsigned NOT NULL, `comment` varchar(200) DEFAULT NULL, `comment_sender_name` varchar(40) DEFAULT NULL, `active` enum('Y','N') NOT NULL DEFAULT 'Y', `comment_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`comment_id`), KEY `com_uid` (`com_uid`), KEY `comment_parent_id` (`comment_parent_id`), KEY `comment_post_id` (`comment_post_id`), KEY `active` (`active`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PHP Code to generate the html:
$query = ' SELECT * FROM comments LEFT JOIN profiles ON (comments.com_uid = profiles.ik_uid) WHERE comments.comment_post_id = :post_id AND comments.active = :active ORDER BY comment_date ASC '; $statement = $pdo->prepare($query); $statement->execute([':post_id' => $post_id, ':active' => 'Y']); $count = $statement->rowCount(); $comments = $statement->fetchAll(PDO::FETCH_ASSOC); $comments = buildNestedComments($comments); $html = getCommentsHtml($comments); function getArrayWithKeys($array, $key) { $result = []; foreach ($array as $value) { $result[$value[$key]] = $value; } return $result; } function buildNestedComments($comments) { $comments = getArrayWithKeys($comments, 'comment_id'); $nestedComments = []; foreach ($comments as &$comment) { $parentId = (isset($comment['comment_parent_id']) && !empty($comment['comment_parent_id'])) ? (int)$comment['comment_parent_id'] : null; if ($parentId === null) { $nestedComments[] = &$comment; if (!isset($comment['comments'])) { $comment['count'] = 0; $comment['comments'] = []; } } else { if (isset($comments[$parentId])) { if (!isset($comments[$parentId]['comments'])) { $comments[$parentId]['count'] = 0; $comments[$parentId]['comments'] = []; } $comments[$parentId]['count']++; $comments[$parentId]['comments'][] = &$comment; } } } return $nestedComments; } function getCommentsHtml($comments) { $html = ''; foreach ($comments as $comment) { if ( isset($comment['ik_img']) && !empty($comment['ik_img']) ) { $image = explode('.',$comment['ik_img']); $ik_img = $image[0] . '.webp'; $ik = $comment['ik_img']; } else { $ik_img = 'avatar.jpg'; $ik = 'avatar.jpg'; } $class = (isset($comment['comment_parent_id']) && !empty($comment['comment_parent_id'])) ? 'comment-' . $comment['comment_id'] . ' comment-parent-' . $comment['comment_parent_id'] : 'comment-' . $comment['comment_id']; $button = ''; $subCommentsHtml = ''; if ( isset($comment['comments']) && !empty($comment['comments']) ) { $button = '<button type="button" class="btn btn-primary btn-xs show_reply">show replies</button>'; $subCommentsHtml = getCommentsHtml($comment['comments']); } $html .= '<div class="form-group border-bottom '.$class.'"> <div class="row"> <div class="col-12"><b>' . htmlspecialchars(ucfirst($comment["comment_sender_name"])) . '</b> said!</div> <div class="row"> <div class="col-2 stimg"> <picture> <source type="image/webp" srcset="uploads/small/' . $ik_img . '"> <img src="uploads/small/' . $ik . '" alt="'.htmlspecialchars($comment['comment_sender_name']).'" class="img-fluid"> </picture> </div> <div class="col-10 sttext">' . htmlspecialchars($comment['comment']) . '</div> </div> <div class="col-12 sttime">' . htmlspecialchars($comment["comment_date"]) . ' <button type="button" class="btn btn-primary btn-xs reply" id="' . intval($comment["comment_id"]) . '">Reply <i class="fas fa-share"></i></button> </div> <div class="col-12">' . $button . '</div> <div class="sub-comments">' . $subCommentsHtml . '</div> </div> </div>'; } return $html; }
As you can see in my example I’m getting all comments at once and after that I’ve made few things:
- Getting the comments as associative array with
comment_id
as key ingetArrayWithKeys
function - Getting a nested associative array with parent comments and all of their sub comments are in
comments
key of the array inbuildNestedComments
function - Getting the html for all of these comments where sub comments are nested in div with class
sub-comments
for easier management with javascript
Then you will be able to use a little css and javascript to style this html and to show/hide sub comments.
Also you have to think about few points:
- Amount of comments – if there are a lot of comments maybe it will be better to load first 10-20 comments
- Lazy load of comments – first load all parent comments and then load their sub comments