Cron fires outside of the standard WordPress loop, so $post
will not be filled with any post-related data when the function is called. It should be obvious, then, that providing an ID directly isn’t referencing the non-existent $post
variable which is why it works in that scenario.
The best way to set post_statuses to expired is to change your function to something that loads posts which need to be expired and loops through them, using WP_Query:
function update_expired_field() {
$today = getdate();
$query = new WP_Query( 'meta_query' => array(
'key' => 'your_expire_meta_key',
'value' => $today['year'] . '-' . $today['month'] . '-' . $today['mday'],
'type' => 'DATE',
'compare' => '<=',
),
'fields' => 'ids',
);
foreach( $query->posts as $postid ) {
wp_update_post( array(
'ID' => $postid,
'post_status' => 'expired',
) );
}
}
In the code I’ve shown querying the meta_key field from the database for a key called ‘your_expire_meta_key’, which needs to have values of the date you want the posts to expire in the format YYYY-MM-DD. We compare that date field against today’s date to determine which posts need expiring. The 'fields' => 'ids'
tells WP_Query to only return post IDs so that we run as fast as possible. Finally we loop-through each post ID and update the relevant post to expired status.