WordPress Custom Post Type Meta Box with Form Validation

So I’m going to cover a lot in a little time in this tutorial so pay close attention and you’ll learn fast I hope.

add_car

What You’ll Learn

  • How to create a simple WordPress plugin
  • How to create a custom post type in WordPress
  • How to add custom fields to a custom post type
  • How to add meta boxes to wp-admin
  • How to save custom post type’s custom fields
  • How to handle form validation for custom meta boxes
  • How to display errors in wp-admin

So while it might seem like a lot to cover, I will demonstrate all of the above points with a simple WordPress plugin that adds a custom post type to WordPress and validates the user’s input.

Here are the steps you’ll follow:

  1. Start a new plugin
  2. Add a new custom post type
  3. Add meta boxes to a custom post type
  4. Add WordPress Error Handler Functions
  5. Write PHP Function to Save Custom Fields Data Meta Boxes

Step One – Start a new plugin

Create a folder on your desktop, pick a name for your plugin and name the folder accordingly. Lets say for the purpose of making this tutorial, we’ll name our plugin “Cars” so we created a folder named “cars” on our desktop. Then add a php file named cars.php and make enter the proper comments at the top of the cars.php file so WordPress will recognize your new plugin. Here’s how I did it:

<?php
/**
 * Plugin Name: Cars
 * Plugin URI: http://jafty.com/blog/crash-course-on-wordpress-plugin-development/
 * Description: A custom Car plugin developed for WordPress car websites.
 * Version: 0.0.1
 * Author: Ian L. of Jafty.com
 * Author URI: http://jafty.com
 * License: GPL2
 */

?>

Feel free to change the name , Plugin URI, Description, etc to fit your desires.

Step Two – Add a Custom Post Type

Next we need to add code to cars.php that will create a new Custom Post Type or CPT in wordpress. Here’s example code to create the cars CPT:

//Add cars Custom Post Type:
add_action(‘init’, ‘create_cars_cpt’);
function create_cars_cpt() {
    $labels = array(    
      ‘name’ => _x( ‘cars’, ‘cars’ ),
      ‘singular_name’ => _x( ‘car’, ‘cars’ ),
      ‘add_new’ => _x( ‘Add New car’, ‘cars’ ),
      ‘add_new_item’ => _x( ‘Add New car’, ‘cars’ ),
      ‘edit_item’ => _x( ‘Edit car’, ‘cars’ ),
      ‘new_item’ => _x( ‘New car’, ‘cars’ ),
      ‘view_item’ => _x( ‘View car’, ‘cars’ ),
      ‘search_items’ => _x( ‘Search cars’, ‘cars’ ),
      ‘not_found’ => _x( ‘No cars found’, ‘cars’ ),
      ‘not_found_in_trash’ => _x( ‘No cars found in Trash’, ‘cars’ ),
      ‘parent_item_colon’ => _x( ‘Parent car:’, ‘cars’ ),
      ‘menu_name’ => _x( ‘cars’, ‘cars’ ),
   );
   
   
 $args = array(
      ‘labels’ => $labels,
      ‘hierarchical’ => false,
      ‘description’ => ‘cars’,
      ‘supports’ => array( ‘title’, ‘author’ ),//author adds the author metabox to cars CPT add car/edit car screens.
      ‘public’ => true,
      ‘show_ui’ => true,
      ‘show_in_menu’ => true,
      ‘show_in_nav_menus’ => true,
      ‘publicly_queryable’ => true,
      ‘exclude_from_search’ => false,
      ‘has_archive’ => true,
      ‘query_var’ => true,
      ‘can_export’ => true,
      ‘rewrite’ => array(‘slug’ => ‘car’),
      ‘map_meta_cap’  => true,
   );

    register_post_type( ‘cars’, $args );
    //flush_rewrite_rules();
}//end create_cars_cpt function

Simply add the above code in green to your cars.php file after the comment area but before then closing PHP tag and save the file.

Step Three – Add simple Meta Box to Our Plugin

Here is my code for reference which you may copy, paste and change as needed:

//Add the Cars Meta Boxes:
add_action( ‘add_meta_boxes’, ‘add_cars_metaboxes’ );
function add_cars_metaboxes() {
add_meta_box(‘create_car_metabox’, ‘Car Information’, ‘create_car_metabox’, ‘cars’, ‘normal’, ‘high’);
}
      
//############################## Function to create the meta box: ########################
function create_car_metabox($p=”) {
global $post;
// Noncename needed to verify where the data originated
echo ‘<input type=”hidden” name=”eventmeta_noncename” id=”eventmeta_noncename” value=”‘ .
wp_create_nonce(plugin_basename(__FILE__)) . ‘” />’;

$car_year = get_post_meta($p, ‘_car_year’, true);
$car_make = get_post_meta($p, ‘_car_make_time’, true);
$car_model = get_post_meta($p, ‘_car_model_time’, true);

if(isset($post->ID)){
//get post meta for car:
$car_year = get_post_meta($post->ID, ‘_car_year’, true);
$car_make = get_post_meta($post->ID, ‘_car_make_time’, true);
$car_model = get_post_meta($post->ID, ‘_car_model_time’, true);
}//end if post ID is set

//add HTML for meta box form fields:
?>
<div style=”float:left”>
<span class=’field-head’>Car Year:</span> <select name=”car_year”>
<option value=”none”>Select Year</option>
<option value=”1990″<?php if($car_year==”1990″)echo ” selected”;?>>1990</option>
<option value=”1991″<?php if($car_year==”1991″)echo ” selected”;?>>1991</option>
<option value=”1992″<?php if($car_year==”1992″)echo ” selected”;?>>1992</option>
</select>
</div>

<div style=”float:left”>
<span class=’field-head’>Car Make:</span> <select name=”car_make”>
<option value=”none”>Select Make</option>
<option value=”Ford”<?php if($car_make==”Ford”)echo ” selected”;?>>Ford</option>
<option value=”Chevy”<?php if($car_make==”Chevy”)echo ” selected”;?>>Chevy</option>
<option value=”Dodge”<?php if($car_make==”Dodge”)echo ” selected”;?>>Dodge</option>
</select>
</div>

<div style=”float:left”>
<span class=’field-head’>Car Model:</span> <select name=”car_model”>
<option value=”none”>Select Model</option>
<option value=”1990″<?php if($car_model==”1990″)echo ” selected”;?>>1990</option>
<option value=”1991″<?php if($car_model==”1991″)echo ” selected”;?>>1991</option>
<option value=”1992″<?php if($car_model==”1992″)echo ” selected”;?>>1992</option>
</select>
</div>
<?php

}//end create_car_metabox function to create car tabs

Paste the above code in your cars.php file right after the last line of PHP code but before the closing PHP tag.

Step Four – Add Error Handling Functions to Your Plugin:

Now we need a couple quick functions to handle errors later on, so copy and paste the following code into your cars.php file next just after the last code you added from step three above:

//Next we need two simple error handling functions:
//Error handling function for use with save_car_data function below it:
add_action(‘admin_notices’, ‘handle_car_errors’);
/**
 * Writes an error message to the screen if error is thrown in save_car_data function
 *
 */
function handle_car_errors() {
  //If there are no errors, then exit the function
  if(!( $errors = get_transient(‘settings_errors’))) {
    return;
  }
  //Otherwise, build the list of errors that exist in the settings errors
  $message = ‘<div id=”acme-message” class=”error below-h2″><p><ul>’;
  foreach($errors as $error) {
    $message .= ‘<li>’ . $error[‘message’] . ‘</li>’;
  }
  $message .= ‘</ul></p></div><!– #error –>’;
  //Write error messages to the screen
  echo $message;
  //Clear and the transient and unhook any other notices so we don’t see duplicate messages
  delete_transient(‘settings_errors’);
  remove_action(‘admin_notices’, ‘handle_car_errors’);
}

function car_error($slug,$err){
    add_settings_error(
        $slug,
        $slug,
        $err,
        ‘error’
    );
    set_transient(‘settings_errors’, get_settings_errors(), 30);
}//end car_error function by Ian L. of Jafty.com

Step Five – Save Meta Box Data in WordPress

Now that we have our error handling functions ready, we can write in our function to save the data. You can copy and paste my save_car_data function and it’s action hook as follows:

//############### SAVE ALL ABOVE METADATA FROM META BOXES HERE: ##################
//hook to save the meta box data:
//add_action(‘save_post’, ‘save_touchpoint_data’);
add_action(‘save_post_cars’, ‘save_car_data’);
    
function save_car_data($post_id) {
    //get the car post’s title title and verify it first:
    $ti = get_the_title($post_id);
    //if the title is blank, throw an error:
    if($ti==”)car_error(‘title_missing’,’Oops! You forgot to enter a title. Please enter a valid car title and click the update button.’);

    //save the car’s year:
    //first make sure they picked a year:
    $y = $_POST[‘car_year’];
    if($y == ‘none’){
        car_error(‘missing_car_year’,’Opps! You have not selected a year for the car. Please select a year and click the update button to save it afterwards.’);
    }else{
    update_post_meta($post_id, ‘_car_year’, strip_tags($y));
    }
    
    //save the car’s make:
    //first make sure they picked a make:
    $y = $_POST[‘car_make’];
    if($y == ‘none’){
        car_error(‘missing_car_make’,’Opps! You have not selected a make for the car. Please select a make and click the update button to save it afterwards.’);
    }else{
    update_post_meta($post_id, ‘_car_make’, strip_tags($y));
    }
    
    //save the car’s model:
    //first make sure they picked a model:
    $y = $_POST[‘car_model’];
    if($y == ‘none’){
        car_error(‘missing_car_model’,’Opps! You have not selected a model for the car. Please select a model and click the update button to save it afterwards.’);
    }else{
    update_post_meta($post_id, ‘_car_model’, strip_tags($y));
    }
}//end save_car_data function

That’s all there is to it! Copy and paste the above code into your cars.php file just before the closing PHP tag and save the file, then upload the entire cars folder to your site’s plugins directory and navigate to wp-admin and click on “plugins” then find your new plugin and click “activate” to activate it and you will see the new custom post type, “Cars” appear in the main wp-admin nav that runs down the left side of the page.

Screen Shots:

Here is what it will look like if you just try to click on “Publish” when adding a new car without entering any values into the add new car page at all:

errors

Below is what the add new Car screen looks like:

add_car

Summary:

There you have it! A simple plugin with everything you need to get you started creating your own custom post types in WordPress that include custom fields, meta boxes and even error handling code! A WordPress beginner’s dream! Good Luck with your next theme or plugin!

How to add Custom Admin Meta Boxes to a WordPress Custom Post Type

I decided to write this detailed tutorial on how to add custom field meta boxes to the WordPress admin mostly because I couldn’t find a decent description of how to do it anywhere online. Hopefully this will help others having trouble trying to learn how to add admin meta boxes with custom fields for a custom post type in WordPress. It is an essential step to learning WordPress plugin development.

Let’s dive in and learn some code because I believe that is the quickest and easiest way to learn this advanced WordPress method. I say it is an advanced subject because it is a little complex for beginners. If you are not sure how to start your own plugin yet in WordPress, then this topic may be too advanced for you. You should at least learn how to make a very basic WordPress plugin first, so feel free to read one of my more basic WordPress plugin tutorials first.

First of all, I’ll show you how to add a single meta box with a single field in it. It is also important to know that you can have more than one field inside a single meta box. It is equally important to know that you can have more than one meta box. Each meta box can have one or more fields in them. It just depends on your individual needs. Below is the basic code for how to add a custom post type with a custom field inside of an admin meta box. I’ll also include a basic plugin heading so you may follow along and create a working example to work from:

Adding a Custom Post Type to WordPress Plugin

<?php
/**
 * Plugin Name: Jafty Metabox Example Plugin
 * Plugin URI: http://jafty.com/blog/crash-course-on-wordpress-plugin-development/
 * Description: A Plugin that adds metaboxes to your WordPress blog or site.
 * Version: 1.0
 * Author: Ian L. of Jafty.com
 * Author URI: http://jafty.com
 * License: GPL2
 */
 

 //Add new Custom Post Type named videos:
add_action(‘init’, ‘create_video_posttype’);
function create_video_posttype(){
    register_post_type(‘videos’,
        array(
            ‘labels’ => array(
            ‘name’ => __(‘Videos’),
            ‘singular_name’ => __(‘Video’)
            ),
        ‘public’ => true,
        ‘has_archive’ => true,
        ‘rewrite’ => array(‘slug’ => ‘video’),
        )
    );
}//end create_video_posttype function

?>

Okay, now start a .php file and add the above code in green to the start of it. Name the file “jafty-metabox-example-plugin.php” and save it. Please NOTE: it is important to go into wp-admin/settings/permalinks and re-save those settings after activating a plugin with a custom post type or after adding a custom post type to a plugin. If you do not do this, the custom post type posts will not show up on the front end of your blog.

At this point, you should be able to upload the jafty-metabox-example-plugin.php file to your WordPress site’s plugins folder and activate it. If it works, you’ll see a new “Video” option in the left navigation menu of wp-admin as in the following photo:

videoCPT

Add a Metabox with a Custom Field to your CPT

Now here is the code to add to the end of the file you started above(just before the closing PHP tag) that will add a meta-box with a custom field inside of it to your plugin. It adds a field to the wp-admin edit screen for the videos CPT(custom post type):

//Add custom admin Meta Boxes:
add_action(‘add_meta_boxes’, ‘add_ians_metaboxes’);
function add_ians_metaboxes(){
//add meta box for Video type and description:
add_meta_box(‘meta_box_html_id’, ‘Video Details’, ‘video_details_function’, ‘videos’, ‘normal’, ‘high’);
}

//##################### Video Details Metabox: ################
function video_details_function() {
global $post;  
//Noncename needed to verify where the data originated
echo ‘<input type=”hidden” name=”eventmeta_noncename” id=”eventmeta_noncename” value=”‘ .
wp_create_nonce(plugin_basename(__FILE__)) . ‘” />’;

//Get video description data if its already been entered
$video_desc = get_post_meta($post->ID, ‘_video_desc’, true);
    
//Start video Description text field HTML:
?>
Video Description:
<input type=”text” name=”video_desc” value=”<?php echo $video_desc; ?>” class=”widefat” />
<?php
}//end video_details_function

Save the “jafty-metabox-example-plugin.php” file and update it on your site and click on the “Video” link in wp-admin and then click on “Add New” to see the add/edit screen for your new CPT. You will notice your  custom meta-box with the heading of “Video Details”. You will also notice the meta-box contains a custom field named “Video Description”. You can see both near the bottom of the photo below:

customField

Saving Custom Field Data

Your not quite done however. While the code above will display the metaboxes, it does not save the data as I soon discovered. To save the data, you’ll need to add this code directly after your video_details callback function:

//hook into save_post to save the meta box data:
add_action (‘save_post’, ‘save_video_desc’);

function save_video_desc($post_id) {
//verify the metadata is set
     if (isset( $_POST[‘video_desc’])) {
     //save the metadata
     update_post_meta ($post_id, ‘_video_desc’, strip_tags($_POST[‘video_desc’]));
     }
}

 

 Adding Multiple Meta-Boxes and Custom Fields

Next I’ll show you what our final plugin file would look like if we were to add another custom field to our meta-box and then another meta-box with yet another custom field inside of it. This way you can see how more than one meta-box is added and also how to add more than one field inside of a single meta-box. Here is the complete plugin code containing a total of two meta-boxes and three total custom fields:

<?php
/**
* Plugin Name: Jafty Metabox Example Plugin
* Plugin URI: http://jafty.com/blog/crash-course-on-wordpress-plugin-development/
* Description: A Plugin that adds metaboxes to your WordPress blog or site.
* Version: 1.0
* Author: Ian L. of Jafty.com
* Author URI: http://jafty.com
* License: GPL2
*/

//Add new Custom Post Type named videos:
add_action(‘init’, ‘create_video_posttype’);
function create_video_posttype(){
register_post_type(‘videos’,
array(
‘labels’ => array(
‘name’ => __(‘Videos’),
‘singular_name’ => __(‘Video’)
),
‘public’ => true,
‘has_archive’ => true,
‘rewrite’ => array(‘slug’ => ‘video’),
)
);
}//end create_video_posttype function

//Add custom admin Meta Boxes:
add_action(‘add_meta_boxes’, ‘add_ians_metaboxes’);
function add_ians_metaboxes(){
//add the first meta box for Video type and description:
add_meta_box(‘meta_box_html_id’, ‘Video Details’, ‘video_details_function’, ‘videos’, ‘normal’, ‘high’);
//add a second meta box for Video Rating
add_meta_box(‘meta_box_html_id2’, ‘Video Rating’, ‘video_rating_function’, ‘videos’, ‘normal’, ‘high’);
}
//################## Video Details Metabox: ####################
function video_details_function() {
global $post;

//Noncename needed to verify where the data originated
echo ‘<input type=”hidden” name=”eventmeta_noncename” id=”eventmeta_noncename” value=”‘ .
wp_create_nonce(plugin_basename(__FILE__)) . ‘” />’;

//Get the video type  if its already been entered
$video_type = get_post_meta($post->ID, ‘_video_type’, true);
// Get the video description if its already been entered
$video_desc = get_post_meta($post->ID, ‘_video_desc’, true);

//Start type select field HTML:
?>
Video Type: <select name=”video_type”>
<option value=”DVD”<?php if($video_type==”DVD”)echo ” selected”;?>>DVD<option>
<option value=”Blueray”<?php if($video_type==”Blueray”)echo ” selected”;?>>Blueray<option>
</select><br />
<?php
// Start video description field HTML:
?>
Video Description:
<input type=”text” name=”_location” value=”<?php echo $video_desc; ?>” class=”widefat” />
<?php
}//end video_details_function
//##################### Video Rating Metabox: ###################
function video_rating_function() {
global $post;

//Noncename needed to verify where the data originated
echo ‘<input type=”hidden” name=”eventmeta_noncename” id=”eventmeta_noncename” value=”‘ .
wp_create_nonce(plugin_basename(__FILE__)) . ‘” />’;

//Get the start_month data if its already been entered
$video_rating = get_post_meta($post->ID, ‘_video_rating’, true);

//Start type select field HTML:
?>
Video Type: <select name=”_video_rating”>
<option value=”R”<?php if($video_rating==”R”)echo ” selected”;?>>R<option>
<option value=”PG”<?php if($video_rating==”PG”)echo ” selected”;?>>PG<option>
</select><br />
<?php
}//end video_rating_function
?>

//add your save data hook and function to save all fields here:

//hook into save_post to save the meta box data:
add_action (‘save_post’, ‘save_video_data’);

function save_video_data($post_id) {
//verify the metadata is set
     if (isset( $_POST[‘video_desc’])) {
     //save the metadata
     update_post_meta ($post_id, ‘_video_type’, strip_tags($_POST[‘video_type’]));

    update_post_meta ($post_id, ‘_video_desc’, strip_tags($_POST[‘video_desc’]));

    update_post_meta ($post_id, ‘_video_rating, strip_tags($_POST[‘video_rating’]));
     }
}

That’s it! Save the above code in a PHP file and name it the same name as before and allow it to overwrite the original plugin file if you were following along and it will add another meta-box and a couple more custom fields to your custom post type. Now you should have enough information to be able to make your own custom admin meta-boxes and fields for your next WordPress plugin or theme. Here is an image of the two meta-boxes from when I tested the above code myself:

metaboxes

Summary

Now you should know more than enough to make your own plugin or custom theme with its own admin meta-boxes and custom fields for any custom post types you may have. It is important to know that you can also use any other type of form element in the above examples. Feel free to experiment and use textareas, radio buttons, checkboxes etc in your own code.