Contents
Overview
This article explains, in exhaustive detail, how to add custom fields to the WordPress comment form and validate, sanitize and persist those fields in PHP. It covers: where to inject fields, how to enforce server-side validation safely (nonces and sanitization), how to save fields to comment meta when a comment is stored, how to show the data in the front-end and admin, and optional client-side validation and styling. All code examples are ready to paste into a themes functions.php or a small plugin file.
Key concepts and hooks used
You will use these WordPress hooks and functions:
- comment_form_default_fields — filter the default author/email/url fields array.
- comment_form_after_fields, comment_form_logged_in_after — action hooks to output extra form markup.
- pre_comment_on_post — action to validate input before the comment is inserted.
- comment_post — action to act after a comment is stored (save comment meta).
- add_comment_meta / update_comment_meta / get_comment_meta — store and retrieve custom fields.
- manage_edit-comments_columns and manage_comments_custom_column — extend the Comments admin list columns.
- comment_text or a custom walker for comments — display fields with comments on the front-end.
Security and sanitization rules
Always validate on the server. Client-side JavaScript can improve UX, but it is not a security control. Use nonces for anti-CSRF when appropriate, then:
- Use sanitize_text_field() for single-line text data.
- Use sanitize_email() for email addresses, esc_attr() and esc_html() when outputting.
- Use wp_die() with a clear, localized error message and back_link to return the user to the form on validation failure.
Example: Add a phone number field and a consent checkbox
We will add two fields: a required phone number and an optional consent checkbox. Implementation steps:
- Insert fields into the comment form markup (for both logged-in and not-logged-in states).
- Insert a nonce for CSRF protection.
- Validate required values on pre_comment_on_post.
- Save the validated data on comment_post into comment meta.
- Show the phone number in front-end comment output and create an admin column.
Full PHP implementation (place in functions.php or a simple plugin)
This example shows adding fields, nonce output, validating, storing meta, and adding an admin column and front-end display.
// Insert phone into the fields array after url (example).
new_fields = array()
foreach (fields as key => value) {
new_fields[key] = value
if (url === key) {
new_fields[phone] = phone_field
}
}
return new_fields
}
add_filter(comment_form_default_fields, mytheme_add_comment_fields, 20)
/ Output the consent checkbox and the nonce for CSRF protection for all visitors (including logged-in). /
function mytheme_comment_form_after_fields() {
// Consent checkbox (optional).
echo
// Nonce field for verifying the form submission.
wp_nonce_field(my_comment_nonce_action, my_comment_nonce_field, true, true)
}
add_action(comment_form_after_fields, mytheme_comment_form_after_fields)
add_action(comment_form_logged_in_after, mytheme_comment_form_after_fields) // for logged-in users
/
2) Server-side validation: pre_comment_on_post
Validate the phone as required and check nonce.
If invalid, use wp_die() with back_link => true so WordPress returns the user to the form.
/
function mytheme_validate_comment_fields(comment_post_id) {
// Only validate on standard comment submit (comment_post will call this earlier).
if ( wp_doing_ajax() ) {
// Optionally handle Ajax separately
}
// Verify nonce - check it exists and is valid.
if ( empty(_POST[my_comment_nonce_field]) ! wp_verify_nonce( sanitize_text_field( wp_unslash( _POST[my_comment_nonce_field] ) ), my_comment_nonce_action ) ) {
wp_die( esc_html__(Security check failed: invalid form submission., textdomain), esc_html__(Comment Submission Error, textdomain), array( back_link => true ) )
}
// Phone required: check presence.
if ( empty( _POST[phone] ) ) {
wp_die( esc_html__(Please enter your phone number., textdomain), esc_html__(Missing Phone, textdomain), array( back_link => true ) )
}
// Sanitize and validate phone number format.
phone_raw = wp_unslash( _POST[phone] )
phone = sanitize_text_field( phone_raw )
// Simple regex example: digits, spaces, parentheses, plus and hyphen are allowed. Adjust to your locale.
if ( ! preg_match( /^[0-9 -() .]{6,20}/, phone ) ) {
wp_die( esc_html__(Please enter a valid phone number., textdomain), esc_html__(Invalid Phone, textdomain), array( back_link => true ) )
}
}
add_action(pre_comment_on_post, mytheme_validate_comment_fields)
/
3) Save the validated fields to comment meta after the comment is inserted.
The comment_post hook passes the new comment ID and the comment approved status.
/
function mytheme_save_comment_meta(comment_id, comment_approved) {
// Save phone (always sanitize before saving).
if ( isset( _POST[phone] ) ) {
phone = sanitize_text_field( wp_unslash( _POST[phone] ) )
add_comment_meta( comment_id, phone, phone, true )
}
// Save consent checkbox as 1 or 0.
consent = isset( _POST[comment_consent] ) 1 === _POST[comment_consent] ? 1 : 0
add_comment_meta( comment_id, comment_consent, consent, true )
}
add_action(comment_post, mytheme_save_comment_meta, 10, 2)
/
4) Display phone with a comment on the front-end (append to comment text)
Use comment_text filter to append sanitized, escaped phone.
/
function mytheme_append_phone_to_comment_text(comment_text, comment = null) {
if ( null === comment ) {
return comment_text
}
phone = get_comment_meta( comment->comment_ID, phone, true )
if ( ! empty( phone ) ) {
phone_escaped = esc_html( phone )
// Example output adjust markup to match your theme.
comment_text .= . esc_html__(Phone:, textdomain) . . phone_escaped .
}
return comment_text
}
add_filter(comment_text, mytheme_append_phone_to_comment_text, 10, 2)
/
5) Add a Phone column to the admin Comments list.
/
function mytheme_comments_columns(columns) {
new_columns = array()
foreach (columns as key => value) {
new_columns[key] = value
if ( author === key ) {
new_columns[phone] = esc_html__(Phone, textdomain)
}
}
return new_columns
}
add_filter(manage_edit-comments_columns, mytheme_comments_columns)
/ Render the phone column content /
function mytheme_comments_custom_column(column, comment_ID) {
if ( phone === column ) {
phone = get_comment_meta(comment_ID, phone, true)
if ( ! empty(phone) ) {
echo esc_html(phone)
} else {
echo mdash
}
}
}
add_action(manage_comments_custom_column, mytheme_comments_custom_column, 10, 2)
?>
Client-side validation (optional)
Use JavaScript to validate inputs before submission to improve user experience. Remember client-side checks do not replace server-side validation.
// Example JavaScript: simple phone required validation.
// Enqueue this script properly in a theme/plugin via wp_enqueue_script.
document.addEventListener(DOMContentLoaded, function() {
var commentForm = document.getElementById(commentform)
if (!commentForm) return
commentForm.addEventListener(submit, function(e) {
var phoneField = document.getElementById(phone)
if (phoneField) {
var phone = phoneField.value.trim()
if (!phone !/^[0-9 -() .]{6,20}/.test(phone)) {
e.preventDefault()
alert(Please enter a valid phone number.)
phoneField.focus()
return false
}
}
}, false)
})
Styling the new fields (optional)
Add CSS to your theme to make the new inputs match your design.
/ Example CSS /
.comment-form-phone input {
width: 100%
max-width: 320px
}
.comment-phone {
margin-top: .5em
font-size: .95em
color: #333
}
Hook and filter summary table
| Hook / Filter | Purpose |
| comment_form_default_fields | Alter or inject inputs into the default author/email/url fields for non-logged-in users. |
| comment_form_after_fields | Echo additional markup after the default fields (non-logged-in). |
| comment_form_logged_in_after | Echo additional markup for logged-in users after the comment fields. |
| pre_comment_on_post | Server-side validation before comment is inserted. Use wp_die() on failure. |
| comment_post | Save comment meta after the comment is created. |
| comment_text | Filter comment output to display saved meta in front-end. |
| manage_edit-comments_columns / manage_comments_custom_column | Add and render custom admin columns for comments list. |
Additional best practices considerations
- Use nonces for the comment form if you are adding fields that must be protected against forged posts. For high-traffic sites or APIs consider rate-limiting or honeypot controls.
- Sanitize and escape consistently. sanitize_ when saving, esc_ when showing.
- Localization — wrap user-facing strings in translation functions (e.g., esc_html__(), esc_attr__(), __()).
- Accessibility — include label elements and aria attributes where appropriate.
- Spam handling — metadata you add can affect spam detection validate and sanitize data before passing to third-party spam detectors.
- Compatibility — test with comment plugins (comment pagination, nesting, custom forms). If a theme or plugin already customizes comment_form(), ensure your hooks run after theirs (adjust priority).
- Multisite and cache — meta storage is per comment so it’s safe, but cached comment lists may not reflect newly saved meta until cache invalidation.
Troubleshooting common issues
- Fields not showing: Ensure your theme calls comment_form() and not a custom template that hardcodes fields. If the theme uses a custom form layout, add your fields directly into that template or use the appropriate hooks the theme exposes.
- Validation not firing: pre_comment_on_post will only fire when WordPress handles the POST if a plugin is intercepting and handling comment submission via AJAX, ensure validation code is executed in the same request or handle AJAX separately.
- Nonce fails: Make sure you output the nonce in the form and check it on submission. For caching systems, ensure the comment form page isnt cached with stale nonces prefer server-side verification and short-lived nonces if the page is dynamic.
- Data not saved: Hook priority: comment_post is the correct hook make sure you bind with appropriate priority and that you are checking isset(_POST[phone]) when saving.
Example variations
- Add the field only for non-logged-in users by adding logic to the field filter or action: if ( is_user_logged_in() ) return.
- Use update_comment_meta() instead of add_comment_meta to allow updates if the same meta key exists.
- Store structured data (like JSON) in meta if you need multiple values but prefer separate meta keys where possible for query simplicity.
References
|
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |
