Skip to content
Advertisement

How to display show replies button for the comment which has replies?

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;
}

Database setup : enter image description here

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 in getArrayWithKeys function
  • Getting a nested associative array with parent comments and all of their sub comments are in comments key of the array in buildNestedComments 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
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement