I was recently creating a WordPress plugin for a client and ran into an interesting challenge that took me a couple of hours to figure out, so I thought I’d post the solution here.
One part of the plugin passed the user’s post title to a third party API, and retrieved an id number from the API. The retrieved ID number had to then be saved into the wp_postmeta
table. The user, however, had the option to override the ID number returned from the API, and so a custom-field meta-box was added to the post to allow him to do so.
This seemed fairly straightforward to me, and I immediate used the publish_post
hook, and added my own action to it:
function pr_save_api_id()
{
global $post;
// get the id from the api
$pr_api_id = pr_get_api_id($post->post_title);
// Now, attempt to update the post_meta field
$meta_id = update_post_meta( $post->ID, 'pr_api_id', $pr_api_id);
}
add_action('publish_post', 'pr_save_api_id');
Simple enough. Using XDebug, I could watch the function get executed, see the value of $pr_api_id
, and even see the successful firing of update_post_meta()
. But, when I checked the database afterward, the meta field was still empty.
I tried adding the action to various hooks including save_post
, transition_post_status
and publish_post
, but nothing seemed to work, despite the fact that the callback was being fired.
It turns out, the problem was the custom field I’d added to the post editor to allow the user to override the API provided id. All the hooks I was using were fired before the custom-field data itself was saved into wp_postmeta which. That meant that if the client had not filled in the field, an empty value was saved into the wp_postmeta table, overwriting the value my plugin function had written to the field.
The hook I should have used was wp_after_insert_post
. This is fired once a post, its tags, categories, and all metadata have been saved. In addition, I had to add a second custom field (a checkbox) to the post form: if the user wanted to overwrite the API provided key, he needed to check it as well as providing the ID value. The final code, which worked nicely:
function pr_save_api_id()
{
global $post;
// Test to see user wants to overwrite/prevent getting an id from the api
if(empty($_POST['pr_api_id_overwrite'])) {
// get the id from the api
$pr_api_id = pr_get_api_id($post->post_title);
// Now, attempt to update the post_meta field
$meta_id = update_post_meta( $post->ID, 'pr_api_id', $pr_api_id);
}
}
add_action('wp_after_insert_post', 'pr_save_api_id');
Note to self: RTFM!