Update: The script was turned into Meta Box WordPress plugin and is being developed. Visit plugin page to see more info and download latest version.
After I wrote the article How To Create A Better Meta Box In WordPress Post Editing Page, I received some questions that asked me how to create multiple meta boxes for multiple post types (in WordPress 3.0). So I decide to improve the script to do that.
Note: It's strongly recommended to read the first part, because I won't take your time by explaining all variables' meanings. They can be found in the first part.
Multiple Post Types
Since version 3.0, WordPress allows us to create custom post types, each post type can have it own meta boxes. So, to register multiple post types for each meta box, we can do like that:
$meta_box = array( 'id' => 'my-meta-box-1', 'title' => 'Custom meta box 1', 'pages' => array('post', 'page', 'link'), // multiple post types, accept custom post types 'context' => 'normal', 'priority' => 'high', 'fields' => array( array( 'name' => 'Text box', 'desc' => 'Enter something here', 'id' => $prefix . 'text', 'type' => 'text', 'std' => 'Default value 1' ) ) );
To add this meta box to multiple editing screens for multiple post types, we just call the function add_meta_box
multiple times (in function mytheme_add_box
) like this:
// Add meta box function mytheme_add_box() { global $meta_box; foreach ($meta_box['pages'] as $page) { add_meta_box($meta_box['id'], $meta_box['title'], 'mytheme_show_box', $page, $meta_box['context'], $meta_box['priority']); } }
This function will loop all the registered post types, and for each post type create a meta box with given data.
Multiple Meta Boxes
1. Define multiple meta boxes
For creating multiple meta boxes, we first need to declare them. We'll use the array()
syntax like this:
$meta_boxes = array(); // first meta box $meta_boxes[] = array( 'id' => 'my-meta-box-1', 'title' => 'Custom meta box 1', 'pages' => array('post', 'page', 'link'), // multiple post types 'context' => 'normal', 'priority' => 'high', 'fields' => array( array( 'name' => 'Text box', 'desc' => 'Enter something here', 'id' => $prefix . 'text', 'type' => 'text', 'std' => 'Default value 1' ) ) ); // second meta box $meta_boxes[] = array( 'id' => 'my-meta-box-2', 'title' => 'Custom meta box 2', 'pages' => array('post', 'link'), // custom post type 'context' => 'normal', 'priority' => 'high', 'fields' => array( array( 'name' => 'Select box', 'id' => $prefix . 'select', 'type' => 'select', 'options' => array('Option 1', 'Option 2', 'Option 3') ) ) );
All the parameters I've explained carefully in the first part. You might want to take a look if you forget.
Here, I declared each meta box as an element of a big array $meta_boxes
. I separate meta box's definition to make it easy to scan. Of course, you can declare like this:
$meta_boxes = array( array( 'id' => 'my-meta-box-1', 'title' => 'Custom meta box 1', 'pages' => array('post', 'page', 'link'), // multiple post types 'context' => 'normal', 'priority' => 'high', 'fields' => array( array( 'name' => 'Text box', 'desc' => 'Enter something here', 'id' => $prefix . 'text', 'type' => 'text', 'std' => 'Default value 1' ) ) ), array( 'id' => 'my-meta-box-2', 'title' => 'Custom meta box 2', 'pages' => array('post', 'link'), // custom post type 'context' => 'normal', 'priority' => 'high', 'fields' => array( array( 'name' => 'Select box', 'id' => $prefix . 'select', 'type' => 'select', 'options' => array('Option 1', 'Option 2', 'Option 3') ) ) ) );
Just the style, don't waste time in there.
2. Wrap all our code to a class
When first time I tried to create multiple meta boxes, I wanted to use the foreach loop to walkthrough the $meta_boxes
and for each meta box, I would use the same function to create, show it and save it's data.
Unfortunately, the function mytheme_show_box
, used for displaying meta box cannot handle multiple meta boxes' data. It can display only one meta box at one time.
So, I think about using a class, that take the meta box's data and deal with it. We can use it as in the following code:
foreach ($meta_boxes as $meta_box) { $my_box = new My_meta_box($meta_box); }
All old functions are now wrapped in My_meta_box
class as in the following code:
class My_meta_box { protected $_meta_box; // create meta box based on given data function __construct($meta_box) { $this->_meta_box = $meta_box; add_action('admin_menu', array(&$this, 'add')); add_action('save_post', array(&$this, 'save')); } /// Add meta box for multiple post types function add() { foreach ($this->_meta_box['pages'] as $page) { add_meta_box($this->_meta_box['id'], $this->_meta_box['title'], array(&$this, 'show'), $page, $this->_meta_box['context'], $this->_meta_box['priority']); } } // Callback function to show fields in meta box function show() { global $post; // Use nonce for verification echo '<input type="hidden" name="mytheme_meta_box_nonce" value="', wp_create_nonce(basename(__FILE__)), '" />'; echo '<table class="form-table">'; foreach ($this->_meta_box['fields'] as $field) { // get current post meta data $meta = get_post_meta($post->ID, $field['id'], true); echo '<tr>', '<th style="width:20%"><label for="', $field['id'], '">', $field['name'], '</label></th>', '<td>'; switch ($field['type']) { case 'text': echo '<input type="text" name="', $field['id'], '" id="', $field['id'], '" value="', $meta ? $meta : $field['std'], '" size="30" style="width:97%" />', '<br />', $field['desc']; break; case 'textarea': echo '<textarea name="', $field['id'], '" id="', $field['id'], '" cols="60" rows="4" style="width:97%">', $meta ? $meta : $field['std'], '</textarea>', '<br />', $field['desc']; break; case 'select': echo '<select name="', $field['id'], '" id="', $field['id'], '">'; foreach ($field['options'] as $option) { echo '<option', $meta == $option ? ' selected="selected"' : '', '>', $option, '</option>'; } echo '</select>'; break; case 'radio': foreach ($field['options'] as $option) { echo '<input type="radio" name="', $field['id'], '" value="', $option['value'], '"', $meta == $option['value'] ? ' checked="checked"' : '', ' />', $option['name']; } break; case 'checkbox': echo '<input type="checkbox" name="', $field['id'], '" id="', $field['id'], '"', $meta ? ' checked="checked"' : '', ' />'; break; } echo '<td>', '</tr>'; } echo '</table>'; } // Save data from meta box function save($post_id) { // verify nonce if (!wp_verify_nonce($_POST['mytheme_meta_box_nonce'], basename(__FILE__))) { return $post_id; } // check autosave if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return $post_id; } // check permissions if ('page' == $_POST['post_type']) { if (!current_user_can('edit_page', $post_id)) { return $post_id; } } elseif (!current_user_can('edit_post', $post_id)) { return $post_id; } foreach ($this->_meta_box['fields'] as $field) { $old = get_post_meta($post_id, $field['id'], true); $new = $_POST[$field['id']]; if ($new && $new != $old) { update_post_meta($post_id, $field['id'], $new); } elseif ('' == $new && $old) { delete_post_meta($post_id, $field['id'], $old); } } } }
This class has 3 methods and one constructor:
- The
constructor
will take the data and save it into protected variable of class for further using. It also hooks to 2 actions:admin_menu
for adding meta box andsave_post
for save meta box's data. - The
add
,show
,save
functions are just the converted version of old functionsmytheme_add_box
,mytheme_show_box
andmytheme_save_data
. In these functions, instead of access the global variable$meta_box
(as in the old code), we use the protected variable of the class$this->_meta_box
.
That's all. Now you can create meta boxes as many as you want, and each meta box can be displayed in multiple editing pages (multiple post types). For easy using the code, I also create a file that includes all we need. What you have to do is just include it into your functions.php
file and re-define the meta boxes in the very top of the file.
This post is the 2nd part of meta box series:
Leave a Reply