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

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.

Read More

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.

Related posts

Leave a Reply

6 comments

  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!

  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.

  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']);
      }
  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.

  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;

  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');