Как избежать бесконечного цикла в save_post callback
Я часто использую этот сайт для решения своих проблем, но на этот раз не смог найти ответ на свой вопрос.
Я получаю бесконечный цикл при использовании wp_update_post
внутри функции, которая вызывается на save_post
. Я знаю, что это распространённая проблема, но не могу понять, как её избежать.
Мне нужно сохранить порядок моих записей (которые относятся к типу 'section'). Я создал пользовательский метабокс, содержащий сортируемые html-элементы. В каждом элементе есть скрытое поле input с name='sectionorder[]'. Когда я нажимаю стандартную кнопку WordPress 'Обновить', массив со всеми ID записей (в порядке сортировки) отправляется через POST. Вот код, где я получаю массив и хочу сохранить порядок:
// Обновляем порядок сортировки секций
$sectionorder = $_POST['sectionorder'];
if (isset($sectionorder)) { // Избегаем ошибки, если секции ещё не добавлены
foreach( $sectionorder as $no => $sectionID ) {
$post_update = array();
$post_update['ID'] = $sectionID;
$post_update['menu_order'] = $no;
wp_update_post( $post_update );
}
}
Проблема в том, что это вызывает бесконечный цикл. Как этого избежать? Возможно, есть другой способ реализации?
Буду благодарен за помощь!

Вы можете удалить колбэк из хука save_post
, обновить запись, а затем снова добавить колбэк к хуку. В Кодексе приведён пример.
add_action('save_post', 'wpse51363_save_post');
function wpse51363_save_post($post_id) {
// Проверяем, что это не автосохранение
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return;
// Проверяем права пользователя
if ( !current_user_can('edit_post', $post_id) )
return;
// Проверяем nonce!
// Если вызываем wp_update_post, отключаем эту функцию, чтобы избежать бесконечного цикла
remove_action('save_post', 'wpse51363_save_post');
// Вызываем wp_update_post для обновления, что снова вызывает save_post. Например:
wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
// Снова подключаем эту функцию
add_action('save_post', 'wpse51363_save_post');
}

Вау. Спасибо за быстрый ответ. Работает отлично! Не понимаю, как я сам не заметил этот пример кода..

@Stephen, я использую update_post_meta
в функции, привязанной к save_post
, нужно ли также отвязывать и снова привязывать после update_post_meta
?

Нет, update_post_meta
(обычно) не вызывает срабатывание save_post
.

Потратив час, нашел это, и это сэкономило мне еще больше времени, спасибо.

У меня пока нет достаточной репутации для комментариев, поэтому я добавляю ответ, хотя ответ Стивена отличный и правильный. Просто он не учитывает случаи, когда нужно установить приоритет действия.
Если вы установите приоритет при добавлении действия, но не укажете его при удалении, это всё равно приведёт к бесконечному циклу.
add_action('save_post', 'wpse51363_save_post', 25 );
// НЕПРАВИЛЬНЫЙ способ обработки — приводит к бесконечному циклу
remove_action('save_post', 'wpse51363_save_post');
wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
add_action('save_post', 'wpse51363_save_post');
// ПРАВИЛЬНЫЙ способ обработки — выполняется только один раз
remove_action('save_post', 'wpse51363_save_post', 25 );
wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
add_action('save_post', 'wpse51363_save_post', 25 );

Вау, спасибо! Я уже сходил с ума, пытаясь понять, почему у меня все еще возникает бесконечный цикл, даже при добавлении remove_action/add_action
.

WordPress Codex :: Plugin API/Action Reference/save post :: Избежание бесконечных циклов Они демонстрируют это. Если посмотреть на WordPress Codex :: Function Reference/remove action :: Использование "Приоритет функции (как определено при первоначальном подключении функции)." Если не указан, используется приоритет по умолчанию (10). То есть - вы должны указать ТОТ ЖЕ приоритет, что и при добавлении действия, чтобы ДЕЙСТВИТЕЛЬНО удалить его.

Это довольно быстрый и грязный трюк, но один из способов — создать глобальную переменную, например $my_plugin_name_saving
, установить её в true перед вызовом wp_update_post
и проверить, не установлена ли переменная заранее.
<?php
/* Plugin Name: My plugin name */
$my_plugin_name_saving = false;
function cc_publish_wpse_263985( $postid ) {
global my_plugin_name_saving;
if ( ! empty( $my_plugin_name_saving ) ) {
return;
}
$my_plugin_name_saving = true;
wp_update_post(array('ID' => $postid, 'post_status' => 'private'));
}
add_action('save_post', 'cc_publish_wpse_263985');
