Recently, I wanted to try to build a custom form because the design was quite complicated and using Contact Form 7 would force me to add a bunch of HTML in the Contact Form 7 options. Ofcourse, when clients are modifying forms, they can make mistakes, breaking the designs. This is what we want to prevent.
A functions.php or a plugin to load in some functions
How to
Step by step instruction summary
Create Custom Post type with Custom Post type UI
This is very simple, in the plugin interface you can name your custom post type slug and other information.
Create the fields in ACF
We need the fields in ACF so that they are displayed in the Admin interface. They need to match with the fields you have in the form. Standard is ofcourse, Email, Name, Message, Phone Number
Create the HTML for the Form
Create the HTML based on the form fields that you require
Create the JS for submitting the form
We need some JS to verify the content, nonce and do the AJAX post request to the REST API
Handle the incoming request
In the REST callback we can make sure to store the incoming data and do some additional validation if needed.
1. Create custom post type
In CPT UI create the CPT with contact_request or another name that you prefer.
2. Create the ACF fields for that Custom Post Type
In order for the fields to show on the Custom Post type you need to create a new field group, and assign it to your newly created Custom Post Type.
Make sure to note down your fields.
3. Create the HTML form
You can create the HTML form with simple html first to validate if everything is working. Use the same names for your input names as your ACF fields. That will give things simple. See below an example. Note that there are several additional informations coming from the page ACF fields here such as “the_field( ‘privacy_policy_text’ )”. You can exchange them with your own text or ACF field.
<textarea name="message" id="message"class="form-control" cols="30" rows="15" placeholder="<?php _e('What would you like to tell us?', 'understrap') ?>"></textarea>
We have the form now, but we need to submit it with AJAX post in jQuery. We need a nonce verification as well, so we need to send that as well along with the data.
This is the JS file that I used for the contact form. Note that it is an example, you can exchange the field names for what fields you use. Mine is based on the above HTML form. Note also this only works with one form on one page. It’s specifically for a contact page.
;(function( $, window, undefined ){
'use strict'
var current_domain = window.location.hostname;
var contact_url = 'https://'+window.location.hostname+'/wp-json/xyz/v1/contact_form/';
;( function( $, window, undefined ) {
'use strict'
var current_domain = window.location.hostname;
var contact_url = 'https://'+window.location.hostname+'/wp-json/xyz/v1/contact_form/';
var contact_form = $('.contact-form');
// newsletter
// TODO decide on which pages this should work?
contact_form.submit(function(e){
e.preventDefault();
if($('input[name="honeyput"]').val() !== ""){
$( ".result" ).html("Oops");
return;
}
var data = new FormData();
data.append('first_name', $('input[name="first_name"]').val());
data.append('last_name', $('input[name="last_name"]').val());
data.append('subject', $('input[name="subject"]').val());
data.append('email', $('input[name="email"]').val());
data.append('topic', $('select[name="topic"] option:selected').val());
data.append('message', $('textarea[name="message"]').val());
data.append('privacy_policy', $('input[name="privacy_policy"]').val());
// TODO make sure checkbox is valid
// TODO make sure email is valid.. So it doesnt even go to post
$.ajax({
url: contact_url,
method: "post",
processData: false,
contentType: false,
data: data,
beforeSend: function (xhr) {
xhr.setRequestHeader('X-WP-Nounce', $('input[name="nonce"]'));
$('.button-field button').addClass('loading')
},
success: function (data) {
//success
$('.button-field button').removeClass('loading');
$( ".contact-form .result" ).html( data.message );
},
error: function (e) {
$( ".contact-form .result" ).html( e.message );
$('.button-field button').removeClass('loading');
}
});
});
} ( jQuery, window ) );
;( function( $, window, undefined ) {
'use strict'
var current_domain = window.location.hostname;
var contact_url = 'https://'+window.location.hostname+'/wp-json/xyz/v1/contact_form/';
var contact_form = $('.contact-form');
// newsletter
// TODO decide on which pages this should work?
contact_form.submit(function(e){
e.preventDefault();
if($('input[name="honeyput"]').val() !== ""){
$( ".result" ).html("Oops");
return;
}
var data = new FormData();
data.append('first_name', $('input[name="first_name"]').val());
data.append('last_name', $('input[name="last_name"]').val());
data.append('subject', $('input[name="subject"]').val());
data.append('email', $('input[name="email"]').val());
data.append('topic', $('select[name="topic"] option:selected').val());
data.append('message', $('textarea[name="message"]').val());
data.append('privacy_policy', $('input[name="privacy_policy"]').val());
// TODO make sure checkbox is valid
// TODO make sure email is valid.. So it doesnt even go to post
$.ajax({
url: contact_url,
method: "post",
processData: false,
contentType: false,
data: data,
beforeSend: function (xhr) {
xhr.setRequestHeader('X-WP-Nounce', $('input[name="nonce"]'));
$('.button-field button').addClass('loading')
},
success: function (data) {
//success
$('.button-field button').removeClass('loading');
$( ".contact-form .result" ).html( data.message );
},
error: function (e) {
$( ".contact-form .result" ).html( e.message );
$('.button-field button').removeClass('loading');
}
});
});
} ( jQuery, window ) );
5. REST API: Handle the incoming request
Now we have everything in place to actually handle the incoming post request. We need to initialise the rest api route and the callback so that we can insert the custom post with all the ACF field data.
$response_info = array('message' =>__('Thank you for your contact request. We will be in touch with you soon', 'understrap'), 'success' =>true);
$response = newWP_REST_Response($response_info);
$response->set_status(200);
return $response;
}
function contact_form($request){
$request['privacy_policy'];
$args = array(
'post_title' => sanitize_text_field($request['first_name']).' '.sanitize_text_field($request['last_name']), // set the title however you want
'post_excerpt' => '',
'post_type' => 'contact_application', // this the slug for my CPT
'post_status' => 'publish',
);
$post_id = wp_insert_post($args);
$first_name = sanitize_text_field($request['first_name']);
$last_name = sanitize_text_field($request['last_name']);
$email = sanitize_email($request['email']);
$topic = sanitize_text_field($request['topic']);
$subject = sanitize_text_field($request['subject']);
$message = sanitize_textarea_field($request['message']);
update_field('first_name', $first_name, $post_id);
update_field('last_name', $last_name, $post_id);
update_field('email', $email, $post_id);
update_field('topic', $topic, $post_id);
update_field('subject', $subject, $post_id);
update_field('message', $message, $post_id);
$data = [
'title' => 'Contact Form Request',
'first_name' => $first_name,
'last_name' => $last_name,
'subject' => $subject,
'message' => $message,
'topic' => $topic,
'email' => $email,
'site_url' => site_url(),
'edit_link' => get_edit_post_link( $post_id, '' )
];
$response_info = array('message' => __('Thank you for your contact request. We will be in touch with you soon', 'understrap'), 'success' => true);
$response = new WP_REST_Response($response_info);
$response->set_status(200);
return $response;
}
function contact_form($request){
$request['privacy_policy'];
$args = array(
'post_title' => sanitize_text_field($request['first_name']).' '.sanitize_text_field($request['last_name']), // set the title however you want
'post_excerpt' => '',
'post_type' => 'contact_application', // this the slug for my CPT
'post_status' => 'publish',
);
$post_id = wp_insert_post($args);
$first_name = sanitize_text_field($request['first_name']);
$last_name = sanitize_text_field($request['last_name']);
$email = sanitize_email($request['email']);
$topic = sanitize_text_field($request['topic']);
$subject = sanitize_text_field($request['subject']);
$message = sanitize_textarea_field($request['message']);
update_field('first_name', $first_name, $post_id);
update_field('last_name', $last_name, $post_id);
update_field('email', $email, $post_id);
update_field('topic', $topic, $post_id);
update_field('subject', $subject, $post_id);
update_field('message', $message, $post_id);
$data = [
'title' => 'Contact Form Request',
'first_name' => $first_name,
'last_name' => $last_name,
'subject' => $subject,
'message' => $message,
'topic' => $topic,
'email' => $email,
'site_url' => site_url(),
'edit_link' => get_edit_post_link( $post_id, '' )
];
$response_info = array('message' => __('Thank you for your contact request. We will be in touch with you soon', 'understrap'), 'success' => true);
$response = new WP_REST_Response($response_info);
$response->set_status(200);
return $response;
}
Conclusion
This is a simple foolproof way to use the REST API above the wp_admin handlers. I prefer it, I always found the WP_ADMIN solution a little limited. Ofcourse, it works as well with Nonce. This is also quite interesting, since in React applications, you’ll need to access the nonce from somewhere. I’ve made an article about that as well as well as a longer tutorial with example code.
[…] can very well be combined with the other post on a custom contact form, then you can receive directly an email when a user submits a […]