Advertisement

Create a Simple CRM in WordPress: Creating Custom Fields

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

In this series, we've been looking at how to create a simple CRM system in WordPress.  In the first part of this series, we created a WordPress Plugin that registered a 'Contacts' Custom Post Type, but we've yet to cover how to store additional information for a Contact.

Creating Custom Fields

WordPress has the add_meta_box() function, which allows plugin and theme developers to register custom meta boxes against various WordPress Administration screens.

WordPress registers some of its own meta boxes for display when you create a Post or a Page. For example, on Pages, you have the Page Attributes meta box:

Let’s add a meta box to our Contacts custom post type. Open the plugin file you created in the first tutorial of this series. Then, in the plugin’s constructor, update the code to match the below. This registers our register_meta_boxes() function against the add_meta_boxes action:

/**
* Constructor. Called when plugin is initialised
*/
function WPTutsCRM() {

	add_action( 'init', array( $this, 'register_custom_post_type' ) );
	add_action( 'add_meta_boxes', array( $this, 'register_meta_boxes' ) ); 
    
}

Next, in our register_meta_boxes() function, we add a call to add_meta_box(). This tells WordPress we want a meta box called Contact Details, which is rendered by our output_meta_box() function. Add the below code after the constructor function:

/**
* Registers a Meta Box on our Contact Custom Post Type, called 'Contact Details'
*/
function register_meta_boxes() {
	add_meta_box( 'contact-details', 'Contact Details', array( $this, 'output_meta_box' ), 'contact', 'normal', 'high' );	
}

Finally, we need an output_meta_box() function, which is called by add_meta_box above. Add the below code after the register_meta_boxes() function:

/**
* Output a Contact Details meta box
*
* @param WP_Post $post WordPress Post object
*/
function output_meta_box( $post ) {

}

Let’s check we have a meta box appearing on our Contacts custom post type. Create a new Contact in the WordPress dashboard by going to Contacts > Add New.

If everything has been written correctly, you should see something similar to the follow screenshot:

Populating the Meta Box With a Field

Let’s go ahead and add an email address field to this meta box. Change your output_meta_box function to the below code:

/**
* Output a Contact Details meta box
*
* @param WP_Post $post WordPress Post object
*/
function output_meta_box( $post 
) {
	// Output label and field
    echo ('<label for="contact_email">' . __( 'Email Address', 'tuts-crm' ) . '</label>' );
	echo ('<input type="text" name="contact_email" id="contact_email" value="'.esc_attr( $email ).'" />' );
    
}

Save your plugin code, and reload the Add Contact screen. You should see our new Email Address field appear in the Contact Details meta box:

Saving Custom Field Data

We’re not quite done yet. We need to tell WordPress to save the content that a user inputs into this field. In WordPress, we do this by registering a function against the save_post action.

As with most actions, we’ll register our action in the plugin’s constructor:

/**
* Constructor. Called when plugin is initialised
*/
function WPTutsCRM() {

	add_action( 'init', array( $this, 'register_custom_post_type' ) );
	add_action( 'add_meta_boxes', array( $this, 'register_meta_boxes' ) ); 
	add_action( 'save_post', array( $this, 'save_meta_boxes' ) );
    
}

Next, let’s create our save_meta_boxes() function:

/**
* Saves the meta box field data
*
* @param int $post_id Post ID
*/
function save_meta_boxes( $post_id ) {

	// Check this is the Contact Custom Post Type
	if ( 'contact' != $_POST['post_type'] ) {
		return $post_id;
	}

	// Check the logged in user has permission to edit this post
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return $post_id;
	}

	// OK to save meta data
	$email = sanitize_text_field( $_POST['contact_email'] );
	update_post_meta( $post_id, '_contact_email', $email );
    
}

This function performs several actions, because the save_post action can be called quite frequently by WordPress and other plugins (for example, when periodically autosaving a draft, or a different Post Type is saved). We need to be sure that we only save our custom field data if the user saved or updated a Contact.

If we are saving a Contact, we sanitize the email address. From the WordPress Codex:

Checks for invalid UTF-8, Convert single < characters to entity, strip all tags, remove line breaks, tabs and extra white space, strip octets.

In short, we make sure there’s no funky formatting on our text string.

Finally, we store the email address in the post meta data, using update_post_meta. Think of post meta as a series of key/value pairs that are attached to a post. You can have as few or as many as you like. In our example, we’re storing the value of our custom field against the key _contact_email.

Reading Custom Field Data

Create a new Contact and enter an email address. Save the new Contact, and you’ll notice that the email address doesn’t appear in the field:

We need to edit our output_meta_box() function to read the Post meta, and display it in the input field. Change the output_meta_box() function to the below code:

/**
* Output a Contact Details meta box
*
* @param WP_Post $post WordPress Post object
*/
function output_meta_box( $post ) {

	$email = get_post_meta( $post->ID, '_contact_email', true );
	
	// Output label and field
    echo ( '<label for="contact_email">' . __( 'Email Address', 'tuts-crm' ) . '</label>' );
	echo ( '<input type="text" name="contact_email" id="contact_email" value="' . esc_attr( $email ) . '" />' );
    
}

We use get_post_meta() to get the value for the given Post ID and meta key combination. We know the meta key is _contact_email, as that’s what we used when we saved the custom field value in update_post_meta()

Security

Security is extremely important when submitting and handling form data. We need to know that the source of our data is trustworthy when saving it. If we cannot trust the source of our data, we must not store it – the data may be compromised or corrupted in a way to try and exploit a bug or security flaw.

WordPress provides us with nonces (a “number used once”), that can be sent along with the form data. This nonce can be checked when our save routine runs, to ensure it matches the value we’d expect.

This helps prevent cross-site request forgery (CSRF) attacks i.e. someone trying to submit form data to our save routine from a different web site.

We need to add in security to the above code in two places:

  1. output_meta_box(): add a nonce value to the form
  2. save_meta_boxes(): verify a submitted nonce value

Let’s edit the output_meta_box() function, replacing it with the below code:

/**
* Output a Contact Details meta box
*
* @param WP_Post $post WordPress Post object
*/
function output_meta_box( $post ) {

    $email = get_post_meta( $post->ID, '_contact_email', true );
	
	// Add a nonce field so we can check for it later.
	wp_nonce_field(' save_contact', 'contacts_nonce' );
	
	// Output label and field
	echo ( '<label for="contact_email">' . __( 'Email Address', 'tuts-crm' ) . '</label>' );
	echo ( '<input type="text" name="contact_email" id="contact_email" value="' . esc_attr( $email ) . '" />' );
    
}

This uses wp_nonce_field(), to generate a hidden field called contacts_nonce, with an action called save_contact. Its value is generated by WordPress.

Next, let’s edit the save routine in save_meta_boxes():

/**
* Saves the meta box field data
*
* @param int $post_id Post ID
*/
function save_meta_boxes( $post_id ) {

	// Check if our nonce is set.
	if ( ! isset( $_POST['contacts_nonce'] ) ) {
		return $post_id;	
	}

	// Verify that the nonce is valid.
	if ( ! wp_verify_nonce( $_POST['contacts_nonce'], 'save_contact' ) ) {
		return $post_id;
	}

	// Check this is the Contact Custom Post Type
	if ( 'contact' != $_POST['post_type'] ) {
		return $post_id;
	}

	// Check the logged in user has permission to edit this post
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return $post_id;
	}

	// OK to save meta data
	$email = sanitize_text_field( $_POST['contact_email'] );
	update_post_meta( $post_id, '_contact_email', $email );
    
}

This adds two checks to our save routine:

  1. Check that a nonce field has been set in our form. If not, don’t save anything.
  2. Check that the nonce field’s value is what we expect. If not, don’t save anything.

Create or edit your Contact, and make sure that the email address is now being saved.

Up Next…

In the next article, we’re going to use Advanced Custom Fields to add custom fields to our Contact custom post type, allowing us to create a rich user interface with a wider range of input types.

Advertisement