7

I am using save_post for a function to send an email when a post is updated by a user. This is firing twice and I am aware this is due to the post revisions and autosaves.

I have tried to prevent this from happening by wrapping my wp_mail within a conditional statement but this still fires twice. What adjustments do I need to make to ensure this only fires once when a user updates the post?

function updated_search_notification($post_id)
{

    $post_type = get_post_type($post_id);
    if ($post_type === 'utility-search') {

        if ((wp_is_post_revision($post_id)) || (wp_is_post_autosave($post_id))) {
            // post is autosave
        } else {

            // Message Variables
            $siteurl                 = get_option('siteurl');
            $post_url                = '' . $siteurl . '/wp-admin/post.php?post=' . $post_id . '&action=edit';
            $new_search_name         = '';
            //$new_search_email = get_option( 'new_search_email' );
            $new_search_email        = '[email]';
            $utility_search_customer = '';
            $subject                 = 'Your search has been updated';

            // Message Contents
            $message = "[Message Contents]";


            // Send Email    
            wp_mail($new_search_email, $subject, $message);
        }
    }


}
add_action('save_post', 'updated_search_notification', 10, 3);
1

5 Answers 5

6

First, you can use this hook to target only one custom type:
https://developer.wordpress.org/reference/hooks/save_post_post-post_type/

This hook (and save_post) is called the first time when you click on "new ..." and then the hook is called with $update = FALSE.

Then to send e-mail only when the object is updated, you can test $update like this:

const UTILITY_SEARCH_POST_TYPE = "utility-search";


add_action("save_post_" . UTILITY_SEARCH_POST_TYPE, function ($post_ID, $post, $update) {

    if (wp_is_post_autosave($post_ID)) {
        return;
    }

    if (!$update) { // if new object
        return;
    }


    // preparing e-mail
    ...

    // sending e-mail
    wp_mail(...);


}, 10, 3);
4
  • This worked perfectly, thank you. Also, a nice reminder for the use of const too!
    – Ben H
    Commented Jul 15, 2018 at 17:36
  • 1
    I tried this and it still sends the email twice.. Commented May 8, 2019 at 14:40
  • Just wanted to add to the fact that the reason the save_post fires when you click "new..." is because WP creates some "partial posts" in the database as a pre-publish version of it all, it assigns IDs and such. I assume it's for drafts.
    – coolpasta
    Commented May 8, 2019 at 19:50
  • 3
    You should also add if ( (defined( 'REST_REQUEST' ) && REST_REQUEST ) { return; } Since Gutenberg uses the Rest API to update/insert the post. Commented May 11, 2021 at 15:38
11

The approved answer didn't work for me. I ended up trying several conditionals and the email would still get sent twice:

function xxx_send_mail($id, $post, $update){  

                if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
                    return;
                }

                if (wp_is_post_revision($id)) {
                    return;
                }

                if (wp_is_post_autosave($id)) {
                    return;
                }

                // if new post
                if (!$update) {
                    return;
                }

                wp_mail('[email protected]', 'The subject', 'The message');
         }
add_action( 'save_post_{the_post_type}', 'xxx_send_mail', 10, 3 );

I ended up fixing this by adding a convinient function provided by WP called did_action():

    function xxx_send_mail($id, $post, $update){  

                        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
                            return;
                        }

                        if (wp_is_post_revision($id)) {
                            return;
                        }

                        if (wp_is_post_autosave($id)) {
                            return;
                        }

                        // if new post
                        if (!$update) {
                            return;
                        }

                $times = did_action('save_post_{the_post_type}');
                if( $times === 1){
                        wp_mail('[email protected]', 'The subject', 'The message');
                 }
    }
add_action( 'save_post_{the_post_type}', 'xxx_send_mail', 10, 3 );

Hope this helps someone

4
  • 4
    did_action() is exactly what I was searching for. To make sure the function is only called once, add an if ( did_action( 'save_post_{the_post_type}' ) > 1 ) return; on top of the function.
    – KittMedia
    Commented Aug 5, 2020 at 8:15
  • I can't emphasize enough how helpful if ( did_action( 'save_post_{the_post_type}' ) > 1 ) return; is. This saves the day! Commented Jul 17, 2022 at 18:35
  • Checking defined('REST_REQUEST') in addition to the above without did_action did the trick for me. I also used the 'edit_post' hook, since I get less redundant calls with it.
    – Tim
    Commented Aug 25, 2022 at 1:54
  • Took forever to find this - but it works!! Commented May 20, 2023 at 2:15
5

Already approved answers have not worked for me and comment from @Nico Pernice helped.

If conditions in my code look like these:

function reset_cpt_expire( $post_id, $post, $update ){

    // Check if it is a REST Request
    if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
        return;
    }

    // Check if it is an autosave or a revision.
    if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
        return;
    }

    // Check if it is a new post
    if ( ! $update ) {
        return;
    }

    // my reseting code...

}

add_action( 'save_post_cpt', 'reset_cpt_expire', 10,3 );
1
  • 1
    when working with gutenberg this is the accepted answer. works for me as well. thank you
    – Nic Bug
    Commented Jun 23, 2023 at 8:15
0

Try this piece of code inside your function:

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;

Alternatively, you can use publish_post hook. If you just want to trigger this function when the post status is publish.

2
  • Thank you for the suggestion. When using this method and clicking the 'Update' button on a post, I still get two emails come through. I have a separate function for when a post is published.
    – Ben H
    Commented Jul 15, 2018 at 11:34
  • Did you put code on right spot? Did you try publish_post hook? Commented Jul 15, 2018 at 14:23
0

If you're having an issue where the publish_post hook is being triggered twice you can try using a global variable to keep track of whether the hook has already been triggered.

// Declare a global variable
global $publish_post_hook_triggered;
$publish_post_hook_triggered = false;

add_action('publish_post', 'my_publish_post_function');

function my_publish_post_function($post_id) {
    // Check if the hook has already been triggered
    global $publish_post_hook_triggered;
    if ($publish_post_hook_triggered) {
        return;
    }
    // Set the global variable to indicate that the hook has been triggered
    $publish_post_hook_triggered = true;

    // Your code to run when the hook is triggered
}

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.