How to add comment form functionality to a blog post

A blog post is not complete without a Comment Form and comments.

People read articles all the time and they would love to express their thoughts if they love or hate a particular article.

And it is extremely easy to create a Comment Form in WordPress.

All you have to do is call comments_template() function inside the Loop of single.php file.


<?php comments_template(); ?>

Go ahead and put the above line of code inside single.php file and to be precise, put it right underneath the_content() function call.

Common, let’s take a look at the Comment Form section in the frontend by visiting any single blog post.

And Bang! There is a Notice! 

Although WordPress is printing out the Comment Form functionality, it says that we have to create a comments.php file and take control of the comments functionality.

Well, let’s do just that then. 

And I am still sticking to my previous statement. 

It is still extremely easy to create a Comment Form in WordPress.

Common, go ahead and create a new file called comments.php inside our theme directory.

Here is the update directory structure of our theme:

if now you go back to the single blog post page in the browser:

Uff…The comment form that wordpress previously outputted is now gone.

This is happening because WordPress is trying to render the comment form using the code inside the comments.php file and the file is empty.

“So, do we have to code the Comment Form our selves?”

Na! We don’t have to! 

WordPress provides us with several PHP functions that output all the functionality that is required for the comment form and in fact the entire comment section.

All we have to do use them properly to create a decent comments.php file.

“So, how do we get started?”

Simple! Ever tried looking into the code of the default WordPress themes like “twentyseventeen”, “twentynineteen”, etc?

“Nope!”

Well, you better start learning from those default themes. The default WordPress themes are GEMS that are not hidden. 

You can learn quite a bit of code organization techniques and coding tricks from them.

It is a good practice to steal the code from the default themes

And, sometimes you have to steal quite a bit of code from them for your custom theme. Trust me, it makes our life as a theme developer much easier. 

So, go ahead and open up the comments.php file of “twentyseventeen” theme. If for some reason you do not have “twentyseventeen” theme installed, you can easily download it by going to:

 Admin Dashboard -> Appearance -> Themes -> Add New -> Search “twentyseventeen”

Next, copy all the code from the comments.php file of “twentyseventeen” theme and paste it inside our “nd-dosth” theme’s comments.php file.

If you are lazy, Here is the code for our theme’s comments.php file. 


<?php
/**
 * The template for displaying comments
 *
 * This is the template that displays the area of the page that contains both the current comments
 * and the comment form.
 *
 * @link https://codex.wordpress.org/Template_Hierarchy
 *
 * @package WordPress
 * @subpackage Twenty_Seventeen
 * @since 1.0
 * @version 1.0
 */
/*
 * If the current post is protected by a password and
 * the visitor has not yet entered the password we will
 * return early without loading the comments.
 */
if ( post_password_required() ) {
    return;
}
?>
<div id="comments" class="comments-area">
    <?php
    // You can start editing here -- including this comment!
    if ( have_comments() ) : ?>
        <h2 class="comments-title">
            <?php
            $comments_number = get_comments_number();
            if ( '1' === $comments_number ) {
                /* translators: %s: post title */
                printf( _x( 'One Reply to &ldquo;%s&rdquo;', 'comments title', 'twentyseventeen' ), get_the_title() );
            } else {
                printf(
                    /* translators: 1: number of comments, 2: post title */
                    _nx(
                        '%1$s Reply to &ldquo;%2$s&rdquo;',
                        '%1$s Replies to &ldquo;%2$s&rdquo;',
                        $comments_number,
                        'comments title',
                        'twentyseventeen'
                    ),
                    number_format_i18n( $comments_number ),
                    get_the_title()
                );
            }
            ?>
        </h2>
        <ol class="comment-list">
            <?php
                wp_list_comments( array(
                    'avatar_size' => 100,
                    'style'       => 'ol',
                    'short_ping'  => true,
                    'reply_text'  => twentyseventeen_get_svg( array( 'icon' => 'mail-reply' ) ) . __( 'Reply', 'twentyseventeen' ),
                ) );
            ?>
        </ol>
        <?php the_comments_pagination( array(
            'prev_text' => twentyseventeen_get_svg( array( 'icon' => 'arrow-left' ) ) . '<span class="screen-reader-text">' . __( 'Previous', 'twentyseventeen' ) . '</span>',
            'next_text' => '<span class="screen-reader-text">' . __( 'Next', 'twentyseventeen' ) . '</span>' . twentyseventeen_get_svg( array( 'icon' => 'arrow-right' ) ),
        ) );
    endif; // Check for have_comments().
    // If comments are closed and there are comments, let's leave a little note, shall we?
    if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) : ?>
        <p class="no-comments"><?php _e( 'Comments are closed.', 'twentyseventeen' ); ?></p>
    <?php
    endif;
    comment_form();
    ?>
</div><!-- #comments -->

If you now go back to the single blog post page in the browser and refresh it:

Ah! Now the comment form is getting outputted without any notice or errors!

Also, if you inspect the HTML markup of the field, you’ll find HTML5 e-mail and URL field types. This is happening because we have enabled support for HTML5.

Great! But don’t let this output fool you. There is a problem.

When we are copying the code from default WordPress themes, in that code, we have to look for PHP function calls whose function definition is not in our theme.

If you notice our theme’s comments.php file carefully, you will find such a PHP function.

Can you guess which function it is?

“Yessssss!! Its twentyseventeen_get_svg() function, right?”

Great! on what line numbers of the comments.php file is this function getting called?

“This function is getting called on the line numbers 60, 66 and 67”

Oh great! You have sharp eyes.

Our theme doesn’t have the function definition for twentyseventeen_get_svg() function. 

“But why PHP is not throwing any error? We did not define this function in our theme, right?”

I honestly have no Idea. I am no PHP expert, but from what I see, although we did not define this function in our theme, PHP could be failing to throw the error because this function is getting called inside an array and is failing silently.

I tried calling this function outside the array and PHP threw a fatal error. 

Anyway, the bottom line is, when we copy the code from a particular default theme, for example, “twentyseventeen”, we have to look for any function calls that belong to the “twentyseventeen” inside the copied code. 

We can easily recognize them because their function starts with their theme name. For example, if you notice the twentyseventeen_get_svg() function name, it is starting with “twentyseventeen”.

And, once we find such a function, we have two options:

  1. Search for the function definition inside the default theme and copy it in our theme as well. 
  2. If removing the function call from our theme file is harmless and doesn’t break the functionality, just remove it.

Most of the time, we have to go with the first option. But, twentyseventeen_get_svg() function is harmless as it is just outputting a SVG icon. So, in our case, we will go with the second option. We will just remove all the traces of twentyseventeen_get_svg() function from our theme’s comments.php file.

This will solve our problem.

Here is the updated code from comments.php file:


<?php
/**
 * The template for displaying comments
 *
 * This is the template that displays the area of the page that contains both the current comments
 * and the comment form.
 *
 * @link https://codex.wordpress.org/Template_Hierarchy
 *
 * @package WordPress
 * @subpackage Twenty_Seventeen
 * @since 1.0
 * @version 1.0
 */
/*
 * If the current post is protected by a password and
 * the visitor has not yet entered the password we will
 * return early without loading the comments.
 */
if ( post_password_required() ) {
    return;
}
?>
<div id="comments" class="comments-area">
    <?php
    // You can start editing here -- including this comment!
    if ( have_comments() ) : ?>
        <h2 class="comments-title">
            <?php
            $comments_number = get_comments_number();
            if ( '1' === $comments_number ) {
                /* translators: %s: post title */
                printf( _x( 'One Reply to &ldquo;%s&rdquo;', 'comments title', 'nd_dosth' ), get_the_title() );
            } else {
                printf(
                    /* translators: 1: number of comments, 2: post title */
                    _nx(
                        '%1$s Reply to &ldquo;%2$s&rdquo;',
                        '%1$s Replies to &ldquo;%2$s&rdquo;',
                        $comments_number,
                        'comments title',
                        'nd_dosth'
                    ),
                    number_format_i18n( $comments_number ),
                    get_the_title()
                );
            }
            ?>
        </h2>
        <ol class="comment-list">
            <?php
                wp_list_comments( array(
                    'avatar_size' => 100,
                    'style'       => 'ol',
                    'short_ping'  => true,
                    'reply_text'  => __( 'Reply', 'nd_dosth' ),
                ) );
            ?>
        </ol>
        <?php the_comments_pagination( array(
            'prev_text' => '<span class="screen-reader-text">' . __( 'Previous', 'nd_dosth' ) . '</span>',
            'next_text' => '<span class="screen-reader-text">' . __( 'Next', 'nd_dosth' ) . '</span>',
        ) );
    endif; // Check for have_comments().
    // If comments are closed and there are comments, let's leave a little note, shall we?
    if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) : ?>
        <p class="no-comments"><?php _e( 'Comments are closed.', 'nd_dosth' ); ?></p>
    <?php
    endif;
    comment_form();
    ?>
</div><!-- #comments -->

Also, if you notice, comments.php file is filled with a ton of WordPress translation functions. So, in the above-updated-code, I just replaced “twentyseveteen” text domain with our theme’s text domain, that is, “nd_dosth”.

Now, before we break down the comments.php file into bits and pieces, let’s test it out by going back to the comment form in the browser.

Let’s test the Comment Form

If you are already logged in, you’ll see just the “Comment” textarea box. Since you are logged in, WordPress is taking all the details like Name, Email and Website from your profile.

If you viewing the same comment form after logging out, you have to fill out the other fields too.

Now post at least four comments and give reply to a couple of them by using a different name. 

I went ahead and created and some comments and everything is working fine in my testing!

If you notice, there are four approved comments and one comment is waiting for moderation. The comment waiting for moderation can only be seen by the person who created it and the administrator of the site.

Now that you know what output that the code inside comments.php file is generating, you can easily skim through the file without any injuries 😛

Now let’s break down the comments template a.k.a comments.php file


if ( post_password_required() ) {
    return;
}

For the rest of the lesson, I will be referring to the blog post as the “post” because, comments.php file can be used for the custom posts too! Just like, single.php file, This file is universal too!

Outputting the title of the comment section

Anyway, First of all, we are checking whether the post is password protected by using the post_password_required()

If the post is password protected, we are just exiting out the of the file so that the rest of the file code will not be processed.

Next, we are checking if there are any comments for the post using the have_comments() WordPress function. This function will return true if there is at least comment made for the post.

If there are comments, first we are getting the comment count using the get_comments_number() WordPress function and if the comment count is one, we are outputting the title of the comment section in the following way:

But if the comment count is more that one, we are outputting the comment count dynamically while providing some context to the translators via _nx() WordPress translation function.

We are now done with the title of the comment.

Outputting the comments

Next, using the wp_list_comments() WordPress function, we are outputting the comments of the post.


<ol class="comment-list">
    <?php
        wp_list_comments( array(
            'avatar_size' => 100,
            'style'       => 'ol',
            'short_ping'  => true,
            'reply_text'  => __( 'Reply', 'nd_dosth' ),
        ) );
    ?>
</ol>

The wp_list_comments() takes an array of options which allow us to control the markup of the comment list.

You can learn more about this function by visiting the following URL.

https://codex.wordpress.org/Function_Reference/wp_list_comments

Feel free play around with various options.

The most interesting option of all is the “callback”.

With “callback” option we can write our own custom markup for individual comment. It might look complex. But in reality, it is not! 

And it is really useful to take control of the markup for individual comment. It helps you style the comment in the way you want!

There are a ton of articles explaining how to do this, do some research and just try it out! You’ll learn a lot.

Comment list depends on Gravatar service too

Also, if you notice the above screenshot, a dummy image is being used for the avatar. 

This thing is that WordPress depends on Gravatar service for comment author avatars as well.

You can’t just skip the Gravatar service.

Outputting the comments pagination

Next, we are outputting the comments pagination by using the_comments_pagination() WordPress function.


the_comments_pagination( array(
    'prev_text' => '<span class="screen-reader-text">' . __( 'Previous', 'nd_dosth' ) . '</span>',
    'next_text' => '<span class="screen-reader-text">' . __( 'Next', 'nd_dosth' ) . '</span>',
) );

We are modifying the markup of the pagination’s “next” and “previous” link text by providing an array with arguments as a parameter. 

Checking if comments are open 

Then, we are checking whether comments are open for a particular post using the comments_open() WordPress function. If the comments are closed, we are informing the commenter that he/she can no longer comment on this post.

// If comments are closed and there are comments, let's leave a little note, shall we?
if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) : ?>
    <p class="no-comments"><?php _e( 'Comments are closed.', 'nd_dosth' ); ?></p>
<?php
endif;

In WordPress, you control whether to close or open comments for a particular post by going to: 

Post edit screen -> Document options -> Discussion 

Outputting the comment form

Lastly, we are outputting the Comment Form using the comment_form() WordPress function.

The comment_form() function is smart too! If the comments are not open for a particular blog post, the comment form will not be displayed. Cool, right?

And, Just like wp_list_comments() function, the comment_form() function also accepts an array of options which allows us to add/modify the markup of the comment form, For example:

<?php $comment_args = array(
        'comment_notes_before'  => '<p class="required-message">'. __( 'Fields with ( * ) are required', 'usablewp' ) .'</p>',
        'title_reply' => __( 'We would love your opinion', 'usablewp'  ),
        'comment_field' => '<p>' .
            '<label for="comment">' . __( 'Let us know what you have to say:', 'usablewp' ) . '</label>' .
            '<textarea id="comment" name="comment" cols="45" rows="6" aria-required="true"></textarea>' .
            '</p>',
        'comment_notes_after'       => '',
        );
?>
<?php comment_form( $comment_args ); ?>

I implemented this technique on UsableWP comment form and here is the output:

Feel free to edit the comments.php file

As long as you know what you are doing, It is totally ok to edit the code inside the comments.php file. 

Just because you copied the code from the default theme, it doesn’t mean you can’t edit it. Just feel free to play around with it.

In the next lesson, we will style the comment section of a blog post!

Let’s style it!

Go ahead and put the following CSS at the end of the style.css file:


/*-------------------------------------------------------------------------------
  17.Comment Section Styling
-------------------------------------------------------------------------------*/
#respond h3, .comments-title{
    color:black;
    padding:20px;
    font-size:20px;
    border-top:1px solid #000;
    border-bottom:1px solid #000;
    margin:30px 0;
}
#commentform .comment-notes{
    margin-bottom:20px;
}
#commentform label{
    display:block;
}
#commentform input[type="text"],
#commentform input[type="url"],
#commentform input[type="email"],
#commentform textarea{
    border: 1px solid #ccc;
    border-radius: 4px;
    margin-bottom: 20px;
    width: 100%;
    background-color: #f8f8f8;
    padding: 7px 10px;
    font-size: 18px;
}
#commentform input[type="submit"]{
    line-height: 38px;
    border-radius: 4px;
    padding: 0 20px;
    background-color: #fdb813;
    color: black;
    font-weight: bold;
    font-size: 16px;
    border:0;
    position: relative;
    transition:all 0.4s;
    cursor:pointer;
}
#commentform input[type="submit"]:hover{
    background-color: #6e551a;
    color: white;
}
.comment-body .comment-author{
    float:left;
    width:30%;
}
.comment-body .comment-author .fn{
    display:block;
    margin-top:10px;
}
.comment-body{
    overflow:hidden;
    margin-bottom:30px;
}
.comment{
    margin-bottom:30px;
}
.comment-list, .children{
    margin:0;
    padding:0;
    list-style-type: none;
}
.comment-list > li:first-child{
    border-top:0;
    padding-top:0;
}
.comment{
    border-top:1px solid #ccc;
    padding-top:30px;
    position:relative;
}
.children .comment{
    border-top:0;
}
.comment .children{
    border-top:1px dotted #5e666b;
    border-left:1px dotted #5e666b;
    padding-top:10px;
    padding-left:10px;
}
.children{
    margin-left:100px;
}
.children img{
    max-width:75px;
    height:auto;
}
.children .fn{
    font-size:12px;
}
.says{
    display:none;
}
.comment-metadata{
    margin-left:30%;        
}
.comment-metadata a{
    color:#5e666b;
    font-size:14px;
    margin-bottom:10px;
    display:block;
}
.comment-reply-link{
    position:absolute;
    top:15px;
    right:0;
    font-size:12px;
    color:#1a3794;
}

Here is the updated style.css file outline:

And here is the final output of the comments section:

Not bad, right?

I know it looks nothing like the mockup! But you know, time constraints!

Anyway, we are almost done with our blog, but we miss one something and we will fix that in the next lesson!