That’s because the hook “transition_post_status” is called:
- After the post has been published or updated in the database
- After the status has been updated in the database.
Based on your problem statement, I believe the hook you want is “pre_post_update”, as stated in relevant WordPress source code:
/**
* Fires immediately before an existing post is updated in the database.
*
* @since 2.5.0
*
* @param int $post_ID Post ID.
* @param array $data Array of unslashed post data.
*/
do_action( 'pre_post_update', $post_ID, $data );
This code is immediately followed by a call to $wpdb->update( $wpdb->posts, $data, $where )
, which actually saves the post to the database.
Usability issue. I would advocate for not calling wp_die()
if e.g. just the title is identical to one in the database. That’s a bad user experience. It would make more sense to use a add_filter
to rename the title if this happened:
/**
* Filters slashed post data just before it is inserted into the database.
*
* @since 2.7.0
*
* @param array $data An array of slashed post data.
* @param array $postarr An array of sanitized, but otherwise unmodified post data.
*/
$data = apply_filters( 'wp_insert_post_data', $data, $postarr );