Ask your WordPress questions! Pay money and get answers fast! (more info)

multiple comment forms in single.php

  • SOLVED

On each post, I want to have two separate comment areas, each with its own separate comment form and output. The comments from each form will need to be separate and not mingled.

My question is...

Which parts of the existing comment system will I need to duplicate (and then rename/edit), in order to create a separate, parallel group of comments on each post?


Answers (3)

2014-08-15

Dbranes answers:

Here's one idea how one could use only the built in comment system in WordPress, by using different <em>comment types</em> for each comment form:

<strong>Modifications in the template:</strong>

// Comment Form #1:
add_filter( 'comments_array', 'wpq_show_only_normal_comments' );
comments_template( '', true );
remove_filter( 'comments_array', 'wpq_show_only_normal_comments' );

// Comment Form #2:
add_action( 'comment_form_top', 'wpq_show_only_extra_comments' );
add_filter( 'comments_array', 'wpq_hidden_input_field_to_set_extra_comment_content_type' );
comments_template( '', true );
remove_action('comment_form_top', 'wpq_hidden_input_field_to_set_extra_comment_content_type' );
remove_filter( 'comments_array', 'wpq_show_only_extra_comments' );


<strong>In functions.php or a plugin:</strong>
add_filter( 'preprocess_comment', 'wpq_preprocess_comment_to_change_content_type', 99 );


Then it's possible to add a filter in the backend, to view each comment type.

Just let me know if you need some assistance implementing this idea.


dirtcastle comments:

I think this could work!

I agree that a custom "comment type" seems like the way to go. I found the following snippet, which looks like a good way to display comments by type.

$args = array(
'user_id' => $userid,
'type' => 'review'
);
$comments = get_comments($args);


But I still need to figure out how to get each form to have its separate comment type.

I'm pretty sure I know how to get two different comment forms to show up. But how do I associate each form with a different comment type?


Dbranes comments:

Here's an example:

[[LINK href="https://gist.github.com/dbranes/0215762a4cf3db50a6b7"]]https://gist.github.com/dbranes/0215762a4cf3db50a6b7[[/LINK]]


<em>ps:</em> You could also use different comment templates for each form:

comments_template( '/normal-comments.php', true ); // in the above code for form #1

comments_template( '/extra-comments.php', true ); //in the above code for form #2


dirtcastle comments:

Wow. I like how you did all this work on good faith. I just topped up the prize to $25.

I will implement what you've sent and get back to you asap.


Dbranes comments:

great ;-)

Let me test this on my install, to see if this need some adjustments


Dbranes comments:

I updated the code to version 0.0.2

I tested it successfully (so it seems) on TwentyTwelve child theme.

You could then create two separate comments template to modify the layout etc.


dirtcastle comments:

It worked!!!

This is some great code. I really appreciate all of your hard work.


Dbranes comments:

Good to hear it worked ;-)

I updated the the code (version 0.0.4) to show you how you can modify the extra comment form.

I also added the <em>wpq_get_comment_count_by_type( $type, $post_id )</em> function to allow you to count coments by type in your comments templates (<em>normal-comments.php</em> and <em>extra-comments.php</em> or <em>comments.php</em>).

<strong>Example:</strong>

echo wpq_get_comment_count_by_type( $type = array( '', 'pingback', 'trackback' ), 3522 );
echo wpg_get_comment_count_by_type( $type = array( 'extra' ), 3522 );


dirtcastle comments:

I got the two forms working when both use the default comments_template().

I created two custom comment templates.

comments.php
comments_extra.php

The <strong>comments.php</strong> works perfectly because it uses all the default comment stuff I already have.

But I'm struggling to customize the output of <strong>comments_extra.php</strong>.

I successfully edited comments_extra.php and replaced the default comment_form() fields using the following. Note, I added a new "website" field.


$commenter = wp_get_current_commenter();
$req = get_option( 'require_name_email' );
$aria_req = ( $req ? " aria-required='true'" : '' );

$comment_args = array( 'fields' => apply_filters( 'comment_form_default_fields', array(
'author' => '' .
'<label for="author">' . __( 'Your Name:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="author" name="author" type="text" value="' .
esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-author .form-section -->',

'email' => '' .
'<label for="email">' . __( 'Your Email:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="email" name="email" type="text" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-email .form-section -->',

'website' => '' .
'<label for="website">' . __( 'Website:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="website" name="website" type="text" value="' . esc_attr( $commenter['comment_website'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',

'url' => '' ) ),

'comment_field' => '' .
'<label for="comment">' . __( 'Comment:' ) . '</label>' .
'<textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea>' .
'<!-- #form-section-comment .form-section -->',
'comment_notes_after' => '',
);
comment_form($comment_args);


If you have suggestions for a better way to set up the form, I am all ears. But this seems to work, so I'll assume it's ok.

On the output side, I added the following code to <strong>functions.php</strong> and it successfully outputted the default fields, but not the new "website" field that I added in "comments_extra.php". Therefore my question is how do I pull the "website" field output from comments_extra.php into functions.php or wherever I should put it?

function mytheme_comment($comment, $args, $depth) {
$GLOBALS['comment'] = $comment;
extract($args, EXTR_SKIP);

if ( 'div' == $args['style'] ) {
$tag = 'div';
$add_below = 'comment';
} else {
$tag = 'li';
$add_below = 'div-comment';
}
?>
<<?php echo $tag ?> <?php comment_class( empty( $args['has_children'] ) ? '' : 'parent' ) ?> id="comment-<?php comment_ID() ?>">
<?php if ( 'div' != $args['style'] ) : ?>
<div id="div-comment-<?php comment_ID() ?>" class="comment-body">
<?php endif; ?>


<div class="comment-author vcard">
<?php if ( $args['avatar_size'] != 0 ) echo get_avatar( $comment, $args['avatar_size'] ); ?>
<?php printf( __( '<cite class="fn">%s</cite> <span class="says">says:</span>' ), get_comment_author_link() ); ?>
</div>

<div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ); ?>">
<?php
/* translators: 1: date, 2: time */
printf( __('%1$s at %2$s'), get_comment_date(), get_comment_time() ); ?></a><?php edit_comment_link( __( '(Edit)' ), ' ', '' );
?>
</div>

<?php comment_text(); ?>

<div class="reply">
<?php comment_reply_link( array_merge( $args, array( 'add_below' => $add_below, 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
</div>
<?php if ( 'div' != $args['style'] ) : ?>
</div>
<?php endif; ?>
<?php
}


Dbranes comments:

You could also use the 'comment_form_default_fields' filter in the same way as I use the comment_form_defaults' filter in the Gist Example.

For example like:

add_filter( 'comment_form_default_fields', 'wpq_custom_comment_form_fields' );
comments_template( '/comments_extra.php', true );
remove_filter( 'comment_form_default_fields', 'wpq_custom_comment_form_fields' );


where you define <em>wpq_custom_comment_form_fields()</em> accordingly.

Do you include the comment templates as:

...
comments_template( ''', true ); // in the above code for form #1
...
comments_template( '/comments_extra.php', true ); //in the above code for form #2
...

?

Regarding <em>mytheme_comment()</em> you could use <em>print_r( $comment );</em> within that function, to see the available data.

But I'm not sure where you call <em>mytheme_comment()</em>, ... as a callback for <em>wp_list_comments()</em> ?


dirtcastle comments:

Below, I have also included my code from <strong>single.php</strong>, so that you can see all three relevant files. I am calling <em>mytheme_comment()</em> When I place the <em>print_r( $comment );</em> in <strong>functions.php</strong>, I get all of the output, except the new "website" field that I added in the <strong>comments_extra.php</strong>. Everything is working great except this extra new field.

<strong>1. single.php</strong> below...
// ----------------------
// Extra Comment Form #2:
//
add_action( 'comment_form', 'wpq_hidden_input_field_to_set_extra_comment_content_type' );
add_filter( 'comments_array', 'wpq_show_only_extra_comments' );
add_filter( 'comment_form_defaults', 'wpq_change_comment_form_defaults' );

// Comments template:
comments_template( '/comments_extra.php', true );

remove_action( 'comment_form', 'wpq_hidden_input_field_to_set_extra_comment_content_type' );
remove_filter( 'comments_array', 'wpq_show_only_extra_comments' );
remove_filter( 'comment_form_defaults', 'wpq_change_comment_form_defaults' );

// -----------------------
// Normal Comment Form #1:
//
add_filter( 'comments_array', 'wpq_show_only_normal_comments' );

// Comments template:
comments_template( '', true );

remove_filter( 'comments_array', 'wpq_show_only_normal_comments' );


<strong>2. comments_extra.php</strong> below...

$commenter = wp_get_current_commenter();
$req = get_option( 'require_name_email' );
$aria_req = ( $req ? " aria-required='true'" : '' );

$comment_args = array( 'fields' => apply_filters( 'comment_form_default_fields', array(
'author' => '' .
'<label for="author">' . __( 'Your Name:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="author" name="author" type="text" value="' .
esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-author .form-section -->',

'email' => '' .
'<label for="email">' . __( 'Your Email:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="email" name="email" type="text" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-email .form-section -->',

'website' => '' .
'<label for="website">' . __( 'Website:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="website" name="website" type="text" value="' . esc_attr( $commenter['comment_website'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',

'url' => '' ) ),

'comment_field' => '' .
'<label for="comment">' . __( 'Comment:' ) . '</label>' .
'<textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea>' .
'<!-- #form-section-comment .form-section -->',
'comment_notes_after' => '',
);
comment_form($comment_args);


<strong>3. functions.php</strong> below...

function mytheme_comment($comment, $args, $depth) {
$GLOBALS['comment'] = $comment;
extract($args, EXTR_SKIP);

if ( 'div' == $args['style'] ) {
$tag = 'div';
$add_below = 'comment';
} else {
$tag = 'li';
$add_below = 'div-comment';
}
?>
<<?php echo $tag ?> <?php comment_class( empty( $args['has_children'] ) ? '' : 'parent' ) ?> id="comment-<?php comment_ID() ?>">
<?php if ( 'div' != $args['style'] ) : ?>
<div id="div-comment-<?php comment_ID() ?>" class="comment-body">
<?php endif; ?>


<div class="comment-author vcard">
<?php if ( $args['avatar_size'] != 0 ) echo get_avatar( $comment, $args['avatar_size'] ); ?>
<?php printf( __( '<cite class="fn">%s</cite> <span class="says">says:</span>' ), get_comment_author_link() ); ?>
</div>


<div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ); ?>">
<?php
/* translators: 1: date, 2: time */
printf( __('%1$s at %2$s'), get_comment_date(), get_comment_time() ); ?></a><?php edit_comment_link( __( '(Edit)' ), ' ', '' );
?>
</div>

<div class="test" style="border:1px solid;">
<?php print_r( $comment ); ?>
</div>


<?php comment_text(); ?>

<div class="reply">
<?php comment_reply_link( array_merge( $args, array( 'add_below' => $add_below, 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
</div>
<?php if ( 'div' != $args['style'] ) : ?>
</div>
<?php endif; ?>
<?php
}


Thanks again for all your help!! By the way, I am happy to top up my Ā¢ontribution again, in return for all your hard work. :-) Or I can post a "part 2" question if you think finding a solution is beyond the scope of my original question.


dirtcastle comments:

Sorry, I left out the part about how I called <em>mytheme_comment()</em> in <strong>comments_extra.php</strong>. Here it is...

wp_list_comments( array( 'callback' => 'mytheme_comment' ) );


Dbranes comments:

Hi @dirtcastle, after digging through your code files for a while, I think you need this modification of your code:

$comment_args = array( 'fields' => apply_filters( 'comment_form_default_fields', array(
'author' => '' .
'<label for="author">' . __( 'Your Name:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="author" name="author" type="text" value="' .
esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-author .form-section -->',
'email' => '' .
'<label for="email">' . __( 'Your Email:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="email" name="email" type="text" value="' . esc_attr( $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-email .form-section -->',
'url' => '' .
'<label for="website">' . __( 'My Website:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="website" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',
) ),
'comment_field' => '' .
'<label for="comment">' . __( 'Comment:' ) . '</label>' .
'<textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea>' .
'<!-- #form-section-comment .form-section -->',
'comment_notes_after' => '',
);


to collect the website url.

Just let me know how it goes.


dirtcastle comments:

For some reason, when I call it 'url', it doesn't work

'url' => '' .
'<label for="website">' . __( 'My Website:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="website" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',


But when I change it to anything BUT 'url', it works. For example...

'anything' => '' .
'<label for="website">' . __( 'My Website:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="website" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',


Therefore, I think it is safe to assume that the t erm 'url' has been specifically blocked or turned off somewhere. Or it requires something that isn't present in our code here.

Just so that I'm being clear, I am looking for a way to create totally new fields in the comment form. I don't want to be limited to the standard name/email/url/comment. I want to be able to add additional custom comment fields.


dirtcastle comments:

To be clear, when I say the term 'anything' works, I mean that the field shows up in the form. But the contents of that field do not get outputted when the comment is posted.


Dbranes comments:

To add custom fields to the comment form, we need to store the data in the comment meta table.

Let's consider some extra input field, like <em>city</em>.

Then you could add this into your form setup in the <em>comments-extra.php</em> file:

'wpq_field_city' => '' .
'<label for="city">' . __( 'City:' ) . '</label> ' .
( $req ? '<span class="required">*</span>' : '' ) .
'<input id="city" name="wpq_field_city" type="text" value="" size="30"' . $aria_req . ' />' .
'<!-- #form-section-website .form-section -->',


Then we need to grap it after the comment insert, and save the value into the comment meta table.

Here's an example for the <em>functions.php</em> file:

function wpq_after_insert_comment_to_add_meta( $id, $comment )
{
$city = filter_input( INPUT_POST, 'wpq_field_city', FILTER_SANITIZE_STRING );

if( $city )
{
update_comment_meta( $id, 'wpq_field_city', $city );
}

return $id;
}

add_filter( 'wp_insert_comment', 'wpq_after_insert_comment_to_add_meta', 99, 2 );



To display the city value, you need to add this part:

$city = get_comment_meta( $comment->comment_ID, 'wpq_field_city', true );
echo 'city: ' . $city;


into your <em>mytheme_comment()</em> function.

Just let me know if you need more info on this.


dirtcastle comments:

That worked! Thank you.

The contents of the new field are now being displayed. And I think I will be able to repeat this approach for more fields. I appreciate your explaining the process. Now I understand a little better how the data goes back and forth from the table.

On a side note, I was unable to get the extra comment count working.

echo wpq_get_comment_count_by_type( $type = array( '', 'pingback', 'trackback' ), 3522 );
echo wpg_get_comment_count_by_type( $type = array( 'extra' ), 3522 );


The pingback/trackback is returning 0 (correct). But "extra" is returning the total comment count (default + extra), and not the extra-only comment count. Any ideas on how to fix that?


Dbranes comments:

ok great,

aha, I forgot about the problem of using IN within prepare.

So I worked out a new version of the counting function:

/**
* Count comments by type
*
* @version 0.0.2
* @uses global $wpdb
* @param array $types
* @param int $post_id
* @return array $comments
*/

function wpq_get_comment_count_by_type( $types = array(), $post_id )
{
global $wpdb;

// Default
if ( ! is_array( $types ) || 0 == count( $types ) )
{
$types = array( '' );
}

// Construct the IN ( ... ) part
$tmp = array();
foreach( $types as $type )
{
$tmp[] = '%s';
}
$in = join( ',', $tmp );

// Use the vsprintf part of prepare:
$data = $types;
$data[] = $post_id;

$sql = $wpdb->prepare(
"SELECT COUNT(*) AS cc FROM {$wpdb->comments} WHERE comment_type IN ({$in}) AND comment_post_ID = %d AND comment_approved = 1 ",
$data
);

return $wpdb->get_var( $sql );
}


I will update the gist accordingly.


Dbranes comments:

Here's yet another extended version:

/**
* Count comments by type and approval
*
* @version 0.0.3
* @uses global $wpdb
* @param array $type
* @param int $post_id
* @param int $approved
* @return array $comments
*/

function wpq_get_comments_count( $types = array(), $post_id, $approved = 1 )
{
global $wpdb;

// Default
if ( ! is_array( $types ) || 0 == count( $types ) )
{
$types = array( '' );
}

// Construct the IN ( ... ) part
$tmp = array();
foreach( $types as $type )
{
$tmp[] = '%s';
}
$in = join( ',', $tmp );

// Use the vsprintf part of prepare:
$data = $types;
$data[] = $post_id;
$data[] = $approved;

$sql = $wpdb->prepare(
"SELECT COUNT(*) AS cc FROM {$wpdb->comments} WHERE comment_type IN ({$in}) AND comment_post_ID = %d AND comment_approved = %d ",
$data
);

return $wpdb->get_var( $sql );
}


<strong>Usage Examples:</strong>

echo ' Approved normal comments: ' . wpq_get_comments_count( array( '', 'pingback', 'trackback' ), 3522 );
echo ' Approved extra comments: ' . wpq_get_comments_count( array( 'extra' ), 3522, 1 );



dirtcastle comments:

I replaced the previous wpq_get_comment_count() with your latest version, and updated the extra comment template.

Unfortunately, it is still adding the posts from both forms together and outputting the total, instead of just the extra count.

Wish I could hack around with it and help you out, but the function mechanics are way beyond me.


dirtcastle comments:

On a side note, I found out how to show additional custom fields when logged in (because only one field is shown by default).

add_action( 'comment_form_logged_in_after', 'mm_extra_comment_fields_logged_in',10,2);
function mm_extra_comment_fields_logged_in($commenter,$user_identity){
global $mm_extra_fields;
foreach($mm_extra_fields as $mm_extra_fields_html){
echo $mm_extra_fields_html."\n";
}
}


The global <em>$mm_extra_fields</em> refers to the form fields array in extra comment template. I found it in the following tutorial.

[[LINK href="http://www.ballyhooblog.com/custom-fields-wordpres-comments/"]]http://www.ballyhooblog.com/custom-fields-wordpres-comments/[[/LINK]]

I just wanted to post this for people down the road. I had seen the <em>comment_form_logged_in_after</em> in the actions list for <em>comment_form()</em>. But there wasn't any documentation for it.

2014-08-15

Remy answers:

I'm not sure you will be able to do that, every comment is linked to a post id in the database, so you can't have separate comments thread with the default system.

You would need to simulate the comment system with custom fields and a custom post type which would store the alternative comments

2014-08-15

timDesain Nanang answers:

try this code in single.php:


<?php get_header(); ?>

<?php while ( have_posts() ) : the_post(); ?>
<?php the_title(); ?>
<?php the_content(); ?>

<?php comments_template( '', true ); ?>
<?php endwhile; // end of the loop. ?>

<?php
$postid = 1; //

$args = array('post_id' => $postid );
$comments = get_comments($args);
foreach($comments as $comment) :
echo('<br />'.$comment->comment_author . ': ' . $comment->comment_content);
endforeach;

$comments_args = array();
comment_form( $comments_args, $postid );
?>

<?php get_footer(); ?>


<strong>the codex:</strong>
http://codex.wordpress.org/Function_Reference/get_comments
http://codex.wordpress.org/Function_Reference/wp_list_comments
http://codex.wordpress.org/Function_Reference/comment_form