commentfield-150x150

Adding Input Fields To Comment Form

Most comment forms contain the same input fields: Name, Email, URL and the comment text field. This is sufficient for most use cases but there are situations where you might want to know a bit more about your commenter: their age, the city they live in, or the color of their underwear. This article explains how to add an input field, store the data in the database and how to display the additional data in your blog if your theme uses the comment_form() function provided by WordPress.

Adding the input field

As an example we will add an input field asking for the city the commenter lives in. Additionally the field will be marked as mandatory.

To add an additional input field you have to modify the comment form defaults by setting a filter for the function comment_form_defaults:

add_filter( 'comment_form_defaults', 'change_comment_form_defaults');

For adding the new input field after the email field you need to extend the default "Email" input field:

function change_comment_form_defaults( $default ) {
    $commenter = wp_get_current_commenter();	
    $default[ 'fields' ][ 'email' ] .= '<p class="comment-form-author">' .
        '<label for="city">'. __('City') . '</label>
        <span class="required">*</span>
        <input id="city" name="city" size="30" type="text" /></p>';
    return $default;
}

Basically this is the same markup used to create the author field with modified values for label, name, and id and "<span class="required">*</span>" added to mark the field as mandatory (only visually).

Storing the data in the database

Storing the value of the new field is quite simple since WordPress already contains a table in the database called commentmeta for storing exactly this type of data called meta data.
The usage of this table is straightforward since WordPress provides the two functions add_comment_meta() and get_comment_meta():

add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false)
get_comment_meta( $comment_id, $meta_key, $single = false )

To store the meta data together with the other comment data an action needs to be added:

add_action( 'comment_post', 'save_comment_meta_data' );

function save_comment_meta_data( $comment_id ) {
    add_comment_meta( $comment_id, 'city', $_POST[ 'city' ] );
}

To make the city field mandatory the script needs to check if the user had entered some text before the data is stored. WordPress provides a filter named preprocess_comment which can be used to verify the data:

add_filter( 'preprocess_comment', 'verify_comment_meta_data' );

function verify_comment_meta_data( $commentdata ) {
    if ( ! isset( $_POST['city'] ) )
        wp_die( __( 'Error: please fill the required field (city).' ) );
    return $commentdata;
}

The data is not actually processed but it is tested if the field $_POST['city'] contains any data. If there is no data the script will die with an error message otherwise the comment data will be returned unaltered.
Alternative ist the Action Hook pre_comment_on_post usable.

Retrieving and displaying the data

To retrieve the data from the database WordPress provides the function get_comment_meta. To avoid to modify the theme template the city name is attached to the author name by using the filter get_comment_author_link:

add_filter( 'get_comment_author_link', 'attach_city_to_author' );

function attach_city_to_author( $author ) {
    $city = get_comment_meta( get_comment_ID(), 'city', true );
    if ( $city )
        $author .= " ($city)";
    return $author;
}

Advanced usage

You might have noticed that both functions add_comment_meta() and get_comment_meta() contain parameters that haven't been explained yet, namely $unique and $single.

The $unique parameter controls whether you can add multiple data field with the same key name for the same comment_id. But why would you want do such a thing?
Let's assume you want to allow the user to add multiple cities separated by commas. Surely you can store the whole string in a single table row but that would make the data hard to handle if you want to use it in a different context and it simply conflicts with basic database principles. So the input data will be split and each city is stored in its own dataset:

function save_comment_meta_data( $comment_id ) {
    $cities = explode( ',', $_POST[ 'city' ] );
    foreach ( $cities as $city )
       echo add_comment_meta( $comment_id, 'city', $city, false );
}

If the user enters e.g. three city names all three are stored with the keyname city. If the parameter unique would have been set to true only the first city would have been stored because the parameter prevents the storage attempts of the last two city names.

When retrieving the data with get_comment_meta the parameter $single comes into view. If it is set to false the function does not return a single value but an array of values.

function attach_city_to_author( $author ) {
    $cities = get_comment_meta( get_comment_ID(), 'city', false );
    if ( $cities ) {
        $author .= ' ( ';
        foreach ( $cities as $city )
            $author .= $city . ' ';
        $author .= ')';
    }
    return $author;
}

Modifying the meta data

There might be situations where you want to change the meta data stored in the database. As expected WordPress provides a function for this called update_comment_meta():

update_comment_meta( $comment_id, $meta_key, $meta_value, $prev_value );

To modify a value you use the function the same way you use add_comment_meta:

$new_city = "Timbuktu";
update_comment_meta( $comment_id, 'city', $new_city );

If the $meta_key does not exist the function adds the meta data to the database. But what is the parameter $prev_value used for?
As noticed above you can store more than one value with the same meta key for a single comment. The code snippet above would change all values with the key 'city'. If you set the parameter $prev_value to the value that is to be modified the function will do exactly what is intended:

$old_city = "Berlin";
$new_city = "Timbuktu";
update_comment_meta( $comment_id, 'city', $new_city, $old_city );

Behind the scenes

The structure of the commentmeta table is quite simple:

meta_id comment_id meta_key meta_value
bigint(20) bigint(20) varchar(255) longtext

The meta_id is an auto-incremented ID and comment_id is of course the ID of the comment. The actual data is stored in the meta_key and meta_value fields, containing the keyname (in our case 'city') and the actual value (the name of the city the user entered). Example:

meta_id comment_id meta_key meta_value
45 76 city Berlin
46 77 city Hamburg
47 78 city Hamburg
48 78 city Berlin
49 78 city Mainz

The comments with the ID 76 and 77 contain meta data with only one value assigned to the key 'city' whereas the comment with the ID 78 got three different values with the same key.

Look it up

If you want to look up the {add|get|update}_meta_data functions in the core you can find them in .../wp-includes/comment.php.
The default values of the comment form which are modified above are defined in ../wp-includes/comment-form.php in the function comment_form().

Source code

Download the complete sources of the examples above:

Comments are closed.

7 comments

  1. Andrew Nacin

    Great concept, but a few issues. One, I think there's a typo in the first code sample, as it says " $default[ 'fields' ][ 'email' ]" rather than "city".

    Second, this code is insecure. It does no checking on the city value, and therefore would allow any commenter to inject scripts into the page. Very, very dangerous.

    You'll want to run the same functions that are run against the name (which is the pre_comment_author_name filter):

    add_filter( $filter, 'sanitize_text_field' );
    add_filter( $filter, 'wp_filter_kses' );
    add_filter( $filter, '_wp_specialchars', 30 );

  2. Frank

    @Nacin: many thanks for post this hint. We add always filter to all _POST-values, but here was a simpler code. But i think, this is important; many users copy only the code, without thinking.
    Current i add the functions from formatting.php to the _POST, nit via filtter, but this is a great idea and better for read the code.

  3. Nic

    Cool tutorial! I looked around for ages for something like this. I had to make a few minor adjustments to get it to work. Based on Otto's tutorial, but I came through in the end! Thank you!

    This example displays fields to users not logged in. What I would like to know is how can I get my input field to display for logged in users?

  4. Justin Germino

    Really cool tutorial and the one major problem from doing this as usual when modifying WP default files is if a user automatic upgrades, all your changes will have to be redone in the upgraded version.

3 pingbacks

  1. wpmag.com - WordPress News, Themes, Tutorials, Plugins, Questions, ...
  2. Linkschleuder 23: WordPress und CSS3 | WordPress & Webwork
  3. Weekly WordPress Review ยป WPCanada