Wordpress - How to hook update_post_meta and delete_post_meta?

The update_post_meta hooks are called from the more generic update_metadata() function, so you might not have found it directly. The three hooks are:

  • update_post_metadata, a filter which allows you to "hijack" the metadata update and do something else. If you hook up a function that returns a boolean, execution stops there and the boolean is returned.
  • update_post_meta, an action that gets called before the data is updated in the database.
  • updated_post_meta, an action that gets called after the data is updated in the database.

If the meta key did not exist in the database yet, update_metadata() passes control to add_metadata(), which has the similar hooks add_post_metadata, add_post_meta, and added_post_meta. This happens before update[d]_post_meta gets called. So if you want to do something when the meta key is added or updated, be sure to hook into both the update* and the add* hooks - but notice that while the updated_* and added_* actions pass similar parameters, update_* and add_* do not (add_post_meta does not pass a meta ID first).

delete_metadata() has similar hooks: delete_post_metadata, delete_post_meta and deleted_post_meta.

Sample code:

add_action( 'added_post_meta', 'wpse16835_after_post_meta', 10, 4 );
add_action( 'updated_post_meta', 'wpse16835_after_post_meta', 10, 4 );
function wpse16835_after_post_meta( $meta_id, $post_id, $meta_key, $meta_value )
{
    if ( 'wpse16835_meta_key' == $meta_key ) {
        wpse16835_do_something( $post_id, $meta_value );
    }
}

add_action( 'deleted_post_meta', 'wpse16835_deleted_post_meta', 10, 4 );
function wpse16835_deleted_post_meta( $deleted_meta_ids, $post_id, $meta_key, $only_delete_these_meta_values )
{
    if ( 'wpse16835_meta_key' == $meta_key ) {
        wpse16835_undo_something( $post_id );
    }
}

Jan answer covers pretty much it above except for the case where a custom metadata is deleted from the Edit Post page. This happens asynchronously as soon as you click on the "Delete" button under the metadata, via a call to wp-admin/admin-ajax.php. Unfortunately, that calls bypass the delete_metadata() function in meta.php and calls delete_meta() in wp-admin/includes/post.php instead (not to be confused with wp-includes/post.php).

There are 2 actions that can still be used though, delete_postmeta (before deletion), and deleted_postmeta (after deletion). Both accept a single parameter $mid, which is the ID of the metadata (not the key). You can retrieve the metadata object using:

$meta = get_post_meta_by_id($mid);  

which returns an object with meta_id, post_id, meta_key abd meta_value. Of course, by the time deleted_postmeta is called the metadata has been deleted so get_post_meta_by_id() won't work (which kinda makes that action useless)

Same goes for updating a custom metadata from the Edit Post page. The update_metadata() function (and its actions) in meta.php is not called, but the update_meta() function in wp-admin/includes/post.php is instead. Two actions again here, update_postmeta and updated_postmeta. The args are $meta_id, $post_id, $meta_key, $meta_value.