Вопрос или проблема
У нас есть плагин, который позволяет управлять пользовательскими типами публикаций, и мы хотели бы добавить функциональность AJAX для сохранения, редактирования и удаления публикаций. Я не мог найти подобных проблем в интернете, поэтому задаюсь вопросом, насколько это сложно реализовать?
Технически вы можете сделать XHR запрос к post.php через JavaScript. Ниже приведен рабочий пример для сохранения/редактирования публикаций только. Я вообще не тестировал это, так что я уверен, что вам придется подкорректировать его. Я хотел дать вам базовый рабочий процесс для достижения чего-то подобного. Вы можете взять это и доработать, если вам нужно расширить функционал.
Шаг 1: Добавьте обработчик AJAX к admin_head для новых и существующих публикаций.
function my_post_type_xhr(){
global $post;
if('my_post_type' === $post->post_type){
$post_url = admin_url('post.php'); #На случай, если мы находимся на post-new.php
echo "
<script>
jQuery(document).ready(function($){
//Обработчик клика - возможно вам нужно будет привязать это событие клика другим способом
$('input#publish, input#save-post').click(function(){
//Запрос к post.php
var postURL = '$post_url';
//Собираем все данные формы публикации
var data = $('form#post').serializeArray();
//Устанавливаем триггер для нашего действия save_post
data.push({foo_doing_ajax: true});
//Отправляем XHR запрос
$.post(postURL, data, function(response){
var obj = $.parseJSON(response);
if(obj.success)
alert('Публикация успешно сохранена!');
else
alert('Что-то пошло не так. ' + response);
});
return false;
});
});
</script>";
}
}
add_action('admin_head-post.php', 'my_post_type_xhr');
add_action('admin_head-post-new.php', 'my_post_type_xhr');
Шаг 2: Подключитесь к действию save_post.
Это выполняется после того, как публикация была сохранена в базе данных, так что вы можете сохранить любую необходимую метаинформацию и остановить рендер страницы.
add_action('save_post', 'save_my_post_type');
function save_my_post_type($post_id){
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
#Если это ваш тип публикации
if('my_post_type' === $_POST['post_type']){
//Сохраните любую мету публикации здесь
#Условно выходим, чтобы не возвращать полный загрузчик wp-admin, если foo_doing_ajax истинно
if(isset($_POST['foo_doing_ajax']) && $_POST['foo_doing_ajax'] === true){
header('Content-type: application/json');
#Отправляем ответ
echo json_encode(array('success' => true));
exit;
#Вы должны сохранить это условие, чтобы корректно обрабатывать случай, когда JS отключен
}
}
}
Единственное серьезное ограничение, которое я вижу, это то, что вам нужно будет обновить nonce на странице каким-то образом через XHR.
Что касается удаления публикации, я не совсем понимаю, зачем вам использовать AJAX, когда достаточно простого нажатия на ссылку, чтобы выполнить эту задачу. Удаление страницы в любом случае отправит вас на другую страницу, а именно на edit.php. Это идет вразрез с целью использования AJAX, которая заключается в том, чтобы все было асинхронно при загрузке одной страницы.
Надеюсь, это поможет вам.
Ответ Брайана Фегтера был правильной идеей, но имел несколько ошибок. Вот более polished решение, основанное на том же принципе.
Шаг 1: PHP Логика.
Поместите в functions.php (или файл плагина)
// Сохранение публикации через AJAX
add_action('save_post', 'save_post_ajax');
function save_post_ajax( $post_id )
{
# Игнорируем автосохранения
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
return;
# Только включено для одного типа публикации
# Удалите это условие, если хотите включить для всех типов публикаций
if ($_POST['post_type'] == 'my_custom_post_type')
{
# Отправляем JSON ответ
# ЗАМЕТКА: Мы используем ==, а не ===, потому что значение может быть строкой("true")
if (isset($_POST['save_post_ajax']) && $_POST['save_post_ajax'] == TRUE)
{
header('Content-type: application/json');
echo json_encode(array('success' => true));
# Не возвращаем полный wp-admin
exit;
}
}
}
Шаг 2: Создайте JavaScript в отдельном файле
Решение Брайана вывода js непосредственно на страницу также работает.
// Избегаем конфликтов с другими библиотеками
(function($) {
// Убедитесь, что документ готов
$(document).ready(function() {
// Это URL для post.php, который мы локализовали (через php) выше
var url = ajax_object.post_url;
// Сериализуем данные формы
var data = $('form#post').serializeArray();
// Сообщаем PHP, что мы делаем
// ЗАМЕТКА: "name" и "value" - это ключи массива. Это важно. Я использую int(1) для значения, чтобы убедиться, что мы не получаем строку на стороне сервера.
data.push({name: 'save_post_ajax', value: 1});
// Заменяет wp.autosave.initialCompareString
var ajax_updated = false;
/**
* Заменяет функцию WP beforeunload, чтобы убрать
* диалог подтверждения при покидании страницы (если мы сохранили через ajax)
*
* Следующий код ДОЛЖЕН работать в $.post.done(), но
* по какой-то причине, wp.autosave.initialCompareString не меняется
* когда вызывается из wp-includes/js/autosave.js
* wp.autosave.initialCompareString = wp.autosave.getCompareString();
*/
$(window).unbind('beforeunload.edit-post');
$(window).on( 'beforeunload.edit-post', function() {
var editor = typeof tinymce !== 'undefined' && tinymce.get('content');
// Используем нашу переменную "ajax_updated" вместо wp.autosave.initialCompareString
if ( ( editor && !editor.isHidden() && editor.isDirty() ) ||
( wp.autosave && wp.autosave.getCompareString() != ajax_updated) ) {
return postL10n.saveAlert;
}
});
// Отправляем
$.post(url, data, function(response) {
// Проверяем ответ
if (response.success) {
// Отмечаем TinyMCE как сохраненный
if (typeof tinyMCE !== 'undefined') {
for (id in tinyMCE.editors) {
var editor = tinyMCE.get(id);
editor.isNotDirty = true;
}
}
// Обновляем сохраненное содержимое для проверки перед покиданием
ajax_updated = wp.autosave.getCompareString();
console.log('Публикация успешно сохранена');
} else {
console.log('ОШИБКА: Сервер вернул false. ',response);
}
}).fail(function(response) {
console.log('ОШИБКА: Не удалось связаться с сервером. ',response);
});
});
})(jQuery);
Шаг 3: Подключите ваш файл JavaScript
Если вы вывели его (как Брайан), вам не нужно это делать. Я предпочитаю этот метод, потому что он позволяет нам отключать скрипт, локализировать переменные и легко регулировать порядок загрузки скриптов.
function my_post_type_xhr()
{
global $post;
# Только для одного типа публикации.
if ($post->post_type == 'custom_post_type')
{
# URL для js файла, который мы создали выше
$url="/url/to/my/javascript.js";
# Регистрируем и подключаем скрипт, в зависимости от jquery
wp_register_script( 'my_script', $url, array('jquery') );
wp_enqueue_script( 'my_script' );
# Локализуем наши переменные для использования в нашем js скрипте
wp_localize_script( 'my_script', 'ajax_object', array(
'post_id' => $post_id,
'post_url' => admin_url('post.php'),
) );
}
}
add_action('admin_head-post.php', 'my_post_type_xhr');
add_action('admin_head-post-new.php', 'my_post_type_xhr');
Этот фрагмент не касается nonce. В любом случае, надеюсь, что это поможет кому-то.
Вот более polished решение 😉
- Исправить ошибку TinyMce
- Добавить слушатель события отправки формы.
- Скрыть спиннер после завершения запроса и снова активировать кнопку сохранения.
Также я сделал его короче для удобства использования 🙂 просто вставьте этот код в ваш functions.php:
<?php
// Сохранение публикации через AJAX
add_action( 'save_post', function ( $post_id ) {
# Игнорируем автосохранения
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
# Только включено для одного типа публикации
# Удалите это условие, если хотите включить для всех типов публикаций
if ( $_POST['post_type'] === 'post' ) {
# Отправляем JSON ответ
# ЗАМЕТКА: Мы используем ==, а не ===, потому что значение может быть строкой("true")
if ( isset( $_POST['save_post_ajax'] ) && $_POST['save_post_ajax'] == true ) {
wp_send_json_success();
}
}
} );
function my_post_type_xhr() {
# Только для одного типа публикации.
if ( get_post_type() === 'post' ) {
?>
<script>
// Избегаем конфликтов с другими библиотеками
(function ($) {
// Убедитесь, что документ готов
$(document).ready(function () {
$(document).on('submit', 'form#post', function (e) {
e.preventDefault()
// Это URL для post.php, который мы локализовали (через php) выше
var url="<?= admin_url( "post.php' ) ?>'
// Сериализуем данные формы
var data = $('form#post').serializeArray()
// Сообщаем PHP, что мы делаем
// ЗАМЕТКА: "name" и "value" - это ключи массива. Это важно. Я использую int(1) для значения, чтобы убедиться, что мы не получаем строку на стороне сервера.
data.push({name: 'save_post_ajax', value: 1})
// Заменяет wp.autosave.initialCompareString
var ajax_updated = false
/**
* Заменяет функцию WP beforeunload, чтобы убрать
* диалог подтверждения при покидании страницы (если мы сохранили через ajax)
*
* Следующий код ДОЛЖЕН работать в $.post.done(), но
* по какой-то причине, wp.autosave.initialCompareString не меняется
* когда вызывается из wp-includes/js/autosave.js
* wp.autosave.initialCompareString = wp.autosave.getCompareString();
*/
$(window).unbind('beforeunload.edit-post')
$(window).on('beforeunload.edit-post', function () {
var editor = typeof tinymce !== 'undefined' && tinymce.get('content')
// Используем нашу переменную "ajax_updated" вместо wp.autosave.initialCompareString
if ((editor && !editor.isHidden() && editor.isDirty()) ||
(wp.autosave && wp.autosave.getCompareString() !== ajax_updated)) {
return postL10n.saveAlert
}
})
// Отправляем
$.post(url, data, function (response) {
// Проверяем ответ
if (response.success) {
// Отмечаем TinyMCE как сохраненный
if (typeof tinyMCE !== 'undefined') {
for (id in tinyMCE.editors) {
if (tinyMCE.get(id))
tinyMCE.get(id).setDirty(false)
}
}
// Обновляем сохраненное содержимое для проверки перед покиданием
ajax_updated = wp.autosave.getCompareString()
console.log('Публикация успешно сохранена')
} else {
console.log('ОШИБКА: Сервер вернул false. ', response)
}
}).fail(function (response) {
console.log('ОШИБКА: Не удалось связаться с сервером. ', response)
}).done(function (){
if ( wp.autosave ) {
wp.autosave.enableButtons();
}
$( '#publishing-action .spinner' ).removeClass( 'is-active' );
})
return false
})
})
})(jQuery)
</script>
<?php
}
}
add_action( 'admin_footer-post.php', 'my_post_type_xhr', 999 );
add_action( 'admin_footer-post-new.php', 'my_post_type_xhr', 999 );
.
Ответ или решение
Внедрение AJAX для сохранения постов в WordPress
Введение
На сегодняшний день многие разработчики сталкиваются с задачей улучшения пользовательского интерфейса своих приложений. Один из способов этого достижения — использование AJAX (Asynchronous JavaScript and XML) для асинхронного сохранения, редактирования и удаления постов в WordPress. Подход, описанный ниже, направлен на решение данной проблемы с помощью вашей пользовательской темы или плагина.
Шаг 1: Создание обработчика AJAX в файле PHP
Первым шагом вам необходимо создать обработчик, который будет отвечать на AJAX-запросы. Ниже представлен пример кода, который вы можете добавить в ваш functions.php
файл темы:
add_action('save_post', 'save_post_ajax');
function save_post_ajax($post_id) {
// Игнорируем автоматические сохранения
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
// Убедитесь, что это ваш пользовательский тип поста
if ($_POST['post_type'] == 'my_custom_post_type') {
// Проверяем наличие сигнала от AJAX
if(isset($_POST['save_post_ajax']) && $_POST['save_post_ajax'] == TRUE) {
// Отправляем JSON ответ
wp_send_json_success();
}
}
}
Шаг 2: Внедрение JavaScript для вызова AJAX-запросов
Теперь необходимо создать JavaScript, который будет выполнять AJAX-запрос. Вы можете как встроить его прямо на страницу, так и разместить в отдельном файле. Для второй опции добавьте эту функцию в ваш functions.php
файл:
function my_post_type_xhr() {
if (get_post_type() === 'my_custom_post_type') {
?>
<script>
jQuery(document).ready(function($) {
$(document).on('submit', 'form#post', function(e) {
e.preventDefault();
var url = "<?php echo admin_url('post.php'); ?>";
var data = $(this).serializeArray();
data.push({name: 'save_post_ajax', value: 1});
$.post(url, data, function(response) {
if (response.success) {
console.log('Пост успешно сохранён');
} else {
console.log('Ошибка: сервер вернул ложный ответ', response);
}
}).fail(function(response) {
console.log('Ошибка: не удалось связаться с сервером', response);
});
});
});
</script>
<?php
}
}
add_action('admin_footer-post.php', 'my_post_type_xhr');
add_action('admin_footer-post-new.php', 'my_post_type_xhr');
Шаг 3: Убедитесь, что скрипт загружается корректно
Чтобы убедиться, что ваш JavaScript-файл корректно загружен и привязан к нужным событиям, воспользуйтесь функцией wp_enqueue_script()
. Вы можете изменить вышеуказанную функцию так, чтобы скрипт загружался по необходимости, что также даст возможность локализовать переменные.
Связывание с Nonce
Несмотря на то, что этот пример не включает проверку Nonce, добавление её важно для упрочнения безопасности вашей AJAX-системы. Вы можете создать и проверить Nonce, используя следующие функции:
// Создание Nonce
wp_localize_script('my_script', 'ajax_object', array(
'nonce' => wp_create_nonce('my_nonce')
));
// Проверка Nonce в обработчике
if (!isset($_POST['_ajax_nonce']) || !wp_verify_nonce($_POST['_ajax_nonce'], 'my_nonce')) {
wp_send_json_error();
}
Заключение
Оптимизация процессов сохранения постов с помощью AJAX является отличным способом улучшения пользовательского опыта в WordPress. После внедрения описанных методов, ваши пользователи могут быстро и удобно сохранять данные без необходимости перезагрузки страницы. Это улучшит не только интерфейс приложения, но и общую производительность работы с пользователем.
Проектируя свою реализацию, помните о тестировании и оптимизации кода. Убедитесь, что все функции работают корректно и безопасно, что поможет избежать потенциальных уязвимостей системы.