WordPress comments have a companion meta table — wp_commentmeta — that works identically to wp_postmeta and wp_usermeta. You can attach any key-value data to a comment with add_comment_meta() or update_comment_meta(), and retrieve it with get_comment_meta(). Out of the box the comment form collects a name, email, URL, and free-text body. Adding custom fields — a product rating (1–5 stars), an RSVP status, a “verified purchase” flag, or a topic selector — requires three coordinated steps: rendering the extra fields in the comment form HTML, saving the submitted values on comment submission, and reading them back in the comments template. WordPress provides action hooks at each step. This pattern is used by WooCommerce itself for product star ratings, and it is the correct approach for any form extension that attaches structured data to individual comments.
Problem: You need a star rating field (1–5) in the comment form so readers can rate a post, store the rating as comment meta, and display the average rating alongside the comment count.
Solution: Add the rating field via the comment_form_fields filter, save it with update_comment_meta() on the comment_post action, read individual ratings with get_comment_meta(), and calculate averages with a $wpdb query.
<?php
// ── 1. Add rating field to the comment form ────────────────────────────
add_filter( 'comment_form_fields', 'add_rating_field_to_comment_form' );
function add_rating_field_to_comment_form( $fields ) {
$fields['rating'] = '
<p class="comment-form-rating">
<label>' . esc_html__( 'Rating', 'textdomain' ) . '</label>
<span class="stars">
' . implode( '', array_map( function ( $i ) {
return sprintf(
'<label>
<input type="radio" name="rating" value="%1$d" required>
<span aria-label="%2$s stars">%3$s</span>
</label>',
$i, $i, str_repeat( '★', $i ) . str_repeat( '☆', 5 - $i )
);
}, range( 1, 5 ) ) ) . '
</span>
</p>';
return $fields;
}
// ── 2. Save the rating when a comment is submitted ─────────────────────
add_action( 'comment_post', 'save_comment_rating', 10, 2 );
function save_comment_rating( $comment_id, $comment_approved ) {
if ( isset( $_POST['rating'] ) ) {
$rating = absint( $_POST['rating'] );
if ( $rating >= 1 && $rating <= 5 ) {
update_comment_meta( $comment_id, 'star_rating', $rating );
}
}
}
// ── 3. Display rating in comments template ────────────────────────────
add_filter( 'comment_text', 'prepend_rating_to_comment' );
function prepend_rating_to_comment( $comment_text ) {
$rating = (int) get_comment_meta( get_comment_ID(), 'star_rating', true );
if ( $rating ) {
$stars = str_repeat( '★', $rating ) . str_repeat( '☆', 5 - $rating );
$comment_text = '<p class="comment-rating" aria-label="' . esc_attr( $rating ) . ' out of 5">'
. esc_html( $stars ) . '</p>' . $comment_text;
}
return $comment_text;
}
// ── 4. Calculate average rating for a post ────────────────────────────
function get_average_comment_rating( $post_id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare(
"SELECT AVG( CAST( meta_value AS UNSIGNED ) )
FROM {$wpdb->commentmeta} cm
INNER JOIN {$wpdb->comments} c ON cm.comment_id = c.comment_ID
WHERE cm.meta_key = 'star_rating'
AND c.comment_post_ID = %d
AND c.comment_approved = '1'",
$post_id
) );
}
NOTE: When a comment is submitted via AJAX (as in some themes and WooCommerce), the standard comment_post action still fires — so the meta save logic works without modification. However, comment form field HTML added via comment_form_fields is not automatically included in AJAX comment submissions by all themes; test that the rating field value is present in $_POST when comments are submitted asynchronously.