Separate comments and pings in a WordPress theme

Instead of listing all post reactions – comments, trackbacks, and pingbacks – in one list, I like to separate comments and pings (pingbacks and trackbacks) for the sake of clarity. That is not too difficult, but there are a few things to consider.

The problem (with my old solution)

The way that might be most obvious (and the way I used until recently) is to call the comments template in this way:

comments_template( '', true );
Code language: JavaScript (javascript)

Setting the second param to true gives you the reactions separated by type via the $comments_by_type variable in the comments.php – to get the number of comments, I used to do a count( $comments_by_type['comment'] ). That works without problems if the reactions are not broken into multiple pages. If that is the case, the above function call would only return the number of currently displayed comments, not the total comment number for the post.

The solution

To fix that, we need to fetch the reactions on our own in the comments.php and do not use the second param from comments_template(). To show you how that can be done, I will go through the comments.php of my new theme.

<?php /** * Comments template. * * @version 1.0.0 * * @package Photographus */ if ( post_password_required() ) { return; } ?> <aside aria-labelledby="aside" class="comments-area"> <?php if ( have_comments() ) { /** * We cannot use the second parameter from comments_template() * to separate the reactions because if they are * broken on multiple pages, the count() would only return * the number of comments and pings which are displayed * on the current page, not the total. * * Because of that, we use our own function to get the comments by type. */ $comments_by_type = photographus_get_comments_by_type();
Code language: HTML, XML (xml)

After using post_password_required() to check if the post is password protected and we do not have the correct password, we check for reactions with have_comments(). Next, we get the reactions – already separated – via a custom function. This function lives in the functions.php (or in my case the template-tags.php) and looks like that:

/** * Returns the post reactions separated by type. * * @return array Post reactions separated by type. */ function photographus_get_comments_by_type() { $comment_args = [ 'order' => 'ASC', 'orderby' => 'comment_date_gmt', 'status' => 'approve', 'post_id' => get_the_ID(), ]; $comments = get_comments( $comment_args ); $comments_by_type = separate_comments( $comments ); return $comments_by_type; }
Code language: PHP (php)

At first, we create an args array to get the approved reactions for that post, ordered by date in ascending order. Afterwards, we get the reactions via a get_comments() call, separate the result with separate_comments(), and return it.

We continue in the comments.php with the part of displaying comment count and the list of comments:

if ( ! empty( $comments_by_type['comment'] ) ) { /** * Save the comment count. */ $comment_number = count( $comments_by_type['comment'] ); ?> <h2 class="comments-title" id="comments-title"> <?php printf( /* translators: Title for comment list. 1=comment number, 2=post title */ _n( '%1$s Comment on “%2$s”', '%1$s Comments on “%2$s”', $comment_number, 'photographus' ), number_format_i18n( $comment_number ), get_the_title() ); ?> </h2> <ul class="commentlist"> <?php /** * We need to specify the per_page key because otherwise * the separated reactions would not be broken correctly into * multiple pages. */ wp_list_comments( [ 'callback' => 'photographus_comments', 'style' => 'ul', 'type' => 'comment', 'per_page' => $comment_args['number'], ] ); ?> </ul> <?php }
Code language: JavaScript (javascript)

At first, we check for comments and save their number. We echo it inside an h2 headline using the _n() function, which gets the following parameters:

  1. The singular form of the string.
  2. The plural form.
  3. A number which is used to display either the singular or plural form.
  4. Text domain.

We display the comments as an unordered list and hand over an array with the following arguments to wp_list_comments():

  • photographus_comments as the callback function to display comments. Because using a custom callback is optional, I will not go into that function – you can take a look at it in the code reference.
  • ul as the list style.
  • comment as type, to only get the comments.
  • $comment_args['number'] as the value for per_page, to display the correct number of comments if the reactions should be split into multiple pages. Without that, we would, for example, get five comments and five pings on one page instead of ten comments and pings, if the setting for comment number per page is ten.

Counting and displaying the pings looks similar:

if ( ! empty( $comments_by_type['pings'] ) ) { /** * Save the count of trackbacks and pingbacks. */ $trackback_number = count( $comments_by_type['pings'] ); ?> <h2 class="trackbacks-title" id="trackbacks-title"> <?php printf( /* translators: Title for trackback list. 1=trackback number, 2=post title */ _n( '%1$s Trackback on “%2$s”', '%1$s Trackbacks on “%2$s”', $trackback_number, 'photographus' ), number_format_i18n( $trackback_number ), get_the_title() ); ?> </h2> <ul class="commentlist"> <?php /** * Display the pings in short version. */ wp_list_comments( [ 'type' => 'pings', 'short_ping' => true, 'per_page' => $comment_args['number'], ] ); ?> </ul> <?php }
Code language: JavaScript (javascript)

If we would want, we could go further and also separate pingbacks and trackbacks, using $comments_by_type['pingback'] and $comments_by_type['trackback'] instead of $comments_by_type['pings'].

Now the last part:

/** * Only show the comments navigation if one of the reaction counts * is larger than the set number of comments per page. Necessary because * of our separation. Otherwise, the navigation would also be displayed if * we should display 4 comments per page and have 3 comments and 2 trackbacks. */ if ( ( isset( $trackback_number ) && $trackback_number > $comment_args['number'] ) || ( isset( $comment_number ) && $comment_number > $comment_args['number'] ) ) { the_comments_navigation(); } /** * Display comments closed hint if comments are closed but we already have comments. */ if ( ! comments_open() && get_comments_number() ) { ?> <p class="nocomments"><?php _e( 'Comments are closed.', 'photographus' ); ?></p> <?php } } // End if(). /** * Display comment form with modified submit label. */ comment_form( [ 'label_submit' => __( 'Submit Comment', 'photographus' ), ] ); ?> </aside>
Code language: JavaScript (javascript)

We check if the comment or ping count is higher than the number from the backend setting, and display the navigation in that case. After that, we conditionally display the comments closed hint and the comment form.

Finally, the complete comments.php:

<?php /** * Comments template. * * @version 1.0.0 * * @package Photographus */ if ( post_password_required() ) { return; } ?> <aside aria-labelledby="aside" class="comments-area"> <?php if ( have_comments() ) { /** * We cannot use the second parameter from comments_template() * to separate the reactions because if they are * broken on multiple pages, the count() would only return * the number of comments and pings which are displayed * on the current page, not the total. * * Because of that, we use our own function to get the comments by type. */ $comments_by_type = photographus_get_comments_by_type(); if ( ! empty( $comments_by_type['comment'] ) ) { /** * Save the comment count. */ $comment_number = count( $comments_by_type['comment'] ); ?> <h2 class="comments-title" id="comments-title"> <?php printf( /* translators: Title for comment list. 1=comment number, 2=post title */ _n( '%1$s Comment on “%2$s”', '%1$s Comments on “%2$s”', $comment_number, 'photographus' ), number_format_i18n( $comment_number ), get_the_title() ); ?> </h2> <ul class="commentlist"> <?php /** * We need to specify the per_page key because otherwise * the separated reactions would not be broken correctly into * multiple pages. */ wp_list_comments( [ 'callback' => 'photographus_comments', 'style' => 'ul', 'type' => 'comment', 'per_page' => $comment_args['number'], ] ); ?> </ul> <?php } if ( ! empty( $comments_by_type['pings'] ) ) { /** * Save the count of trackbacks and pingbacks. */ $trackback_number = count( $comments_by_type['pings'] ); ?> <h2 class="trackbacks-title" id="trackbacks-title"> <?php printf( /* translators: Title for trackback list. 1=trackback number, 2=post title */ _n( '%1$s Trackback on “%2$s”', '%1$s Trackbacks on “%2$s”', $trackback_number, 'photographus' ), number_format_i18n( $trackback_number ), get_the_title() ); ?> </h2> <ul class="commentlist"> <?php /** * Display the pings in short version. */ wp_list_comments( [ 'type' => 'pings', 'short_ping' => true, 'per_page' => $comment_args['number'], ] ); ?> </ul> <?php } /** * Only show the comments navigation if one of the reaction counts * is larger than the set number of comments per page. Necessary because * of our separation. Otherwise, the navigation would also be displayed if * we should display 4 comments per page and have 3 comments and 2 trackbacks. */ if ( ( isset( $trackback_number ) && $trackback_number > $comment_args['number'] ) || ( isset( $comment_number ) && $comment_number > $comment_args['number'] ) ) { the_comments_navigation(); } /** * Display comments closed hint if comments are closed but we already have comments. */ if ( ! comments_open() && get_comments_number() ) { ?> <p class="nocomments"><?php _e( 'Comments are closed.', 'photographus' ); ?></p> <?php } } // End if(). /** * Display comment form with modified submit label. */ comment_form( [ 'label_submit' => __( 'Submit Comment', 'photographus' ), ] ); ?> </aside>
Code language: HTML, XML (xml)

Leave a Reply

Your email address will not be published. Required fields are marked *