I have a custom post type where I need to check for a duplicate record before the record actually gets saved into the database.

The requirement is like this: Before saving a record, I need to check whether another record with the currently provided combination of post_title, county (meta field) and city (meta field) exists already.

I have written the function for searching a similar record:

function mbs_check_duplicate_record() {
    global $post, $wpdb;
    $company = $_REQUEST['post_title'];
    $post_type = $_REQUEST['post_type'];
    $country = $_REQUEST['txtClientsCountryMbs'];
    $city = $_REQUEST['txtClientsCityMbs'];

    $args = array(
        'post_title'      => $company,
        'post_type'       => $post_type,
        'post_status'     => 'publish',
        'posts_per_page'  => 1,

        'meta_query' => array(
                'key'     => '_mbs_clients_country',
                'value'   => $country,
                'compare' => '='
                'key'     => '_mbs_clients_city',
                'value'   => $city,
                'compare' => '='

    $dup = new WP_Query( $args );

    if( $dup->post_count > 0 ) {
        return false;
    } else {
        return true;

I have also deregistered autosave functionality by adding this:

add_action( 'wp_print_scripts', function(){
    wp_deregister_script( 'autosave' );
} );

Then I called the above function inside the function which I am using to hook with save_post like this:

function mbs_save_clients_meta( $post_id ) {

        ! isset( $_POST['clients_url_nonce'] ) ||
        ! isset( $_POST['clients_city_nonce'] ) ||
        ! isset( $_POST['clients_country_nonce'] ) ||
        ! isset( $_POST['clients_lat_nonce'] ) ||
        ! isset( $_POST['clients_lng_nonce'] ) ) {

    if( ! current_user_can( 'edit_post', $post_id) ) {

    if( ! mbs_check_duplicate_record() ) {
        add_filter( 'admin_notices', function(){
            echo '<div id="mbs_duplicate_entry">Client already exists! Please check Name, Country and City.</div>';
        } );
    } else {
       The save routine starts ...

add_action( 'save_post',  'mbs_save_clients_meta' );

But the post always gets save even it finds an existing record.


Actually, when a duplicate record is found, no metadata is saving, but a new post is always getting added with the title. How can I prevent WordPress from doing this, i.e., nothing should be saved if another record with the given combination is already existing in the database?

Also, the admin_notices hook is not displaying my custom message. This is probably because the post is being created?

  • Some questions. Are you using Gutenberg or Classic editor? And wouldn't it be enough to allow saving incorrect records, but preventing them from being published? Feb 10, 2020 at 9:59
  • I am using Classic Editor. Saving needs to be done only if there is no duplicate record found based on a given combination of title, city and country fields. Feb 10, 2020 at 10:13
  • Sure, I understand your combination requirement. I asked about preventing records from being published because it is easier to do in WordPress. I have some similar use cases where user may create whatever "invalid" records he wants, but those records can't be published, my filter function prevents state change to 'published' if record is invalid. Feb 10, 2020 at 10:22
  • This should work! But at the same time, I need to show that the record exists as an admin notice. However, is this not possible at all to NOT save such a record at all and display an admin notice? Feb 10, 2020 at 10:25

1 Answer 1


See this answer for solution, see comments there about error messages. That should work with Classic editor.

It could work with Gutenberg too, but it would need additional effort to get correct error messages.


If using classic editor and preventing publishing of invalid records is enough, you can use this code:

function my_save_post($post_id) {

    if (.. record is not valid ..) {
        // remove Post Published message
        add_filter( 'redirect_post_location', function ( $location ) {
            return preg_replace( '/&message=\d+/', '', $location );
        } );

        // this is important to prevent recursive loop
        remove_action( 'save_post', 'my_save_post' );
        wp_update_post( [ 'ID' => $post_id, 'post_status' => 'draft' ] );
        add_action( 'save_post', 'my_save_post' );

        // admin_notice will not work because of redirect
        // try to change message in redirect_post_location - see above
        // or store transient and here and use it on next request
        // to show error message
        set_transient( 'my_save_errors_' . $post_id, $error_message, 20 );

add_action( 'save_post', 'my_save_post' );

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.