Is there a hook / action that is triggered when adding or removing a post thumbnail?
-
11-12-2020 - |
Question
I'm trying to update a custom field after a post thumbnail (Featured Image) is either added or removed from a post. The aim of this is to keep track of whether a featured image has been added or removed in order to do a synchronised export of only the updated 'dirty' posts for use in an external service.
I've looked throughout the codex for a hook that would be triggered after a post_thumbnail is set but I haven't been able to find anything. The solution I'd hoped would work was to use the slimly documented 'updated_post_meta' action (not to be confused with 'update_post_meta'!), using the following code:
add_action('updated_post_meta', 'check_dirty_fields_updated_post_meta', 10, 4);
function check_dirty_fields_updated_post_meta($meta_id, $post_id, $meta_key, $meta_value) {
if ('_thumbnail_id' == $meta_key) {
update_post_meta($post_id, 'thumbnails_dirty', 1);
}
if ('schedule' == $meta_key) {
update_post_meta($post_id, 'schedule_dirty', 1);
}
}
So, 'updated_post_meta' should be triggered whenever post_meta is updated, but unfortunately '_thumbnail_id' never gets triggered so the 'thumbnails_dirty' custom field that I subsequently want to set doesn't get updated.
You'll see from that code that I'm also checking to see if a meta_key of 'schedule' is ever updated and then marking another custom field called 'schedule_dirty' (the 'schedule' post_meta value is a custom field that gets set within the standard post UI) In the case of this more standard custom field the 'updated_post_meta' action does see it when it's updated and set the 'schedule_dirty' as intended.
The problem I have is that I can't see why the '_thumbnail_id' post_meta isn't triggering the 'updated_post_meta' action.
Compounding the problem further I just can't find any clear documentation on when the post thumbnail is set and subsequently updating it's related '_thumbnail_id' post_meta field. I note that when setting the Featured Image on a post that this is set straight away and therefore does not seem to be dependent on the 'save_post' action, so whilst I have looked through various aspects related to saving posts I think the answer lies elsewhere.
A few other bits of info that might be relevant to know:
The posts in question here are a custom post type
I'm also using the Multiple Post Thumbnails plugin and subsequently want to check for the updated state of these additional post thumbnails too.
Solution
I think you want to use the added_post_meta
hook instead of updated_post_meta
because you're not updating the meta here, only adding it. At least in the case of the _thumbnail_id
, where we must delete it before adding it again (no update) through the admin UI.
Investigating this further we see that this part of the update_metadata()
function:
if ( empty( $meta_ids ) ) {
return add_metadata($meta_type, $object_id, $meta_key, $passed_value);
}
is causing you the problem, because it calls add_metadata()
and returns it, before the update_{$meta_type}_meta
and updated_{$meta_type}_meta
hooks are ever fired.
You therefore need to hook into the add_metadata()
function, instead of the update_metadata()
function, through e.g. the add_{$meta_type}_meta
(before) or added_{$meta_type}_meta
(after) hooks.
If we check out the wp_ajax_set_post_thumbnail()
function, that's ajax-requested from the admin UI when adding/removing the featured image, we see that it uses the functions
set_post_thumbnail()
and delete_post_thumbnail()
.
The latter one is a wrapper for delete_metadata()
, that fires up the delete_{$meta_type}_meta
(before) and deleted_{$meta_type}_meta
(after) hooks.
OTHER TIPS
This is a working example that hooks into the add / remove events for a post thumbnail. It also includes the meta key required for the Secondary thumbnail created by the MultiPostThumbnail. Help for this solution came from here and the MultiPostThumbnail docs. birgire gives a good explanation about these hooks in the accepted answer.
// Initialize the MultiPostThumbnails based on https://github.com/voceconnect/multi-post-thumbnails/wiki
if (class_exists('MultiPostThumbnails')) {
new MultiPostThumbnails(
array(
'label' => 'Secondary Image',
'id' => 'secondary-image',
'post_type' => 'post'
)
);
}
// Listen for Updates
add_action( 'added_post_meta', '___after_post_meta', 10, 4 );
add_action( 'updated_post_meta', '___after_post_meta', 10, 4 );
function ___after_post_meta( $meta_id, $post_id, $meta_key, $meta_value )
{
if( $meta_key === '_thumbnail_id' ){
// Primary Thumbnail Added
update_post_meta($post_id, 'thumbnails_dirty', 1);
} else if ( $meta_key === 'post_secondary-image_thumbnail_id' ) {
// Secondary Thumbnail Added
update_post_meta($post_id, 'thumbnails_dirty', 1);
}
}
add_action( 'deleted_post_meta', '___deleted_post_meta', 10, 4 );
function ___deleted_post_meta ( $deleted_meta_ids, $post_id, $meta_key, $only_delete_these_meta_values )
{
if( $meta_key === '_thumbnail_id'){
// Primary Thumbnail Deleted
update_post_meta($post_id, 'thumbnails_dirty', 1);
} else if ( $meta_key === 'post_secondary-image_thumbnail_id' ) {
// Secondary Thumbnail Deleted
update_post_meta($post_id, 'thumbnails_dirty', 1);
}
}