How do I save metadata for a specific custom post type only?

The question:

I’m trying to set up a custom post type following this tutorial. However, I’m a bit confused as how/where to implement update_post_meta(). The tutorial suggests this pattern:

add_action('save_post', 'save_my_metadata');

function save_my_metadata()
{
    global $post;
    update_post_meta($post->ID, 'my_metadata', $_POST['my_metadata']);
}

Which does work, but has the unfortunate effect of adding that metadata to each and every post, whether it belongs to this custom type or not.

I’ve put the above in functions.php and am guessing that might be part of the problem. I’m guessing I need to restrict the ‘save_post’ action to only trigger for posts of my custom type.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

function save_my_metadata($ID = false, $post = false)
{
    if($post->post_type != 'your_post_type')
        return;
    update_post_meta($ID, 'my_metadata', $_POST['my_metadata']);
}

That should work. Just replace ‘your_post_type’ with the name of the post type. Also, little known fact: the ‘save_post’ hook passes the post’s ID as an argument.

EDIT

I updated the function to reflect Jan’s comment. Thanks Jan!

Method 2

If you want to handle multiple post types, I’d recommend a basic switch statement:

add_action('save_post', 'save_my_metadata');

function save_my_metadata($ID = false, $post = false)
{
    switch($post->post_type) 
    {
        case 'post_type_1':
            // Do stuff for post type 1
            update_post_meta($ID, 'my_metadata', $_POST['my_metadata']); // Example...
            break;
        case 'post_type_2':
            // Do stuff for post type 2
            break;
        default:
            return;
    }
}

The cases are basically the same as if($post->post_type) == 'post_type_1') {} But don’t require multiple if-else blocks. The default block in the switch handles cases where the post type isn’t in your custom set.

Method 3

@John P Bloch and @EAMann have already given great answers so mine is in addition:

  1. Consider prefixing your meta_keys with an underscore. Doing so hides them from the list of custom fields displayed on a post edit screen, i.e.
    function save_my_metadata($post_id,$post=false) {
       if($post->post_type=='your_post_type')
          update_post_meta($post_id, '_my_metadata', $_POST['my_metadata']);
    }
    

    Obviously that means you’d need a custom metabox to be able to edit the fields too.

    Here’s an edit screen for context:

  2. Another thing you could do is add your own hook to make saving specific post types easier, i.e. your hook could be “save_{$post_type}_post“; for a movie post type it would be save_movie_post. Here’s what you’d have to add to your theme’s functions.php file or in a plugin somewhere:

    add_action('save_post', 'save_custom_post_type_posts',10,2);
    function save_custom_post_type_posts($post_id,$post=false) {
       do_action("save_{$post->post_type}_post");
    }
    

    With that you could then rewrite your original code like so (including the underscore trick from #1 above):

    add_action('save_my_postype_post','save_my_postype_metadata',10,2);
    function save_my_postype_metadata($post_id,$post) {
        update_post_meta($post_id, '_my_metadata', $_POST['my_metadata']);
    }

Method 4

Personally, I prefer to follow the below pattern for adding custom meta handlers to post types. With the below, you can add the meta support to a post type by just adding the supports key (‘subtitle’ in the example below) to the supports array for the post type by calling add_post_type_support(‘my_post_type’, ‘subtitle’);

class Subtitle_Meta_Handler {
    public function initialize() {
        add_action('add_meta_boxes', array($this, 'add_metabox'), 10, 2);
        add_action('save_post', array($this, 'update'));
    }

    public function add_metabox($post_type, $post)
    {
        if(post_type_supports($post_type, 'subtitle'))
        {
            add_meta_box('subtitle', 'Subtitle', array($this, 'metabox'), $post_type);
        }
    }

    public function metabox($post)
    {
        $subtitle = get_post_meta($post->ID, 'subtitle', true);
        if(!$subtitle)
        {
            $subtitle = '';
        }
        ?>
        <input type="text" style="width: 70%;" value="<?php echo esc_attr($subtitle);?>" name="subtitle" id="subtitle">
        <?php
        wp_nonce_field('update_subtitle', 'subtitle_nonce');
    }

    public function update($post_id)
    {
        if(wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
            return $post_id;
        }
        if(isset($_REQUEST['subtitle_nonce']) && wp_verify_nonce($_REQUEST['subtitle_nonce'], 'update_subtitle')) {
            $subtitle = trim(strip_tags($_REQUEST['subtitle'], '<b><strong><span><a>'));
            if(empty($subtitle)) {
                delete_post_meta($post_id, 'subtitle');
            } else {
                update_post_meta($post_id, 'subtitle', $subtitle);
            }
        }
    }
}
add_action('init', array(new Subtitle_Meta_Handler(), 'initialize'));

Hopefully something like this will soon be added into core.

Method 5

Prior updating check whether or not the current post is of your post-type. That would ensure you don’t save it for all posts.

You should check for the input as well (that’s missing in your example) and next to that, keep in mind that you might only add the action when that post-type is active. If that’s the case, you do not need to check for that post-type later on.

Getting a posts type: get_post_type() or $post->post_type;

Method 6

I cant get this to work – not sure what Im doing wrong – but Im trying to use post_updated hook instead of the save_post – as I want these values to be inserted after the post has been updated so I can retrieve values from the other custom fields.

 function update_meta ($ID = false, $post = false) {
  update_post_meta($ID, 'rest_long', 'Test 1');
  update_post_meta($ID, 'rest_lat', 'Test 2');
}

add_action('post_updated', 'update_meta');


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment