Не удается предотвратить двойное срабатывание функции, использующей save_post.

Вопрос или проблема

Я использую save_post для функции, которая отправляет электронное письмо, когда пользователь обновляет запись. Это происходит дважды, и я понимаю, что это связано с ревизиями записей и автоматическим сохранением.

Я пытался предотвратить это, обернув wp_mail в условный оператор, но письмо все равно отправляется дважды. Какие корректировки мне нужно сделать, чтобы это происходило только один раз, когда пользователь обновляет запись?

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))) {
            // запись является автосохранением
        } else {

            // Переменные сообщения
            $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="Ваш поиск был обновлен";

            // Содержание сообщения
            $message = "[Содержание сообщения]";

            // Отправка письма    
            wp_mail($new_search_email, $subject, $message);
        }
    }

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

Во-первых, вы можете использовать этот хук для нацеливания только на один пользовательский тип:
https://developer.wordpress.org/reference/hooks/save_post_post-post_type/

Этот хук (и save_post) вызывается первый раз, когда вы нажимаете «новое …», и затем хук вызывается с $update = FALSE.

Чтобы отправлять письма только при обновлении объекта, вы можете проверить $update так:

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) { // если новый объект
        return;
    }

    // подготовка письма
    ...

    // отправка письма
    wp_mail(...);

}, 10, 3);

Одобренный ответ не сработал для меня. В итоге я попробовал несколько условных операторов, и письмо все равно отправлялось дважды:

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 (!$update) {
                    return;
                }

                wp_mail('[email protected]', 'Тема', 'Сообщение');
         }
add_action( 'save_post_{the_post_type}', 'xxx_send_mail', 10, 3 );

Я исправил это, добавив удобную функцию, предоставляемую WP, под названием 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 (!$update) {
                            return;
                        }

                $times = did_action('save_post_{the_post_type}');
                if( $times === 1){
                        wp_mail('[email protected]', 'Тема', 'Сообщение');
                 }
    }
add_action( 'save_post_{the_post_type}', 'xxx_send_mail', 10, 3 );

Надеюсь, это поможет кому-то

Уже одобренные ответы не сработали для меня, и комментарий от @Nico Pernice помог.

Если условия в моем коде выглядят так:

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

    // Проверка, является ли запрос REST
    if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
        return;
    }

    // Проверка, является ли автосохранением или ревизией.
    if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
        return;
    }

    // Проверка, является ли новой записью
    if ( ! $update ) {
        return;
    }

    // мой код сброса...

}

add_action( 'save_post_cpt', 'reset_cpt_expire', 10, 3 );

Попробуйте этот код внутри вашей функции:

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

В качестве альтернативы, вы можете использовать хук publish_post. Если вы просто хотите сработать эту функцию, когда статус записи — опубликовано.

Если у вас возникла проблема, когда хук publish_post срабатывает дважды, вы можете попробовать использовать глобальную переменную, чтобы отслеживать, был ли хук уже сработан.

// Объявление глобальной переменной
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) {
    // Проверка, был ли хук уже сработан
    global $publish_post_hook_triggered;
    if ($publish_post_hook_triggered) {
        return;
    }
    // Установка глобальной переменной, чтобы указать, что хук был сработан
    $publish_post_hook_triggered = true;

    // Ваш код, выполняемый при срабатывании хука
}

Мы можем проверить переменную $_POST[‘_wpnonce’]. Это должно работать


add_action( 'save_post', 'clearCDNCache', 10, 3);

public function clearCDNCache($post_id, $post, $update){
        $needed_post_types = ['page', 'post', 'boutique', 'brand', 'video', 'product'];
        if( ! in_array($post->post_type,$needed_post_types) || empty($_POST['_wpnonce']) ) {
            return false;
        }
        $sample_post_url = get_front_end_post_url($post, 'sg');
        $base_url = get_front_end_base_url('sg');
        $cdn_path = str_replace($base_url, '*', $sample_post_url);

        purge_cdn_paths( [
            $cdn_path,
        ] );
    }

.

Ответ или решение

Для решения проблемы с функцией WordPress save_post, которая срабатывает дважды, возникает необходимость в понимании теоретической основы, примеров применения и практической реализации решения данного вопроса. Давайте подробно разберем эту проблему и предложим подходы к её решению.

Теория

Функция save_post в WordPress выступает как хук, который вызывается всякий раз, когда пост сохраняется в базе данных. Этот хук срабатывает при ряде событий, таких как:

  • Сохранение новой записи.
  • Обновление существующей записи.
  • Автосохранение (autosave).
  • Создание ревизий (revisions).

Причиной дополнительного срабатывания save_post могут быть именно автосохранения и ревизии, которые являются встроенными функциональностями WordPress. Для избежания этих нежелательных вызовов необходимо учитывать условия, чтобы выполнять действие только для фактического обновления поста пользователем.

Пример

Ваш код, использующий save_post, может выглядеть следующим образом:

function updated_search_notification($post_id, $post, $update) {
    $post_type = get_post_type($post_id);

    // Проверка типа поста
    if ($post_type !== 'utility-search') {
        return;
    }

    // Проверка на автосохранение и ревизию
    if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
        return;
    }

    // Проверка, если это не обновление, а создание нового поста
    if (!$update) {
        return;
    }

    // Формирование переменных сообщения
    $siteurl = get_option('siteurl');
    $post_url = $siteurl . '/wp-admin/post.php?post=' . $post_id . '&action=edit';
    $new_search_email = "[email]";
    $subject = "Your search has been updated";
    $message = "[Message Contents]";

    // Отправка email
    wp_mail($new_search_email, $subject, $message);
}
add_action('save_post', 'updated_search_notification', 10, 3);

Чтобы соответствовать действующему функционалу и устранить нежелательное поведение, рассмотрим практическое применение нескольких методов.

Приложение

Для успешной реализации рассмотрим следующие шаги:

  1. Проверка на автосохранение и ревизии: wp_is_post_autosave($post_id) и wp_is_post_revision($post_id) анализируют, является ли сохранение поста автосохранением или ревизией, и если это так, функция завершает выполнение. Эта проверка позволяет предотвратить повторные вызовы, вызванные такими операциями.

  2. Проверка обновления поста: Используйте параметр $update, передаваемый в функцию, чтобы убедиться, что действие выполняется только при обновлении поста, а не при его создании.

  3. Фильтрация по типу поста: Использование специализированного хука save_post_{post_type} для конкретного типа постов, например save_post_utility-search, позволяет изолировать операции только для указанных типов записей. Это значительно снижает вероятность неправомерной обработки данных для других типов постов.

  4. Использование глобальных флагов или did_action: Для дополнительной гарантии, что функция вызывается единожды, можно применять did_action для проверки количества вызовов. Это добавляет еще одну степень контроля над исполнением функции.

  5. Обработка REST-запросов: Поскольку REST-запросы могут запускать сохранение постов, стоит исключать их обрабатывая defined('REST_REQUEST') && REST_REQUEST.

Таким образом, внедрение всех этих проверок обеспечит, что ваше уведомление об обновлении поста будет отправляться только тогда, когда пост действительно обновляется пользователем, и будет эффективно предотвращать срабатывание функции более одного раза в ходе одного и того же сохранения.

Этот подход надежно минимизирует вероятность ненужного выполнения вашего кода и отправки редандантных email-уведомлений. Надеемся, что данное решение будет полезным и облегчит вашу работу с функцией save_post в WordPress.

Оцените материал
Добавить комментарий

Капча загружается...