Хук add_action для абсолютно нового поста?
publish_post запускается, когда пост публикуется или когда он редактируется и его статус "опубликован". Аргументы функции действия: ID поста.
Я добавил хук publish_post в WordPress плагин, который я пишу. Функция, вызываемая хуком, должна изменять категории нескольких постов, используя функцию wp_update_post.
Однако этот хук не работает, так как результат выполнения wp_update_post всегда равен 0. Я предполагаю, что выполнение wp_update_post вызывает другой экземпляр моего хука, потому что он повторно публикует пост... что, я думаю, приводит к "...или если он редактируется и его статус "опубликован"" из утверждения выше.
Есть ли другой action-хук, который будет вызываться только тогда, когда добавляется совершенно новый пост, а не редактируется?
<?php
/*
Plugin Name: Category Switcher Plugin
Plugin URI: http://www.example.com
Description: Когда создается новый пост, этот плагин вызовет
Version: 0.1
Author: Me
License: GPL2
?>
<?php
class categoryShifter {
function shiftCategories($post_ID) {
$maxNumPostsFirstTeir = 4;
$first_teir_cat = "Свежие новости 1";
$second_teir_cat = "Устаревшие новости 2";
$firephp = FirePHP::getInstance(true);
$firephp->info('НАЧАЛО: categoryShifter.shiftCategories()');
$firephp->log($post_ID, 'post_ID: ');
$firephp->trace('трассировка до этого места');
$first_teir_id = categoryShifter::getIDForCategory($first_teir_cat, $firephp);
$second_teir_id = categoryShifter::getIDForCategory($second_teir_cat, $firephp);
$firephp->log($first_teir_id, '$first_teir_id');
$firephp->log($second_teir_id, '$second_teir_id');
$qPostArgs = array(
'numberposts' => 100,
'order' => 'DESC',
'orderby' => 'post_date',
'post_type' => 'post',
'post_status' => 'published',
'category_name' => $first_teir_cat
);
$firstTeirPosts = get_posts($qPostArgs);
$firephp->log($firstTeirPosts, 'получены посты:');
$firephp->log(sizeof($firstTeirPosts), 'размер');
// ПРИМЕЧАНИЕ: Похоже, это работает.
for($i = sizeof($firstTeirPosts)-1; $i > $maxNumPostsFirstTeir-4; $i--)
{
$newCats = array($second_teir_id);
$editingId = $firstTeirPosts->ID;
$result = wp_set_post_categories($editingId, $newCats); /* ПРИМЕЧАНИЕ: В настоящее время не работает... возвращает массив с $second_teir_id в нем. */
$firephp->log($result, 'Результат');
}
/*
$my_post = array();
$my_post['ID'] = 132;
$my_post['post_category'] = array($second_teir_id);
$firephp->log('До', 'До');
if(wp_update_post( $my_post ) == 0) {
$firephp->Error('Фатальная ошибка, Пост не обновлен', 'ошибка');
}
$firephp->log('После', 'После');
*/
return $post_ID;
}
function getIDForCategory($cat_name, $logger) {
$logger->Info("Начало: getIDForCategory()");
$cats = get_categories();
$whichCatId = "";
foreach($cats as $single_cat) {
if($single_cat->name == $cat_name) {
$whichCatId = $single_cat->term_id;
break;
}
}
$logger->Info("Конец: getIDForCategory()");
return (int)$whichCatId;
}
}
/* Хук создания поста */
/* add_action('publish_post', array('categoryShifter','shiftCategories')); */
add_action('wp_insert_post', array('categoryShifter', 'shiftCategories'));
?>
Я переключился на использование хука wp_insert_post на данный момент... но я всё ещё не могу заставить функцию wp_set_post_categories изменять категории постов.
Я понимаю, что мне, вероятно, нужно будет обновить этот код, чтобы он учитывал существующие категории поста и изменял только те, которые указаны в плагине, но пока это просто альфа-версия.

// Хук для новых записей при публикации
add_action('new_to_publish', 'your_function');
// Хук для черновиков при публикации
add_action('draft_to_publish', 'your_function');
// Хук для записей на модерации при публикации
add_action('pending_to_publish', 'your_function');

А что насчёт действия wp_insert_post
? http://core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/post.php#L2656

Но будут ли они запускаться снова, если, например, статус изменится с опубликованного обратно на черновик, а затем снова на опубликованный?

@soulseekah Хук wp_insert_post
будет вызван снова при редактировании существующей записи и сохранении. Проверено на WordPress 5.9. Я знаю, что ваш комментарий от 2012 года, но на случай, если кому-то интересно.

Я тщательно изучил ядро WordPress и перепробовал все возможные варианты. wp_transition_post_status()
, new_to_publish()
, new_{post_type}()
, wp_insert_post()
.
Все эти методы, в конечном счете, ненадежны.
wp_transition_post_status()
ненадежен, потому что новый статус "publish" является статусом по умолчанию как для создания новых записей, так и для обновления существующих. Старый статус также ненадежен для определения новой записи, так как он может быть draft, auto-draft, publish и т.д.
new_to_publish()
не работает для пользовательских типов записей.
new_{post_type}
передает только параметр $post, и невозможно определить, создается новая запись или обновляется существующая.
wp_insert_post()
имеет параметр $update, который должен быть TRUE при обновлении существующих записей и FALSE при создании новых, но он ненадежен, так как возвращает TRUE для новых записей из-за auto-draft.
Решение: Использование метаполя записи
В итоге я использовал пользовательское метаполе, которое назвал check_if_run_once
, чтобы выполнить некоторую логику только один раз:
/**
* Выполнить действие при создании новой книги
*/
function new_book($post_id, $post, $update) {
if ( $post->post_type == 'book' && $post->post_status == 'publish' && empty(get_post_meta($post_id, 'check_if_run_once')) ) {
# Новая запись
# Выполнить действия здесь...
# Обновить метаполе, чтобы код не выполнялся повторно
update_post_meta( $post_id, 'check_if_run_once', true );
}
}
add_action( 'wp_insert_post', 'new_book', 10, 3 );
Дополнительно, если вам нужно обновить существующие записи метаполем "check_if_run_once", чтобы код выше не выполнялся для записей, созданных до добавления этой функции, можно сделать следующее:
/**
* Временная функция для обновления существующих записей метаполем "check_if_run_once"
* Для использования войдите на сайт как администратор и добавьте ?debug к URL.
*/
function temporary_function() {
if (current_user_can('manage_options')) {
$posts = get_posts(array(
'post_type' => 'book',
'posts_per_page' => -1, // Возможно, потребуется пагинация в зависимости от количества записей
'fields' => 'ids'
));
foreach($posts as $post_id) {
update_post_meta($post_id, 'check_if_run_once', true);
}
}
}
if (isset($_GET['debug'])) {
add_action('init', 'temporary_function');
}

Точное отслеживание создания новой записи на самом деле сложнее, чем кажется. Технически существует несколько способов, которыми запись может быть создана или обновлена, а также множество не столь очевидных вещей, которые технически также являются записями (например, ревизии).
WordPress предоставляет динамические хуки, которые отслеживают не только создание записи, но и то, чем она была и чем стала. См. Переходы статусов записей в Кодексе.

Больше экспериментируя, чем следуя документации, я нашел рабочее решение (для WP 3.3). Хук transition_post_status срабатывает с параметром $new_status, установленным в "auto-draft", при создании нового поста.
function my_post_new($new_status, $old_status=null, $post=null){
if ($new_status == "auto-draft"){
// здесь выполняем нужные действия
}
}
add_action('transition_post_status', 'my_post_new');

Это будет срабатывать каждый раз при сохранении авто-черновика, т.е. примерно каждую минуту во время редактирования.

Верное замечание. В моем случае это не проблема, но для других может быть важно, в зависимости от того, что вы делаете.

хотел проголосовать за, но пока не могу. так что респект тут, @PapaFreud. кстати, теперь это задокументировано на http://codex.wordpress.org/Post_Status_Transitions. еще раз, спасибо, чувак

@soulseekah Интересный момент, который ты поднял. Я проверил это, и статус auto-draft
присваивается как $new_status
записи при её создании (т.е. при нажатии "Добавить новую"), только один раз. Как только срабатывает первая процедура AUTOSAVE
через 1 минуту, значение $new_status
обновляется до draft
. $old_status
остаётся NULL
до тех пор, пока запись не будет сохранена вручную или опубликована. Так что технически это будет работать и не срабатывать каждый раз, когда редактор автоматически сохраняет вашу работу. В качестве дополнительной меры вы можете проверить, что $old_status
равен NULL
, чтобы убедиться -> if ($new_status == "auto-draft" && $old_status === NULL)

Я обнаружил, что лучшим вариантом будет проверять статус записи при вызове wp_insert_post
.
Когда создаётся новая запись, её первоначальный статус — auto-draft
.
if (!function_exists('benyonsFunction')) {
function benyonsFunction($postId, $post) {
if ($post->post_status == "auto-draft") {
error_log('НОВАЯ ЗАПИСЬ!!!');
}
}
}
add_action('wp_insert_post', 'benyonsFunction', 10, 2);
Этот error_log сработает только один раз.

Наиболее чистый и надежный способ обработки этого для известного типа записи - использовать хук {$new_status}_{$post->post_type}
, который предоставляет старый статус и сравнить его с "publish".
Например, если вы хотите выполнить действие при создании новой "страницы", вы должны использовать:
add_action( 'publish_page', function( int $post_id, \WP_Post $post, string $old_status ) {
if ( 'publish' === $old_status ) {
// Ничего делать не нужно.
return;
}
// Здесь размещается бизнес-логика.
}, 10, 3 );

Нет, это будет срабатывать каждый раз, когда страница переходит из любого статуса в 'publish', поэтому это будет происходить гораздо чаще, чем только при первом создании.
