Handling WordPress meta data and post revisions

I’ve worked with WordPress post meta data and meta boxes extensively in the past, but it’s been a while, and I’m using them in a current project, so I looked online for a refresher. There’s a lot of information out there, and a lot of it is either way out of date or way out of whack in terms of best practices. The best introduction I found was How to Create Custom WordPress Write/Meta Boxes at wptuts.com. It covers all the basics, but there’s a more advanced aspect I want to cover here, which is managing meta data with post revisions (the following was written for wordpress.com blogs, but applies to WordPress in general):

Each time you click Save Draft or Update, a revision is saved… Revisions allow you to look back at the recent changes you’ve made and revert to an earlier version if necessary.

If you are simply adding or replacing meta data for a post when you save it, then you don’t need to worry about post revisions: your meta data will get attached to the published version of the post. But if you need to, for example, add a value to an existing serialized array of meta data, you need to check for and handle post revisions.

If you are taking the input from your meta box and adding it to an existing array of meta data, you’ll want to make sure you retrieve the meta data that’s attached to the published post, as WordPress saves the revisions as child posts of the parent published post. Here’s an example callback for the save_post action:

function your_save_meta_data_function($post_id) {
    // standard status and security checks
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
    if (!isset( $_POST['meta_box_nonce']) || !wp_verify_nonce($_POST['meta_box_nonce'], 'your_meta_box_nonce')) return;
    if (!current_user_can('edit_post')) return;

    // if we need to augment existing meta data,
    // we need to make sure to retrieve it from the parent post
    if ($parent_id = wp_is_post_revision($post_id)) {
        $your_meta_array = get_post_meta($parent_id, 'your_meta_array', true);
    }
    else {
        $your_meta_array = get_post_meta($post_id, 'your_meta_array', true);
    }

    // make sure your user input is safe 
    $your_new_value = sanitize_text_field($_POST['your_new_value']);

    // if there are previously saved meta values, initialize an array
    // as otherwise $your_meta_array will be an empty string
    $your_meta_array = empty($your_meta_array) ? array() : $your_meta_array;
    $your_meta_array[] = $your_new_value;
    update_post_meta($post_id, 'your_meta_array', $your_meta_array);
}

The AUTOSAVE check blocks the function from executing for automatic saves, but if you click “save draft,” this comes into play (and, interestingly, when you click “publish” as well, because your callback actually gets called twice by save_post). Also, if you’re wondering where the array serialization happens, WordPress does it for you.

There are other ways you could approach this particular problem (like passing all the array values through the form, but that’s just another kind of ugly, and may not be very practical if its a nested array). But you’ll also want to check post revision status if you’re doing something like firing off emails to people, or anything else that shouldn’t happen when just saving a revision.

Leave a Reply