Deluxe Blog Tips About Projects

How To Create A Better Meta Box In WordPress (Part 2)

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.

Note: the term ‘post type’ is used here not very exactly, for example ‘link’ is not a post type. It’s the type of editing page, which includes post type. But we accept this for easy understanding.

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 and save_post for save meta box’s data.
  • The add, show, save functions are just the converted version of old functions mytheme_add_box, mytheme_show_box and mytheme_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:

How To Create A Better Meta Box In WordPress Post Editing Page
How To Create A Better Meta Box In WordPress (Part 2)
Meta Box Script Update V3.0

Subscribe to the Newsletter

Subscribe to get my latest content by email. I won't send you spam. Unsubscribe at any time.