Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/wp-includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2812,7 +2812,59 @@ function wp_update_comment_count_now( $post_id ) {
$new = apply_filters( 'pre_wp_update_comment_count_now', null, $old, $post_id );

if ( is_null( $new ) ) {
$new = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id ) );
/**
* Get all the comments related to the post ID.
*/
$comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_ID, comment_parent, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach could be too expensive if there are a large number of comments. Have you tested this with, for example, 1ok unapproved comments? It would be good to understand the performance impact at that scale.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not tested this scenario with that number of comments, I will check for the same. Also, I am not sure if we have a better solution than this? because everything is in one table like the replationships as well, SQL query would be also expensive in this case and would not cover all the deeply nested comments.

We can also do some caching with array mechanism to skip the parents that are already checked in the current approach?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mukeshpanchal27, I tested with 10K comments and it looks like it is not performant heavy, I have recorder the screencast below with the changes in PR and it looks good with not performant heavy in counting the comments, ofCourse it would take time when the actual comments are being rendered on the page.

Screen.Recording.2025-07-17.at.2.39.26.PM.mov

Thank You,

$comments_by_id = array();

/**
* Create a lookup array by comment ID.
*/
foreach ( $comments as $comment ) {
$comments_by_id[ $comment->comment_ID ] = $comment;
}

// Count for comment.
$comment_count = 0;

/**
* Loop through each comment and check for approved comment.
*/
foreach ( $comments as $comment ) {

// Proceed only if comment is approved for counting.
if ( '1' !== $comment->comment_approved ) {
continue;
}

$parent_id = (int) $comment->comment_parent;
$has_unapproved = false;

/**
* Check until we get the parent id as 0.
*/
while ( 0 !== $parent_id ) {
if ( ! isset( $comments_by_id[ $parent_id ] ) ) {
break;
}

$parent_comment = $comments_by_id[ $parent_id ];

if ( '1' !== $parent_comment->comment_approved ) {
$has_unapproved = true;
break;
}

$parent_id = (int) $parent_comment->comment_parent;
}

if ( ! $has_unapproved ) {
$comment_count++;
}
}

$new = $comment_count;
} else {
$new = (int) $new;
}
Expand Down
114 changes: 114 additions & 0 deletions tests/phpunit/tests/comment/wpUpdateCommentCountNow.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,118 @@ public function test_using_filter_adjusts_comment_count_without_an_additional_da
public function _return_100() {
return 100;
}

/**
* Test case where a trashed parent comment causes its child comments to be excluded from the comment count.
*
* @ticket 36409
*
* @return void
*/
public function test_trashed_parent_comment_excludes_child_comments_from_count() {
$post_id = self::factory()->post->create();

/**
* Create 2 parent comment, and 2 child comment for parent 1.
*/
$parent_comment_id = self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_parent' => $parent_comment_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_parent' => $parent_comment_id,
'comment_approved' => 1,
)
);

wp_update_comment_count_now( $post_id );
$this->assertSame( '4', get_comments_number( $post_id ) );

wp_update_comment(
array(
'comment_ID' => $parent_comment_id,
'comment_approved' => 'trash',
)
);

wp_update_comment_count_now( $post_id );
$this->assertSame( '1', get_comments_number( $post_id ) );
}

/**
* Test case for unapproved parent comment causing its child comments to be excluded from the comment count.
*
* @ticket 36409
*
* @return void
*/
public function test_unapproved_parent_comment_excludes_child_comments_from_count() {
$post_id = self::factory()->post->create();

/**
* Create 2 parent comment, and 2 child comment for parent 1.
*/
$parent_comment_id = self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_parent' => $parent_comment_id,
'comment_approved' => 1,
)
);

self::factory()->comment->create(
array(
'comment_post_ID' => $post_id,
'comment_parent' => $parent_comment_id,
'comment_approved' => 1,
)
);

wp_update_comment_count_now( $post_id );
$this->assertSame( '4', get_comments_number( $post_id ) );

wp_update_comment(
array(
'comment_ID' => $parent_comment_id,
'comment_approved' => '0',
)
);

wp_update_comment_count_now( $post_id );
$this->assertSame( '1', get_comments_number( $post_id ) );
}
}
Loading