Вопрос или проблема
Возможно ли в действии 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)
Процесс создания поста примерно следующий:
- После нажатия “Добавить новый” (пост)
- $post = get_default_post_to_edit( $post_type, true ); будет вызван, где
- get_default_post_to_edit() получает аргумент $create_in_db = true
- поэтому wp_insert_post() немедленно вызывается,
auto-draft
пост создаётся, даже если он не сохраняется, каждый раз при нажатииДобавить новый
, создаётсяauto-draft
- $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:
-
проблема проверки между обновлением или вставкой
-
проблема двойной вставки при действии 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
, даже если пост создаётся впервые.
Проблемные случаи:
- Автосохранение и черновики: При создании нового поста WordPress может автоматически создать черновик (
auto-draft
), что может повлиять на логику проверки, если вы просто полагаетесь на$update
. - Перепубликация поста: Если вы перепубликуете пост, то также произойдёт обновление.
Варианты альтернативных решений
Чтобы избежать путаницы, можно рассмотреть другие методы определения, создаётся ли новый пост или обновляется существующий:
- Сравнение дат: Сравнение дат создания и последнего изменения поста – это надёжный способ. Если они совпадают, можно предположить, что пост новый.
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');
- Проверка существования мета-значений: Если вы используете метаполя, вы можете проверять существование определённых метаданных.
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');
- Использование
$_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, избегая ошибок и недоразумений.