How to add a Metabox. A step by step approach. add_meta_box

The WordPress posts edit Tool is not an unique compact module that shows all the boxes that appear on the screen but it’s a set of different little modules –called metaboxes– working together. A little module controls the title, another one controls the visual editor, another one defines the publishing options and actions, another one is for the Post Format… the Categories, the Author’s post, etc. The reason of this modular design is to allow developers to introduce easily new boxes (or remove some ones) in the Edit Screen.

The Edit Screen of Posts and its different Metaboxes

So thanks to this modular design, we can create our metaboxes for adding new functionalities or data to the WordPress Posts. The complete method for developing a new metabox has these three steps:

  • To inform that a new metabox has to be included in the Posts Edit Module.
  • To write the metabox function (for example two simple fields for selecting among 5 different colors and sizes).
  • To write a function for saving the new data (color and size) with the rest of common post data as two new meta values.

Let’s go to see these three steps.

To inform that a new metabox has to be included in the Posts Edit Module

In this first step, we have to inform to WordPress that we want to include a new metabox in the edit screen of posts and that there is a new function that controls this new metabox… For doing this step, there are some ways, in fact it’s possible to do this step with just a couple of code lines however, in this example we’ll see the official long way.

The first part of this step is attaching a new function adding_metaboxes_to_editPosts to the action current_screen that controls the load of all screens in WordPress environment and always receives one parameter: the current screen as a WP_Screen Object. Obviously, we could go straightly to the second step but sometimes, in a more complex situations probably we want to control the load of metaboxes from the beginning so that’s the real beginning: the action current_screen.

<?php
function adding_metaboxes_to_editPosts ( $screen ) {

    // When we need, we can introduce some control over the $screen object, for example
    // if ( $screen->base != 'post' ) return;
    // for controling that this new metabox is just loaded in case of posts.

    // Or for distinguishing between a new post and an old post
    // if ( $screen->action == 'add' ) return;
    // etc.

    $page = $screen->base;
    add_action( "load-$page.php",     'load_our_environment' );   // for old post
    add_action( "load-$page-new.php", 'load_our_environment' );   // for new post
    // or, 
    // add_action( "load-$page-new.php", 'load_our_environment_for_new_posts' );

}
if ( is_admin() ) //
    add_action ( 'current_screen', 'adding_metaboxes_to_editPosts', 10, 1 );
?>

As you can see, if we use the action current_screen we can easily introduce all kinds of check and verification points in our code that help us to construct specific environtments for each case, ie, for example, in case of new item, in case of old item, in case of special post type, in case of etc. Take a look to the WP_Screen Object for further information in the official documentation.

The second part is to use the action(s) that specíficaly loads each one of the WP screens: the action load-$page. In our example, we’ll use this action for two things:

  • To attach the function that attaches the metabox(es) to the Post Edit Screen.
  • To attach the function(s) that will save the new data (in our example, color and size).

As I said in the first part of this step, these final functions can be called directly in the functions.php but it’s always better to call them inside the functions that are executed inside the correct hooks.

<?php
function saving_the_metabox_data ( $post_id, $post, $update ) {
    // we'll develop this function later
}
function loading_the_metabox ( $post_type, $post ) {
    add_meta_box(
        'the-metabox-id', // this ID must be unique
         __( 'Your personal options', 'textdomain' ),
         'render_metabox_callback_function', // the name of the function used to display the metabox
         $post_type,
         'advanced',
         'default',
         NULL, // or $post, or whatever addtional parameters you need pass to the callback function
    );
}

function load_our_environment () {
    add_action( 'add_meta_boxes', 'loading_the_metabox', 10, 2 );
    // if you want, you can also use add_meta_boxes_{post_type} that just receives one parameter, the $post
    add_action( 'save_post', 'saving_the_metabox_data', 10, 3  );
}
?>

By the way, the official documentation of the action add_meta_box for a deeper look at all its parameters is here.

Complete scheme of actions and callback functions

To write the metabox function code

In the second step, we’ll write the function that renders the metabox inside the Posts Edit Module. This (and all) metabox(es) render function(s) always receives two parameters, the current $post (in general an object), and the array of values defined in the last parameter of the action add_meta_box. So we’re going to use both of them so, first we’ll rewrite the former metabox call, and then we write our render_metabox_callback_function

<?php
// we rewrite the function that actives the new metabox
function loading_the_metabox ( $post_type, $post ) {
    add_meta_box(
        'the-metabox-id', // this ID must be unique
         __( 'Your personal options', 'textdomain' ),
         'render_metabox_callback_function', // the name of the function used to display the metabox
         $post_type,
         'advanced',
         'default', 
         // in the next lines we define the parameters the function receives
         array ( 
                 'defaColo' => 'black',
                 'colors'   => array ( 
                                'white'  => __ ( 'White', 'textdomain' ), 
                                'black'  => __ ( 'Black', 'textdomain' ), 
                                'red'    => __ ( 'Red', 'textdomain' ), 
                                'blue'   => __ ( 'Blue', 'textdomain' ), 
                                'yellow' => __ ( 'Yellow', 'textdomain' )
                              ), 
                 'defaSize' => 'medium',
                 'sizes'    => array ( 
                                'x-small'  => __ ( 'Extra Small', 'textdomain' ), 
                                'small'    => __ ( 'Small', 'textdomain' ), 
                                'medium'   => __ ( 'Medium', 'textdomain' ), 
                                'large'    => __ ( 'Large', 'textdomain' ), 
                                'x-large'  => __ ( 'Extra Large', 'textdomain' )
                              ) 
               )
    );
}

// Now, the function that renders the new metabox
function render_metabox_callback_function ( $post, $data ) {

     $color = get_post_meta ( $post->ID, 'color', TRUE );
     $size  = get_post_meta ( $post->ID, 'size',  TRUE );

     // In case of a new post or if the current post hasn't got a defined color or size,
     // we use the array $data for setting the default value(s).
     if ( ! $color ) $color = $data['args']['defaColo'];
     if ( ! $size )  $size  = $data['args']['defaSize'];

     // We construct the list of options
     $clist = $data['args']['colors'];
     $slist = $data['args']['sizes'];

     //
     // the nonce field will be here
     //
     // and now two simple select fields
     echo '<p><label for="theColor">' . __( 'Select your Color', 'textdomain' ) . '</label>&nbsp;';
     echo '<select name="theColor" id="theColor" value="' . $color . '">';
     foreach ( $clist as $c => $value ) 
         echo '<option value="' . $c . '" ' . selected ( $c, $color, false ) . ' >' . $value . '</option>';
     echo '</select></p>';

     echo '<p><label for="theSize">' . __( 'Select your Size', 'textdomain' ) . '</label>&nbsp;';
     echo '<select name="theSize" id="theSize" value="' . $size . '">';
     foreach ( $slist as $s => $value ) 
         echo '<option value="' . $s . '" ' . selected ( $s, $size, false ) . ' >' . $value . '</option>';
     echo '</select></p>';
}
?>

Now, in the Edit Screen of Posts you can see the new metabox,

An example of our metabox

that works like this next code example.

Your personal options

 
So, now it’s time for the last step: to save this new data –color and size– as a new meta values for each post.

To write a function for saving this new data

In this step, we’ll write a function that saves these two new values. The name of the function has been decided and attached to the action save_post in the first step so, now, we can directly rewrite the former minimal function saving_the_metabox_data for converting it into a real function.

This function can be very sophisticated because we can introduce a lot of checking points in it: nonce values, privileges and roles, etc. however now, for understanding how does it work, we’ll begin with a simple function that just saves these two values: theColor, and theSize.

<?php
function saving_the_metabox_data ( $post_id, $post, $update ) {

    // the function just checks the existence of the values theColor and theSize
    // in PHP $_REQUEST array and saves the values as a new meta for the post 
    // whose ID = $post_id.
    if ( isset( $_REQUEST['theColor'] ) ) 
        update_post_meta( $post_id, 'color', $_REQUEST['theColor'] );
  
    if ( isset( $_REQUEST['theSize'] ) ) 
        update_post_meta( $post_id, 'size',  $_REQUEST['theSize'] );
      
}
?>

This is the basic core of the saving function, that simply checks and saves the values so from this core, if we add this next line to the function render_metabox_callback_function to the marked point tfor introducing a hidden nonce field,

wp_nonce_field( 'myoptions_nonce_action', 'myoptions_nonce' );

we can develop our final saving function with all checking points.

<?php
function saving_the_metabox_data ( $post_id, $post, $update ) {

    // Firtsly we retreive the value of the hidden nonce field
    $nonce_name   = isset( $_POST['myoptions_nonce'] ) ? $_POST['myoptions_nonce'] : '';
    $nonce_action = 'myoptions_nonce_action';

    // and then, we can check if nonce is set.
    if ( ! isset( $nonce_name ) ) return;
 
    // afterwards, if nonce is valid.
    if ( ! wp_verify_nonce( $nonce_name, $nonce_action ) ) return;


    // Here, if ew need it, we can also introduce a user's capabilities checking point
    if ( ! current_user_can( 'edit_post', $post_id ) ) return;


    // At this point, as the official documentation suggests for 'save_post' action,  
    // we can check if the save action is an background 'autosave' action or a real user's submit
    // and also, if it is just a 'revision' or the real last post version.

    // We check if it's not an autosave.
    if ( wp_is_post_autosave( $post_id ) ) return;
 
    // We check if it's not a revision.
    if ( wp_is_post_revision( $post_id ) ) return;
   

    // Finally, if we need it, we could check the value of the third parameter $update 
    // for choosing two different procedures: a procedure in case of new post and another one in case 
    // of updating an existing post.

    // if ( $update )
    //     call_function_old_post();
    // else
    //     call_function_new_post();

    
    // Here, we save the new data
    if ( isset( $_REQUEST['theColor'] ) ) 
        update_post_meta( $post_id, 'color', $_REQUEST['theColor'] );
  
    if ( isset( $_REQUEST['theSize'] ) ) 
        update_post_meta( $post_id, 'size',  $_REQUEST['theSize'] );
      
}
?>

And that’s all, now we can already use these new values in our Themes via the function get_post_meta.

<?php echo get_post_meta( get_the_ID(), 'color', TRUE ); ?>

I hope this ‘step by step’ approach can help you to develop easily a better metaboxes.

Have a nice WordPressing!

7 Comments on How to add a Metabox. A step by step approach. add_meta_box

Leave a Comment

   Mandatory field
You can use these HTML tags inside the commment.
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>