Ultimate Guide to Meta Boxes in WordPress

Posted by & filed under Advanced, Beginner, Developing for WordPress, Frameworks, Inter, Meta Boxes, Recommended Practices.

What is a meta box?

Meta boxes are the the little blocks in the editor section for each post type. They are actually quite old in WordPress terms – they were introduced back in 2.5 and now almost every premium theme uses this feature. Here’s what one looks like on my portfolio:

Meta boxes are commonly used by themes and plugins when a developer wants to implement a nicer interface for saving/editing a number of custom fields than the one provided with WordPress. As you can see in the screenshot above, I’m using a meta box to add two fields to my “Portfolio” post type: an url and a corresponding text. Then in the front end of the theme, I get these two fields and if they are present, I show a button like this:

I’ll show you how to do this in the following sections.

How to use meta boxes, the current WordPress way

Now we will write some standard WordPress code that registers a meta box, renders it and fires a callback when you save the post. You can put everything in functions.php and it will work.

Registering a meta box

We use the `add_meta_box` function when registering a meta box. It takes the following parameter (in this order):

$id – string (required) – the HTML id of the meta box (should be unique ;)
$title – string (required) – the title of the meta box (please put it in __(‘the title’, ‘your text domain’), so it can be translated)
$callback – string or array if inside a class (required) – the callback that renders the meta box
$page – string – the post type on which edit screen you want the meta box
$context – string – ‘normal’, ‘advanced’ or ‘side’
$priority – string – ‘high’, ‘low’, ‘core’ or ‘default’
$callback_args – array – arguments to pass to the callback (the callback always receives the $post object, so these arguments come after it)

The first three arguments are required, while the others are optional.

You may be wondering what is the difference between the different contexts and priorities. Basically, the different combinations define where the meta box should appear by default. For each section the priority order from top to bottom is ‘high’, ‘core’, ‘default’, ‘low’. When the user is using the default two column layout in the left column are shown the ‘normal’ and then the ‘advanced’ contexts. The right column consists only of the ‘side’ context. When using the single column layout, the context’s order becomes ‘normal’, ‘side’, ‘advanced’.

I know it can be difficult to imagine all combinations, so I’ve prepared two screenshots for you. First, with two column layout:

Our meta boxes are colored in red. I’ve registered every possible combination, so you can see how they interact with each other. The titles of our metaboxes are basically “context+priority”.

Now, see how ‘side’ comes between ‘normal’ and ‘advanced’ when you switch to single column layout:

Enough theory, show me some code already!

As we have said, here is how you register a meta box:

function add_my_meta_boxes() {
add_meta_box('my-meta-box', 'A box of awesomeness', 'show_my_meta_box', 'post', 'normal', 'high');
}
add_action('add_meta_boxes', 'add_my_meta_boxes');

This line adds a meta box with the id “my-meta-box”, the title is “A box of awesomeness”. It appears in posts, “normal” context with “high” priority (refer to the screenshots above). But an important bit is missing – we have to create the show_my_meta_box() callback that renders the contents, otherwise the meta box is useless and you’ll get a PHP warning.

What is this mystical callback?

It’s really simple, believe me. Try this:

function show_my_meta_box($post) {
echo "This post's id is {$post->ID}";
}

Here’s a screenshot of what I get:

Not really useful yet, but it’s working. We can make it useful with an input field:

function show_my_meta_box($post) {
?>
<table class="form-table">
<tr valign="top">
<th scope="row"><label for="my-awesome-field">Put awesomeness here</label></th>
<td><input type="text" name="_my-awesome-field" id="my-awesome-field" value="<?php echo get_post_meta($post->ID, '_my-awesome-field', true)?>" /></td>
>/tr>
<</table>
<?php
}

ID. Second, the name of the field – we use the same as the input’s name. The third argument is not really intuitive, but I’ll try to explain it briefly. If we pass true as the third argument, the function will return a only the first result. That’s because you can store several values under the same key. While this parameter is optional, you will specify it in most cases, because in practice you usually store one one value per key. Note that get_post_meta works in the front end too. You may use it like this in the loop:

get_post_meta(get_the_id(), '_my-awesome-field', true);

But our code misses one security feature: a nonce. “Nonce” stands for “Number used once”. It’s basically a hash that changes every time we visit the page – that way we can be sure that when we save the meta fields, the data is send by a proper user and not an attacker. We will add it using a hidden field in the form and generate it with the built in wordpress function wp_create_nonce. The function takes one argument – a key. We will use this key to retrieve the correct nonce when we save the fields later. Here’s what our code look like when we add the nonce:

function show_my_meta_box($post) {
echo '<input type="hidden" name="my_meta_box_nonce" value="'. wp_create_nonce('my_meta_box'). '" />';
?>
<table class="form-table">
<tr valign="top">
<th scope="row"><label for="my-awesome-field">Put awesomeness here</label></th>
<td><input type="text" name="_my-awesome-field" id="my-awesome-field" value="<?php echo get_post_meta($post->ID, '_my-awesome-field', true)?>" /></td>
</tr>
<</table
<?php
}>

Here’s what it looks like:

With this part done, our meta box interface is done, but it’s “read only”. We show a field, but if you try to save the post with something in this field – it won’t work. That’s why we continue to:

Saving the contents of a meta box

This part is all code, because we won’t change anything in the interface. So, no fancy screenshots here. Don’t worry, though, I’ll explain everything thoroughly.

The correct action for saving the fields is “save_post” (who would have thought! ;)

We start with an empty add_action callback:

function save_my_meta_box($post_id) {
}
add_action('save_post', 'save_my_meta_box');

The callback takes one argument – the post’s ID. We will need this, so don’t omit it.

Next, we proceed with the most important thing: checking the integrity of the data. We don’t want to save anything malicious.

It may be the most important, but it’s nowhere near close to hard with WordPress. Remember the wp_create_nonce function from the previous part? Right, it has a sibling: wp_verify_nonce.

Here is the nonce check:

if (!isset($_POST['my_meta_box_nonce']) || !wp_verify_nonce($_POST['my_meta_box_nonce'], 'my_meta_box')) {
return $post_id;
}

Briefly, this code checks if there is a nonce in the POST data. If it’s there, we pass it to wp_verify_nonce. It takes two arguments – first, the nonce we want to verify, and second, the key we used when we generated it. If any of the two checks fails – we interrupt the save function and we don’t do anything else. Now our function looks like this:

function save_my_meta_box($post_id) {
// check nonce
if (!isset($_POST['my_meta_box_nonce']) || !wp_verify_nonce($_POST['my_meta_box_nonce'], 'my_meta_box')) {
return $post_id;
}
}
add_action('save_post', 'save_my_meta_box');

The second malicious data check we want to do is to verify that the current user has the capabilities to edit this post or page – if the current post type is “post” – we check for ‘edit_post’, otherwise – we check for ‘edit_page’. We verify user capabilities with the function current_user_can($capability, $post_id). Here is how to do it:

if ('post' == $_POST['post_type']) {
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_page', $post_id)) {
return $post_id;
}

The third thing we have to check before we save the data is if this is a “real” post save or it’s an autosave. Because we won’t support revisions, we shouldn’t save the meta fields on post autosaves. Thankfully, it’s an one line check:

if (defined('DOING_AUTOSAVE') &amp;&amp; DOING_AUTOSAVE) {
return $post_id;
}

With these new additions, the save callback should look something like this:

function save_my_meta_box($post_id) {
// check nonce
if (!isset($_POST['my_meta_box_nonce']) || !wp_verify_nonce($_POST['my_meta_box_nonce'], 'my_meta_box')) {
return $post_id;
}

// check capabilities
if ('post' == $_POST['post_type']) {
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_page', $post_id)) {
return $post_id;
}

// exit on autosave
if (defined('DOING_AUTOSAVE') &amp;&amp; DOING_AUTOSAVE) {
return $post_id;
}
}
add_action('save_post', 'save_my_meta_box');

Now all we have to do is to call either the update_post_meta or the delete_post_meta function for our field:

if(isset($_POST['_my-awesome-field'])) {
update_post_meta($post_id, '_my-awesome-field', $_POST['_my-awesome-field']);
} else {
delete_post_meta($post_id, '_my-awesome-field');
}

The update_post_meta function takes three arguments – the post ID, meta key and the value we want to add. The other function – delete_post_meta – takes two arguments – post ID and meta key.

Our final save callback looks like this:

function save_my_meta_box($post_id) {
// check nonce
if (!isset($_POST['my_meta_box_nonce']) || !wp_verify_nonce($_POST['my_meta_box_nonce'], 'my_meta_box')) {
return $post_id;
}

// check capabilities
if ('post' == $_POST['post_type']) {
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_page', $post_id)) {
return $post_id;
}

// exit on autosave
if (defined('DOING_AUTOSAVE') &amp;&amp; DOING_AUTOSAVE) {
return $post_id;
}

if(isset($_POST['_my-awesome-field'])) {
update_post_meta($post_id, '_my-awesome-field', $_POST['_my-awesome-field']);
} else {
delete_post_meta($post_id, '_my-awesome-field');
}
}
add_action('save_post', 'save_my_meta_box');

Note that with this technique, we have to add the update_post_meta part for every field we want to save.

But that is too much work!

I know, I know. But never fear! I’ll show you a nice little trick that with some variations is used in almost all premium themes.

Meta box generator class

What if we can simplify our job in such way: instead of registering a metabox, we will describe a certain set of fields and tell a generator to put them in a meta box? We can even make it reuse the definitions for different post types.

Designing an API

We will create a public function add_smart_meta_box(). It will take a two arguments – the ID of the meta box and an array describing the meta box and its fields. Here is how we should be able to recreate the interface from the previous example:

add_smart_meta_box('my-meta-box', array(
'title' => 'A box of awesomeness', // the title of the meta box
'pages' => array('post'),  // post types on which you want the metabox to appear
'context' => 'normal', // meta box context (see above)
'priority' => 'high', // meta box priority (see above)
'fields' => array( // array describing our fields
array(
'name' => 'Put awesomeness here',
'id' => 'my-awesome-field',
'type' => 'text',
),
// put more arrays to add different fields
)
));>

The piece of code I’ve shown you should create exactly what we accomplished in the first part of this tutorial. We will also add several other field types besides text, but this will be later in the tutorial. However, when designing the “Smart meta box” API we have to take into account that it should be easy to add new fields. This means that the field templates should be separate from the generator class.

Generator skeleton

We will start with a class full of empty functions and gradually implement each of them. We will put this class in smartmeta.php. Here’s our initial version:

class SmartMetaBox {

protected $meta_box;
protected $id;

static $prefix = '_smartmeta_';

// create meta box based on given data
public function __construct($id, $opts) {
if (!is_admin())
return;

$this-&gt;meta_box = $opts;
$this-&gt;id = $id;

add_action('add_meta_boxes', array(&amp;$this, 'add'));

add_action('save_post', array(&amp;$this, 'save'));
}

// Add the meta box for multiple post types
public function add() {
}

// Callback function to show fields in meta box
public function show($post) {
// Use nonce for verification
echo 'id.'_meta_box_nonce" value="', wp_create_nonce('smartmetabox'.$this-&gt;id), '" /&gt;';
}

// Save data from meta box
public function save($post_id) {
// verify nonce
if (!isset($_POST[$this-&gt;id.'_meta_box_nonce']) || !wp_verify_nonce($_POST[$this-&gt;id.'_meta_box_nonce'], 'smartmetabox'.$this-&gt;id)) {
return $post_id;
}

// check autosave
if (defined('DOING_AUTOSAVE') &amp;&amp; DOING_AUTOSAVE) {
return $post_id;
}

// check permissions
if ('post' == $_POST['post_type']) {
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_page', $post_id)) {
return $post_id;
}
}

static function get($name, $single = true, $post_id = null) {
}

static function set($name, $new, $post_id = null) {
}

static function delete($name, $post_id = null) {
}
};

function add_smart_meta_box($id, $opts) {
new SmartMetaBox($id, $opts);
}

It’s almost empty, I’ve added the data integrity check – it’s almost the same as in the previous example, so I don’t think I have to explain it again. The constructor is fairly simple one, too. It checks if we are in the admin section and if it is so – it stores the ID and definition of the meta box. It calls the add() and save() callbacks – the former should register the meta box, the latter should save the meta fields. We will implement them, but first…

The get, set and delete helpers

Note that in all callbacks the $post_id is the last argument and is optional. This is intentionally different from the “WordPress way” where you put a data object’s id as a first argument. This way if we don’t provide an ID, we will the the ID of the current post (stored as ID in the global object $post). We can do it like this:

global $post;

$post_id = isset($post_id) ? $post_id : $post->ID;

Another useful thing is that in our class $single defaults to true, rather than the WordPress default – false.

These three helpers are essentially wrappers to get_post_meta, update_post_meta and delete_post_meta. I have explained above how to use them in general, so let’s see how I deal with them in this class:

static function get($name, $single = true, $post_id = null) {
global $post;

return get_post_meta( isset($post_id) ? $post_id : $post->ID, self::$prefix.$name, $single);
}

static function set($name, $new, $post_id = null) {
global $post;

return update_post_meta( isset($post_id) ? $post_id : $post->ID, self::$prefix.$name, $new);
}

static function delete($name, $post_id = null) {
global $post;

return delete_post_meta( isset($post_id) ? $post_id : $post->ID, self::$prefix.$name);
}

Each helper requests the global post and then returns the result of the corresponding WordPress function. Note that we prefix each field’s name with self::$prefix. I’ve set this prefix to ‘_smartmeta_’, but you can change it if you want. The idea behind it is to avoid collisions with other field’s name.

Registering a meta box within the class

Remember how we provide an array with post types? However, the add_meta_box WordPress function takes a string. We have to iterate over the desired post types and add the meta box for each post type:

// Add meta box for multiple post types
public function add() {
foreach ($this->meta_box['pages'] as $page) {
add_meta_box($this->id, $this->meta_box['title'], array(&amp;$this, 'show'), $page, $this->meta_box['context'], $this->meta_box['priority']);
}
}>

Besides the loop, there is nothing new in this function.

Saving the custom fields using our class

Contrary to the first part, this time we will start from the save callback, because it’s more simple than the other. We begin with a loop iterating over all fields in the current meta box:

foreach ($this->meta_box['fields'] as $field) {
$name = self::$prefix.$field['id'];
}

Again, we prefix the field’s id with self::$prefix. After this is done, we can add something very similar to what we did in the first part:

if(isset($_POST[$name]) || isset($_FILE['name'])) {
$old = self::get($field['id'], true, $post_id);
$new = apply_filters('save_smart_meta_'.$field['type'], $_POST[$name], $name);

if($new != $old) {
self::set($field['id'], $new, $post_id);
}
} elseif($field['type'] == 'checkbox') {
/*
 * Checkboxes are not send in POST if they are not checked;
 * We should set them to 'false' instead of deleting them;
 * Otherwise, we won't be able to distinguish them from new fields, which have not been saved yet.
 */
self::set($field['id'], 'false', $post_id);
} else {
self::delete($field['id'], $name);
}

The basic idea behind the code is the same as before – we check if a field is in the POST data (checking both $_POST and $_FILES), and if it’s not there – we delete it. If it is – we update it, with the only difference being that this time we check if the field really has new data coming in. We can make it without the

$new != $old

check, but I’ve added it here to show you how it’s done. I’m also adding a filter (for example, save_smart_meta_text for text inputs) in case you need to implement a not so straightforward field. For instance, a filter dealing with file uploads may look something like this:

function handle_smart_meta_file($file, $name) {
$file = wp_handle_upload($_FILES[$name], array('test_form' => false));
return $file['url'];
}
add_filter('save_smart_meta_file', 'handle_smart_meta_file', 10, 2);

Note that if you want to add files to the class, you have to set the enctype of the #post form to “multipart/form-data”.

Rendering the metabox

And now, the interface. As we have said, we should do this part in such way that it’s easy to add new field types. My preferred solution to this problem is to add each field type as a separate file in some directory relative to the SmartMetaBox class. Let’s they will be in smart_meta_fields/. In there we will create text.php – for text inputs, textarea.php – for textareas, and so on.

As for the show() function, we start with a loop iterating over the fields. We will borrow some HTML code from the first part of the tutorial.

echo '<table class="form-table">';

foreach ($this->meta_box['fields'] as $field) {
extract($field);
$id = self::$prefix.$id;

$value = self::get($field['id']);

echo '<tr>',
'<th style="width:20%"><label for="', $id, '">', $name, '</label></th>',
'>td>';

// we will place here the real deal

<echo '</td></tr>';
}

echo '</table>';

We start with the familiar layout that WordPress uses for config pages. First, we extract $field for convenience. Then, in $value we store the current saved value of the meta field. But what if we haven’t saved it before? We should provide a default. We can do this by checking if $value is an empty string but this won’t save us from the case where a user has actually stored an empty string. That’s why we do the following:

$value = self::get($field['id']);
if(empty($value) &amp;&amp; !sizeof(self::get($field['id'], false))) {
$value = isset($default) ? $default : '';
}

What this does is that after we try to get the value, we check if it is empty. If this is so, it may be set to be empty or may not be set at all. We do this using a second query – this time we get an array of all values stored for this key ($single is false). If this array doesn’t contain any elements – we are sure that the element isn’t stored at all. Then we check if we have provided a default when registering the meta box so we can use it.

Then, in the

we put a simple include for the field:

include "fields/$type.php";

And a simple description span immediately after this:

if(isset($desc)) {
echo '<span>'.$desc.'</span>';
}

With these final changes, our show() function is ready. Here’s what it should look like:

// Callback function to show fields in meta box
public function show($post) {
// Use nonce for verification
echo '<input type="hidden" name="'.$this->id.'_meta_box_nonce" value="',wp_create_nonce('smartmetabox'.$this->id), '" />';

echo '<table>';

foreach ($this->meta_box['fields'] as $field) {
extract($field);
$id = self::$prefix.$id;

$value = self::get($field['id']);
if(empty($value) &amp;&amp; !sizeof(self::get($field['id'], false))) {
$value = isset($default) ? $default : '';
}

echo '<tr>',
'<th style="width:20%"><label for="', $id, '">', $name, '</label></th>',
'<td>';

include "fields/$type.php";

if(isset($desc)) {
echo '&amp;nbsp;<span>'.$desc.'</span>';
}
echo '</td></tr>';
}

<echo '</table';
}>

Implementing the different field types

Text input

We start with the most simple field – a text input. It’s just one line in smart_meta_fields/text.php

<input type="text" name="<?php echo $id ?>" id="<?php echo $id ?>" value=">?php echo $value ?>" />

Textarea

This one is also a one-liner. Put the code in smart_meta_fields/textarea.php

textarea name="<?php echo $id ?>" id="<?php echo $id ?>" rows="5" cols="50"><?php echo $value ?></textarea<>

Checkbox

The last “simple” field. Put the code in smart_meta_fields/checkbox.php

<input type="checkbox" name="<?php echo $id ?>" id="<?php echo $id ?>" <?php checked($value, 'true') ?> value="true" />

Select

This one is a bit longer, but don’t worry – it’s just five lines. We will provide the list of options in $field['options']. Here’s an example select field:

array(
'id' => 'meta-key',
'name' => 'Awesomeness level',
'default' => 'most-awesome',
'options' => array(
'good' => 'Good',
'better' => 'Better',
'plain-awesome' => 'Plain awesome',
'most-awesome' => 'Most awesome',
>),
)

The array keys are each option’s value, the corresponding value is the option’s title.

Here is the template for selects (smart_meta_boxes/select.php):

<select name="<?php echo $id?>" id="<?php echo $id?>">
<?php foreach ($options as $opt_value=>$opt_name): ?>
<option <?php selected($value, $opt_value)?> value="<?php echo $opt_value?>"><?php echo $opt_name?></option>
<?php endforeach ?>
</select>

List of radio buttons

Suppose we want the achieve the same as in the select field type, but with radio buttons. OK, let’s do it:

<?php foreach($options as $opt_value=>$opt_name): ?>
<label>
<input type="radio" name="<?php echo $id?>" id="<?php echo $id?>_<?php echo $opt_value ?>" value="<?php echo $opt_value?>" <?php checked($value, $opt_value)?> />
<?php echo $opt_name ?>
></label>
?php endforeach ?>

The code goes in smart_meta_boxes/radio.php

Testing the generator

Here’s a meta box that demonstrates all features supported by the generator:

add_smart_meta_box('smart_meta_box_demo', array(
'title'     => 'Smart meta box',
'pages'		=> array('post'),
'context'   => 'normal',
'priority'  => 'high',
'fields'    => array(
array(
'name' => 'Text',
'id' => 'smb_text',
'default' => 'default',
'desc' => 'Description',
'type' => 'text',
),
array(
'name' => 'Textarea',
'id' => 'smb_textarea',
'default' => 'default',
'desc' => 'Description',
'type' => 'textarea',
),
array(
'name' => 'Checkbox',
'id' => 'smb_checkbox',
'default' => 'true',
'desc' => 'Description',
'type' => 'checkbox',
),
array(
'name' => 'Select',
'id' => 'smb_select',
'default' => 'second-val',
'desc' => 'Description',
'type' => 'select',
'options' => array(
'first-val' => 'First',
'second-val' => 'Second',
)
),
array(
'name' => 'Radios',
'id' => 'smb_radio',
'default' => 'second-val',
'desc' => 'Description',
'type' => 'radio',
'options' => array(
'first-val' => 'First',
'second-val' => 'Second',
)
),
>)
));

If you have done everything properly, here’s what you should see when editing a post:

And let’s check if everything is working properly:

Using this class in themes and plugins

It’s really simple. Include the SmartMetaBox class in you functions.php and then you can use SmartMetaBox::get() to access a meta field.

Generator conclusion

This is a fairly extensible starting point for every project. If you need more field types – you just add a template file. I hope you understood everything. Use as a starting point for your future projects. Here is the fully implemented class:

/**
* Meta box generator for WordPress
* Compatible with custom post types
*
* Support input types: text, textarea, checkbox, select, radio
*
* @author: Nikolay Yordanov <me@nyordanov.com>
* @version: 1.0
*
*/

class SmartMetaBox {

protected $meta_box;
protected $id;

static $prefix = '_smartmeta_';

// create meta box based on given data
public function __construct($id, $opts) {
if (!is_admin())
return;

$this->meta_box = $opts;
$this->id = $id;

add_action('add_meta_boxes', array(&amp;$this, 'add'));

add_action('save_post', array(&amp;$this, 'save'));
}

// Add meta box for multiple post types
public function add() {
foreach ($this->meta_box['pages'] as $page) {
add_meta_box($this->id, $this->meta_box['title'], array(&amp;$this, 'show'), $page, $this->meta_box['context'], $this->meta_box['priority']);
}
}

// Callback function to show fields in meta box
public function show($post) {
// Use nonce for verification
echo '<input type="hidden" name="'.$this->id.'_meta_box_nonce" value="', wp_create_nonce('smartmetabox'.$this->id), '" />';

echo '<table class="form-table">';

foreach ($this->meta_box['fields'] as $field) {
extract($field);

$id = self::$prefix.$id;

$value = self::get($field['id']);
if(empty($value) &amp;&amp; !sizeof(self::get($field['id'], false))) {
$value = isset($field['default']) ? $default : '';
}

echo '<tr>',
'<th style="width:20%"><label for="', $id, '">', $name, '</label></th>',
'<td>';

include "fields/$type.php";

if(isset($desc)) {
echo '&amp;nbsp;<span>'.$desc.'</span>';
}
echo '</td></tr>';
}

echo '</table>';
}

// Save data from meta box
public function save($post_id) {
// verify nonce
if (!isset($_POST[$this->id.'_meta_box_nonce']) || !wp_verify_nonce($_POST[$this->id.'_meta_box_nonce'], 'smartmetabox'.$this->id)) {
return $post_id;
}

// check autosave
if (defined('DOING_AUTOSAVE') &amp;&amp; DOING_AUTOSAVE) {
return $post_id;
}

// check permissions
if ('post' == $_POST['post_type']) {
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_page', $post_id)) {
return $post_id;
}

foreach ($this->meta_box['fields'] as $field) {
$name = self::$prefix.$field['id'];

if(isset($_POST[$name]) || isset($_FILES[$name])) {
$old = self::get($field['id'], true, $post_id);
$new = $_POST[$name];

if($new != $old) {
self::set($field['id'], $new, $post_id);
}
} elseif($field['type'] == 'checkbox') {
/*
* Checkboxes are not send in POST if they are not checked;
* We should set them to 'false' instead of deleting them;
* Otherwise, we won't be able to distinguish them from new fields, which have not been saved yet.
*/
self::set($field['id'], 'false', $post_id);
} else {
self::delete($field['id'], $name);
}
}
}

static function get($name, $single = true, $post_id = null) {
global $post;

return get_post_meta( isset($post_id) ? $post_id : $post->ID, self::$prefix.$name, $single);
}

static function set($name, $new, $post_id = null) {
global $post;

return update_post_meta( isset($post_id) ? $post_id : $post->ID, self::$prefix.$name, $new);
}

static function delete($name, $post_id = null) {
global $post;

return delete_post_meta( isset($post_id) ? $post_id : $post-<ID, self::$prefix.$name);
}

};

function add_smart_meta_box($id, $opts) {
>new SmartMetaBox($id, $opts);
}

Download the code for Smart Meta Box from Github:

The future of the meta box API in WordPress

Although at the time of writing, there’s a little more than three months until the release of WordPress 3.3, meta boxes are expected to have a new API either in 3.3 or in 3.4. It is not yet final, so I will not try to say anything specific about it, because it can change in the future. It will resemble the current Widget API – a meta box will be registered by extending some class and implementing the functions for rendering and saving in it. The advantages of using this class insted of the current standard method are that you will have both the render and save callbacks packed in a single class, and you also probably won’t have to deal with checking the integrity of the in the save callback. But as I have said – thing may change, so stay tuned to the WordPress Trac if you want the most recent information.

Tags: , ,

77 Responses

  1. Pippin August 3, 2011 at 11:14 pm

    Excellent! I I’m really glad that you used the metabox generator class. This is now one of the very, very few tutorials out there that show how to do this.

    Reply
    • AJ August 5, 2011 at 3:57 am

      Hell ya. This is awesome stuff. The generator class determinately makes things a whole lot easier. Awesome.

    • http://Enchant-Him--Review.blogspot.com November 28, 2013 at 4:29 am

      Hello there I am so delighted I found your weblog, I really found you by error, while I was researching on Google for something else, Anyways I am here now and would just like to say cheers for a tremendous post and a all round exciting blog (I also love the
      theme/design), I don’t have time to read it all at the minute
      but I have saved it and also included your RSS feeds, so
      when I have time I will be back to read a lot more, Please
      do keep up the awesome jo.

  2. Alex August 12, 2011 at 10:01 am

    Nice, thank you. There is a typo: “echo” misses before “get_post_meta” function.
    I have a question: is there a way to add content in an existing box please? (the “Publish” one for instance)

    Reply
    • Nikolay Yordanov August 12, 2011 at 10:37 am

      Thanks for correcting my mistake.

      You can add more fields to Publish. There are several do_action() calls inside the function that renders this meta box – for example, ‘post_submitbox_misc_actions’ or ‘post_submitbox_start’. It’s defined in wp-admin/includes/meta-boxes.php. As for saving the new fields – just hook a function to the ‘save_post’ action.

  3. Don August 13, 2011 at 7:53 am

    Awesome tutorial. Was planning to make some custom post types that would need meta boxes and your tutorial was just what I needed. Just have to sit down and make sense of it all. Lots to learn!

    Reply
  4. Jenni August 14, 2011 at 1:20 pm

    Hi Nikolay, you’ve made a very nice tutorial but I notice this way will create a lot of custom field in the post.

    In some modern WordPress theme and plugin like Genesis, Optimizepress, they create a lot of meta box below the post, can save the date but don’t create any additional post custom field, do you know how to archive this?

    Reply
  5. steffy August 14, 2011 at 6:51 pm

    Thank you very much for this tutorial, coming at the right time cause I’m learning how to do a ‘custom write panel’ on a custom post type basis.

    how can I put already registered custom field in a custom meta box.. let say that I have a site where I have already used a lot of custom fields but to ease the filling, I wish to ‘regroup’ some in one custom meta box and other is another. is that something that can be done?

    Is it possible to do Custom Meta box filled not only with custom fields but also with taxonomies (where the 2 would live togethere for example one custom meta box with 3 custom fields and 2 taxonomies under the same ‘roof’

    thank you in advance for your assistance

    Steffy

    Reply
    • Nikolay Yordanov August 14, 2011 at 7:11 pm

      First, in this tutorial a meta box is just an interface for editing meta fields. So technically, it doesn’t matter if you have already used this field.

      As for the taxonomies – it is also possible, but I haven’t provided a template in the tutorial. If you decide to implement your own, please consider adding it to the project (a pull request on github, maybe?).

    • steffy August 14, 2011 at 7:31 pm

      Greetings Nikolay

      First of all thank you for your very fast answer, much appreciated.

      Thank you to have clarified me with the meta box just being an interface for editing meta fields, so I guess I will be able to use the ‘names’ of my current custom fields and have them showed in my meta box for previous post

      I’m not really a developper per se, but I’m willing to learn how to be able to add taxonomies to the custom meta boxes.. I’m not very familiar with github too but if I come accross a solution, I will tell you

      thank you again for your help

      Steffy

    • steffy August 14, 2011 at 7:57 pm

      Thank you again for your help and offer Nikolay, I won’t hesitate to mail you directly if I’m stuck somewhere very specific.

      So far my request is quite ‘generic’ and might benefit others.. I want to be able to put in the very same custom meta box values that are ‘custom fields’ and ‘custom taxnomies’…

      here is an example, let say that you want to do a directory. I want a custom meta box as a ‘custom write panel’ where I could have with in the same box named ‘personal datas’
      Name -> post title
      Surnemame -> Custome field
      Street address -> Custome field
      Town -> Custom taxonomy, since different people can come from the same town, and I want to be able to group them
      Country -> Custom taxonomy, since different people can come from the same country, and I want to be able to group them

      is my explaination clear enough?

      Thank you again for your assistance

    • Nikolay Yordanov August 15, 2011 at 9:30 pm

      Sorry that this reply is a bit late.

      I think you’ll be best if you register two taxonomies (using register_taxonomy()) – “town” and “country”. Then you can put the “name” and “address” fields in a custom meta box. In my opinion this is the easiest way to accomplish what you indent.

  6. G01010 August 20, 2011 at 8:43 am

    This is a really helpful post and a huge timesaver. One thing I’m struggling with is how I might add a file upload field. I can create a file.php that will allow me to add a file upload option to the form, but no idea how to ensure the file url is saved with the post. Although you provide the ‘handle_smart_meta_file’, I’m not clear how that would fit with the rest of the code.

    Grateful for any assistance.

    Reply
  7. G01010 August 20, 2011 at 9:06 am

    Thanks for such a quick response but I’m still not getting it. I add the filter outside the class and create a meta box using this code:

    add_smart_meta_box(‘smart_meta_box’, array(
    ‘title’ => ‘Document details’,
    ‘pages’ => array(‘document’),
    ‘context’ => ‘normal’,
    ‘priority’ => ‘high’,
    ‘fields’ => array(
    array(
    ‘name’ => ‘Event name’,
    ‘id’ => ‘smb_select’,
    ‘desc’ => ‘Event’,
    ‘type’ => ‘select’,
    ‘options’ => $args,
    ),
    array(
    ‘name’ => ‘Document upload’,
    ‘id’ => ‘smb_file’,
    ‘desc’ => ‘Document’,
    ‘type’ => ‘file’,
    ),
    )
    ));

    So the file upload field appears in my post admin but when I upload and update the post, I can’t if or where it’s saved.

    Thanks again for your help.

    Reply
    • Nikolay Yordanov August 20, 2011 at 9:37 am

      What the filter does is that instead of saving $_POST[$name] in the meta field it save the url of where the file is stored. Try accessing it using SmartMetaBox::get(‘smb_file’). I haven’t tested the file upload, but the code should work fine, provided that you have a proper file.php and the enctype of the form is “multipart/form-data” (it should be).

  8. AJ Troxell August 25, 2011 at 3:46 pm

    For some reason, I cannot get the results of my meta boxes to render on my site. I get no error, but no result as well. I am using SmartMetaBox::get(‘site_name’) to display the content of a field with the id site_name from my “reviews” custom meta box. Any ideas?

    Reply
  9. Andrew August 25, 2011 at 8:10 pm

    Is there any chance, any chance at all, you could follow this up by showing how to add a couple of other field types in such details.

    It would be crazy if you could show how to add date/time picker fields and/or colour pickers and file uploader fields.

    Thanks so much again :) Your tuts are awesome.

    Reply
    • Nikolay Yordanov August 26, 2011 at 5:18 pm

      Thanks. I’m currently writing a tutorial on different topic which is almost ready. Once I have completed it, I’ll talk with the guys behind wproots about writing a sequel to this tutorial. Stay tuned ;)

  10. Andrew August 26, 2011 at 8:17 pm

    Hi Nikolay. Thanks so much for taking the time to respond. Look forward to your next tutorial and can’t wait for your sequel to this one, thanks for listening :)

    Will you cover the things I mentioned, do you think?

    Best wishes, Ab

    Reply
  11. zack August 31, 2011 at 4:07 am

    Nikolay…just had occasion to look into your SmartMetaBox class. Looks really good. My only concern is that it does not provide validation or sanitization out of the box and it does not appear to allow for the user to define his/her own sanitization functions. Do you have any plans to build in this functionality? I think this could be easily accomplished by adding a parameter to define a “sanitize_callback” and run that function in the “set” method.

    Great work on the class!

    Reply
  12. Ty Richards September 16, 2011 at 7:49 pm

    This is great stuff. I’m with a few others on this, I’d really like to get a solid image/media uploader setup for this and don’t even know how to accomplish this. Let me know if you have any ideas of where I can start. Thanks again!

    Reply
  13. Marie February 24, 2012 at 3:12 pm

    Hi, Thanks for this, really helpful.

    I’m getting a problem though when I want to add a custom field on the Link ‘page’. everything works fine if for the page I mention ‘page’, ‘post’ or a custom field, but if I change it to ‘link’ nothing works anymore. Anyone ever had this problem ? do you know how I can resolve it ? thanks :)

    Reply
  14. Madalin May 7, 2012 at 10:18 pm

    Great tutorial, Nikolay.

    I have a question though: can I output the new fields in the theme without using the “echo SmartMetaBox::get(‘field_id’);” ?

    I want to use the standard “get_post_meta($post->ID, ‘field_id’, TRUE);” but I can`t make it work with your files.

    Thanks!

    Reply
  15. Peter December 28, 2012 at 4:54 pm

    First of all great article. Probably the best wordpress article I’ve read on any wp topic ;) There are however quite a few typos in the final classes source that it would be a good idea to correct when you get a chance. I managed to spot all but one immediately, but for instance in your static delete function you have:

    return delete_post_meta( isset($post_id) ? $post_id : $post-<ID, self::$prefix.$name);
    }

    This part:

    $post-<ID

    Took me a while to spot and i was just getting a server error when I tried to incl. your class initially.

    Thanks again for the article. Great job.

    Reply
  16. Mike Lathrop January 10, 2013 at 12:58 am

    I noticed that if I set the ‘desc’ parameter in one field, and didn’t in the next, it would repeat in the meta box. I think this is because the variables set in line 47 aren’t unset, and are still there the next time the foreach loops through.

    Adding this line after line 65 fixed it for me, though:


    // unset extracted fields...
    foreach($field as $key=>$value) { unset($$key); }

    Reply
  17. Daniel February 7, 2013 at 11:00 am

    Awesome!
    How´s about to use this for normal page templates?
    i tryed something like this in functions.php:

    $post_id = $_GET['post'] ? $_GET['post'] : $_POST['post_ID'] ;
    if ( ‘gallery_h.php’ == get_post_meta( $post_id, “_wp_page_template”, true ) ) {
    add_smart_meta_box( … )
    }
    - seen on a few other sites.

    I see the meta box after saving the page, but get errors like :
    “Notice: Undefined index: post” and “Notice: Undefined index: post_ID”
    Any Ideas?

    Thanks :)

    Reply
  18. Tony February 18, 2013 at 10:38 pm

    Hi, I followed the tut and everything seems to be working great, except I’m unsure if I setup the nonce correctly. I included my meta box id and name below so you can see if I referenced them correctly when 1 – creating the nonce and 2 -checking the nonce. Specifically, I’m not sure if I could or should change the references to ‘my_meta_box’ to the id or name of my unique meta box. Thanks for your help!

    function tk_create_book_meta_box() {
    add_meta_box(
    ‘book_meta_box’, // Unique ID
    ‘Book Details’, // Title
    ‘tk_display_book_meta_box’, // Callback function
    ‘product’, // Post Type (or Admin page)
    ‘normal’, // Context (“side”, “normal”)
    ‘high’ // Priority (“default”, “high”, “low”)
    );
    }

    // create nonce
    echo ”;

    // check nonce
    if (!isset( $_POST['book_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['book_meta_box_nonce_input'], ‘my_meta_box’ ) ) {
    return $post_id;
    }

    Reply
  19. Fern May 1, 2013 at 3:55 am

    Fantastic beat ! I would like to apprentice at the same time
    as you amend your site, how can i subscribe for a weblog web site?
    The account helped me a appropriate deal. I were tiny bit acquainted of this your broadcast offered bright transparent concept

    Reply
  20. kik for pc July 3, 2013 at 4:07 am

    What i do not realize is actually how you are no longer really a lot more neatly-liked than you might be now.
    You are very intelligent. You know thus considerably in terms of this topic, produced me personally believe it from a lot of numerous angles.
    Its like men and women don’t seem to be interested except it’s
    one thing to do with Woman gaga! Your individual stuffs nice.
    All the time take care of it up!

    Reply
  21. Lyndon August 4, 2013 at 11:04 am

    Magnificent goods from you, man. I have take into account your stuff previous to
    and you’re simply extremely fantastic. I really like what you’ve
    got right here, certainly like what you are stating and the way in which by which you are saying it.

    You’re making it entertaining and you continue to care for to keep it wise. I can not wait to learn much more from you. This is really a tremendous web site.

    Reply
  22. Anonymous October 18, 2013 at 10:43 am

    Performed You will Learned This Samsung Unveils Corby HandsetsSamsung seems to have intrdouced a pair of brand new mobile phones to help you the nation’s brand of machines Coby, your young adults inside Eu being adedd. The ultra-modern styles incllude your Coprby Corby (TXT) as well as Coprby (PRO), which are usually obtainable along with your originaal Corby S3650-Mobile, witout a doubt availlable. The particular Cotrby (TXT) is usually knnown as being the B3210 and possesses some 2. 2-inch LCD, 2MP video camera utilizing video tutorial help support, included FM tuner, adjustable contains, as well as 40 MB involving on the deck of ram expandable with a microSD minute card position. The iphone mutually FRAME GPRS sites Quad-band. The particular mobile shall be availabe inside Eu before stop involving Sept. The particular Crby (PRO) handet boasts a QWERTY kyboard as well as succeeds for the 900 2100MHz HSDPA 7. 2Mbps networking. The particular phpone comes with a 2. 8-inch touchscreen LCD help support and will be offering some 3MP video camera. The particular video camera also can track record some video tutorial. Otehr includes involve a particular Radio utilizing RDS, extrenal take care of, Wi-Fi, Bluuetooth, Mirco HARDWARE, 100 MB ram by using a microSD minute card position etc .. The particular mobile should cruise ship inside Nov. Entire body Reasons for having The particular LG KP500 Wireless PhoneMobile phonees tend to be about simply just transmission inventions. At present, many of us need to be classy thewse inventions, gammes systems as well as company involving Online world solutions. Attractive to discover of which thhese teeny systems have been completely efficiently develop such critical featurers. The particular LG KC910 is normally a particular good mobile phones which are avaoilable for the mobile phone emails economy. This particular pohne posseses an manifestation of which makes some full imprint within the intellects involving customers. If you happen to zealous abotu your video camera, which will thrust some couplpe involving snap shots involving big consequently the following LG phne is normally mandatory to suit your needs. It gives 8-megapixel video camera is a imzage answer involving 3264 back button 2448 pixels. Morover, the following device has the benefit of includes similar to car totally focus, video tutorial recordinng, as well as vidoe video camera telephone. Gamerts will probably come to be happpy using this type of device, like this individual comes with sophisticated efatures exceptional activity. There are numerous activities that anyone can participate in aytime as well as everywhere. About the toher hands, the following device is available in dark coloor, that is definitely the most common colors across the world. By way of choosing the following device suggests that anyone can ship as well as experience e-maipls utilizing attachemnts in any extra rapiid solution. This particular phoone shall be used by personnel whom frequently must check out ones own e-mail whle they’re just possibly not around ones own Personal computers. LG KP500 Sandwich some other product out of LG is really a knowledgeable developer. This particular type Sandwich smartphone seems to have numerous includes of which absolutely get your mpobile consumers tend to be dependnet built in. This particular wonderful system is available in a lot of colorings, similar to dark, Vandyke dark brown, anodized silcver as well as golden stylish. You can aquire the following smartphone inside such colorings. There’s lots of people that want to purchase mobile phones according to thheir favoruite colorings as well as horoscope. This particular communicationms device boasts a video camera 3. 2 ultra pixels whicch your graphic reslution involving 2048 back button 1536 pxels. This particular video camera usually takes high-quality imaegs, and down load tese snap shots on the net, protect using your pc or possibly get them involving prrint as well as, thereby, obtainable these. With standby application, the nation’s power is normally 350 time as well as discussion the following device.

    Reply
  23. http://jsp-system.com/userinfo.php?uid=143522 December 31, 2013 at 1:05 pm

    Welcome to the official winrar password remover website!
    Here you can down load our special plan to eliminate the password for
    any RAR or ZIP file!

    Welcome to the official Rar Password recovery tutorial!

    I will be educating you how to hack the password of any WinRAR password, this is effortless to do with the RAR password cracker I
    am demonstrating to you in this video clip.

    This WinRAR password remover terms 10x faster than any other you
    will locate on the internet! It cracks WinRAR passwords in a issue
    of seconds! We are giving this RAR password recover away for totally free!

    Reply
  24. electric skillet cooking recipes January 4, 2014 at 2:00 am

    Today, while I was at work, my cousin stole my iPad and
    tested to see if it can survive a thirty foot drop, just so she can be
    a youtube sensation. My iPad is now destroyed and she has 83 views.
    I know this is completely off topic but I had to share it with someone!

    Reply
  25. Skype Premium Free March 23, 2014 at 4:50 am

    This specific Skype Premium electrical generator will certainly
    promptly get 12 30 days Skype Advanced Codes, allowing you to receive them and luxuriate in the rewards
    along with features of top quality for the complete season, without the need of further inconvenience.
    This particular Skype Premium Electrical generator is actually completely cost-free, light
    in weight and extremely user friendly. In less
    than a few minutes you’ll be able to be a part of class online video
    media telephone calls and also come with an advert free of charge experience, because of the sleek creator algorithm!
    Each signal may be worth more than 100$ UNITED
    STATES DOLLAR, however with this particular Skype
    Premium Generator you won’t should spend 1 dollar. You may have
    as much as a pair of (2) unique codes on the same Top quality account.

    Reply
  26. http://youtu.be/hzRnmAsGVWc March 25, 2014 at 4:27 pm

    This is the forthcoming Fallout 4 Beta Keys. With this Fallout 4 Generator
    you will be in a position to generate beta keys to use on your Xbox 360|Ps3 |or even Computer.
    Offering you opportunity to enter the world of the new redesigned Fallout four : Neglected Purchase in a beta phase.
    We are a large gaming local community with lots of keys on each platform.
    Our keys are coming from various builders such as Obsidian Leisure who them selves
    want beta testers for this match just before launch day. Every single beta keys coming from our Fallout
    4 Beta Keys Generator will be generated from our databases and locate
    the unused valid keys and supply them to you in a
    solitary click. Every keys. As we can continuously get beta
    keys and feed our database our programs are constantly up-to day.

    Reply
  27. Gry March 26, 2014 at 1:32 pm

    Hi!

    Thank you for an awesome tutorial! I was just wondering how I can write an if statement to check the state of a radio button.

    Reply

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>