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 🙂 |