Проверка на обновление или новый пост в действии save_post

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

Возможно ли в действии save_post определить, создаётся ли новый пост или обновляется существующий пост?

С версии WordPress 3.7 – если я правильно помню – хуки save_post – больше информации о хуке и его использовании можно найти по ссылкам Справочник кода: save_post и Codex: save_post – имеют третий параметр $update, который можно использовать для определения именно этого.

@param     int               $post_ID     ID поста.
@param     WP_Post     $post          Объект поста.
@param     bool            $update     Является ли это существующим постом, который обновляется, или нет.


Примечание:

$update не всегда true – вы можете увидеть и протестировать это с помощью кода ниже. Однако документация не очень хорошая, возможно, название далеко от оптимального, и поэтому создаёт ложные ожидания. Код ниже можно использовать для отладки, экспериментируя с моментами перехвата выполнения кода, потому что в противном случае вы не увидите информацию/сообщения. Я думаю, что причиной обманчивого поведения является обработка ревизий и автосохранений – которые можно отключить, но я не рекомендую это и не тестировал. Не уверен, стоит ли это создавать Trac Ticket, поэтому я не открывал его; если вы так думаете, пожалуйста, воспользуйтесь ссылкой и сделайте это сами. Кроме того, как указано в комментариях, если у вас есть конкретная проблема, задайте новый вопрос.

add_action( 'save_post', 'debug_save_post_update', 10, 3 );
function debug_save_post_update( $ID, $post, $update ) {

  echo '<pre>';
  print_r( $post ); echo '<br>';
  echo '$update == ';
  echo $update ? 'true' : 'false';

  //условия
  if( ! $update && $post->post_status == "auto-draft" ) {
    // относится к новому посту
    echo ' && $post->post_status == "auto-draft"';
    //die();
  } else if ( ! $update ) {
    // относится, в основном, к (автосохранённой) ревизии 
    //die();
  } else {
    // относится к обновлению опубликованного поста
    // когда есть ревизия, что обычно бывает, 
    // стандартное поведение WordPress, тогда это считается 
    // обновлением, что и вызывает путаницу
    // есть и другие методы, например, проверка времени или статуса поста
    // в зависимости от вашей ситуации может быть более уместно 
    // использовать один из этих альтернативных подходов 
    //die();
  }

  echo '</pre>';
  //die();
}

Способ, которым я выполняю эту проверку (в рамках подключенной функции), – это сравнение даты поста и даты изменения (в GMT для стандартизации)

function check_new_vs_update( $post_id ){
    $myPost     = get_post($post_id);
    $created    = new DateTime( $myPost->post_date_gmt );
    $modified   = new DateTime( $myPost->post_modified_gmt );
    $diff       = $created->diff( $modified );
    $seconds_difference = ((($diff->y * 365.25 + $diff->m * 30 + $diff->d) * 24 + $diff->h) * 60 + $diff->i)*60 + $diff->s;

    if( $seconds_difference <= 1 ){
        // Новый пост
    }else{
        // Обновлённый пост
    }
}
add_action('save_post', 'check_new_vs_update' );

Это работает, потому что даже при создании у поста есть дата ‘изменён’, которая точно такая же, как дата ‘создан’, но мы допускаем отклонение в 1 секунду в любом направлении, на случай, если одна секунда истечёт во время создания поста.

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

function attributes_save_postdata($post_id) {
  if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
  if (!wp_verify_nonce($_POST['_attributes_noncename'], plugin_basename(__FILE__))) return;
  if ('page' == $_POST['post_type']) {
    if (!current_user_can('edit_page', $post_id)) return;
  } else {
    if (!current_user_can('edit_post', $post_id)) return;
  }
  $termid = get_post_meta($post_id, '_termid', true);
  if ($termid != '') {
    // это новая запись
    $termid = 'update';
  } else {
    // это существующая запись
  }
  update_post_meta($post_id, '_termid', $termid);
}
add_action('save_post', 'attributes_save_postdata');

Пример ответа ialocin с параметром “update”:

function save_func($ID, $post, $update) {

   if($update == false) {
     // сделай что-то, если это первое публикация
   } else {
     // Сделай что-то, если это обновление
   }
}

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

Я только что столкнулся с save_post о новых и обновлениях. После чтения исходного кода, чтобы понять поток, я обнаружил, что следующий метод может быть полезен. Поскольку он ещё не упоминался раньше, посмотрите, будет ли он полезен кому-то. (Тест выполнен на Core 5.3.3)

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

  1. После нажатия “Добавить новый” (пост)
  2. $post = get_default_post_to_edit( $post_type, true ); будет вызван, где
  3. get_default_post_to_edit() получает аргумент $create_in_db = true
  4. поэтому wp_insert_post() немедленно вызывается, auto-draft пост создаётся, даже если он не сохраняется, каждый раз при нажатии Добавить новый, создаётся auto-draft
  5. $update всегда равно true при публикации. Поэтому, когда вы публикуете новый пост, он равен true.

Сравнив объект $_POST для нового поста и обновлённого поста или повторной публикации поста, заметное различие – значение _wp_http_referer, новый пост это /wp-admin/post-new.php

Предположение: предполагается, что пост публикуется/добавляется из пользовательского интерфейса. Если это сделано другим механизмом, пользовательским кодом, необходима проверка для корректировки.

add_action( 'save_post', 'test_save_post_check', 0, 3 );
function test_save_post_check( $post_ID, $post, $update ) {
    // другие тесты + это
    // проверка позиции 'post-new' из '_wp_http_referer'
    if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ) {
        // новый
    } else {
        // обновление
    }
}

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

  1. проблема проверки между обновлением или вставкой

  2. проблема двойной вставки при действии save_post

    function save_annonces_callback($post_ID, $post, $update){
    
            $post_type = get_post_type($post_ID);
    
            if ( $post_type === 'annonces' ){
    
                // это предотвращает двойную вставку при действии save_post :)
                if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
                    return;
                } else {
    
                    //проверка, если новый пост, то вставка
                    if( strpos( wp_get_raw_referer(), 'post-new' ) > 0 ){
    
                        //выполнить вставку
    
                    }else{
    
                        //выполнить обновление
                    }
    
                }
    
            }
        }add_action('save_post','save_annonces_callback', 10, 3);
    

Вы можете использовать действие pre_post_update для кода обновления и save_post для кода нового поста. Это работает до обновления поста.

Как намекнул Даршан Тханки (и далее уточнил Стивен Харрис), вы можете использовать pre_post_update в своих интересах.

global $___new_post;
$___new_post = true;

add_action(
  'pre_post_update',
  function() {
    global $___new_post;
    $___new_post = false;
  },
  0
);

function is_new_post() {
  global $___new_post;
  return $___new_post;
}

Причина, по которой я использовал глобальные переменные, в том, что function is_new_post() use ( &$new_post ) недопустим в PHP (шок…) так что использование этой переменной в области функции не работает – вот и причина глобальной переменной.

Обратите внимание, что это можно надёжно использовать только в рамках/после события save_post (что обычно достаточно, по крайней мере, для того, что мы с этим делаем).

Когда срабатывает save_post, вся информация о посте уже доступна, так что, теоретически, вы могли бы использовать

function f4553265_check_post() {

    if (!get_posts($post_id)) {
    // если это новый пост get_posts($post_id) должен вернуть null
    } else {
    // $post_id уже существует в базе данных
    }
}
add_action('save_post','f4553265_check_post');

это непроверено, однако. =)

Другой подход, который использует встроенную функцию и не требует дополнительных изменений в базе данных, включает get_post_status().

$post_status = get_post_status();
if ( $post_status != 'draft' ) {
    //черновик
} else { 
    //не черновик: может быть опубликован, ожидает и т. д.
}

Однако обратите внимание, что это может быть неуместно, если вы планируете позже вернуть статус обратно к “черновику” – ваши инструкции будут повторены в следующий раз, когда вы обновите пост. В зависимости от контекста, вы можете рассмотреть различные строки, которые могут быть возвращены get_post_status(), чтобы построить более подходящий сценарий.

Смотрите Codex для get_post_status() и Статус поста

Возможные значения:

  • ‘publish’ – Опубликованный пост или страница
  • ‘pending’ – пост ожидает проверки
  • ‘draft’ – пост в статусе черновика
  • ‘auto-draft’ – недавно созданный пост, без содержания
  • ‘future’ – пост для публикации в будущем
  • ‘private’ – не виден для пользователей, не вошедших в систему
  • ‘inherit’ – ревизия. см. get_children.
  • ‘trash’ – пост в корзине. добавлено в версии 2.9.

Поскольку параметр $update бесполезен, это самый быстрый способ, который я протестировал:

function wpse48678_check_is_post_new($post_id, $post, $update)
{
    if (false !== strpos($_POST['_wp_http_referer'], 'post-new.php')) {
        return true; // Или сделай что-то другое.
    } else {
        return false; // Или сделай что-то другое.
    }
}
add_action('save_post_{$post_type}', 'wpse48678_check_is_post_new', 10, 3);

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

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

Параметр $update

Параметр $update передаётся функции-обработчику, и его значение будет true, если пост был обновлён, и false, если создаётся новый пост. Это звучит просто, но в реальности поведение этого параметра может быть не совсем очевидно из-за различных сценариев, таких как автоматические сохранения и создание черновиков.

Пример использования:

add_action('save_post', 'my_save_post_function', 10, 3);
function my_save_post_function($post_ID, $post, $update) {
    if (!$update) {
        // Это новый пост
    } else {
        // Это обновление существующего поста
    }
}

Проблемы с определением нового поста

Несмотря на наличие параметра $update, существует ряд сценариев, когда его поведение может вызвать недоразумение. Например, во время создания черновика для нового поста автоматическое сохранение может привести к тому, что $update будет возвращать true, даже если пост создаётся впервые.

Проблемные случаи:

  1. Автосохранение и черновики: При создании нового поста WordPress может автоматически создать черновик (auto-draft), что может повлиять на логику проверки, если вы просто полагаетесь на $update.
  2. Перепубликация поста: Если вы перепубликуете пост, то также произойдёт обновление.

Варианты альтернативных решений

Чтобы избежать путаницы, можно рассмотреть другие методы определения, создаётся ли новый пост или обновляется существующий:

  1. Сравнение дат: Сравнение дат создания и последнего изменения поста – это надёжный способ. Если они совпадают, можно предположить, что пост новый.
function check_new_vs_update($post_id) {
    $post = get_post($post_id);
    $created = new DateTime($post->post_date_gmt);
    $modified = new DateTime($post->post_modified_gmt);
    $diff = $created->diff($modified);

    if ($diff->s <= 1) {
        // Это новый пост
    } else {
        // Это обновление
    }
}
add_action('save_post', 'check_new_vs_update');
  1. Проверка существования мета-значений: Если вы используете метаполя, вы можете проверять существование определённых метаданных.
function attributes_save_postdata($post_id) {
    if (get_post_meta($post_id, '_my_meta_key', true)) {
        // Это обновление существующего поста
    } else {
        // Это новый пост
        update_post_meta($post_id, '_my_meta_key', 'some_value');
    }
}
add_action('save_post', 'attributes_save_postdata');
  1. Использование $_POST: Можно проверять URL в поле $_POST['_wp_http_referer'] на наличие таких значений, как post-new.php.
function check_new_post($post_id) {
    if (strpos(wp_get_raw_referer(), 'post-new.php') !== false) {
        // Новый пост
    } else {
        // Обновление
    }
}
add_action('save_post', 'check_new_post');

Заключение

В общем, определение того, создаётся новый пост или обновляется существующий, можно выполнять несколькими способами. Хоть параметр $update и является самым очевидным из них, важно помнить о нюансах его работы. Использование дополнительных методов проверки, таких как сравнение дат или проверка метаполей, поможет избежать путаницы и обеспечить надёжную обработку данных при создании и обновлении постов в WordPress. Так вы сможете более эффективно управлять своим контентом в WP, избегая ошибок и недоразумений.

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

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