Как сохранить состояние редактора макета jQuery UI Sortables с перетаскиванием на фронтенде?
Я создаю редактор макета записей на фронтенде с использованием jQuery UI Sortable.
Записи расположены в блоках размером 300px на 250px поверх фонового изображения. Записи создаются и редактируются через админку WordPress, но я хочу позволить администратору сайта настраивать порядок блоков с помощью drag and drop интерфейса на фронтенде.
Я реализовал функционал перетаскивания sortable, но нужно придумать способ сохранения состояния (порядка) блоков. В идеале я хотел бы иметь возможность сохранять состояние как опцию и встроить его в запрос.
Запрос для постов - это простой WP_Query, который также получает данные из пользовательских мета-полей для определения макета отдельных блоков:
$args= array(
'meta_key' => 'c3m_shown_on',
'meta_value'=> 'home' );
$box_query = new WP_Query($args); ?>
<ul id="sortable">
<?php
while ($box_query->have_posts()) : $box_query->the_post(); global $post; global $prefix;
$box_size = c3m_get_field($prefix.'box_size', FALSE);
$box_image = c3m_get_field($prefix.'post_box_image', FALSE);
$overlay_class = c3m_get_field($prefix.'overlay_class', FALSE);
if ( c3m_get_field($prefix.'external_link', FALSE) ) {
$post_link = c3m_get_field($prefix.'external_link', FALSE);
} else
{ $post_link = post_permalink();
} ?>
<li class="<?php echo $box_size;?> ui-state-default">
<article <?php post_class() ?> id="post-<?php the_ID(); ?>">
<?php echo '<a href="'.$post_link.'" ><img src="'.esc_url($box_image).'" alt="Изображение с xxxxx.com" /></a>'; ?>
<div class="post-box <?php echo $overlay_class;?>">
<?php if ( c3m_get_field( $prefix.'text_display', FALSE) ) { ?>
<h2><a href="<?php echo $post_link?>"><?php the_title();?></a></h2>
<p><?php echo substr($post->post_excerpt, 0, 90) . '...'; ?></p>
<?php } ?>
</div>
</article>
</li>
<?php endwhile; ?>
</ul>
</section>
JavaScript - это просто базовые инструкции sortable по умолчанию
jQuery(document).ready(function() {
jQuery("#sortable").sortable();
});
Существуют методы с использованием cookies для сохранения состояния, но мне также нужно отключить перетаскивание sortable для пользователей без прав администратора, поэтому мне действительно нужно сохранять в базу данных.
Я ищу наиболее креативный и удобный метод и назначу награду в 100 баллов за лучший ответ.
Обновление:
Я заставил ответ somatic работать с одним небольшим изменением.
ajaxurl не возвращает значение на не-административных страницах, поэтому я использовал wp_localize_script( 'functions', 'MyAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
для определения значения и изменил строку javascript в опциях на:
url: MyAjax.ajaxurl,
Чтобы ограничить доступ к изменению порядка только для администраторов, я добавил условие в мою функцию wp_enqueue_script:
function c3m_load_scripts() {
if ( current_user_can( 'edit_posts' ) ) {
wp_enqueue_script( 'jquery-ui' );
wp_enqueue_script( 'functions', get_bloginfo( 'stylesheet_directory' ) . '/_/js/functions.js', array( 'jquery', 'jquery-ui' ), false);
wp_localize_script( 'functions', 'MyAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
}
}
Я собираюсь провести еще небольшое тестирование и отметить этот вопрос как решенный и выдать награду.
Брейди прав, что лучший способ управления сохранением и отображением порядка пользовательских типов записей — это использование свойства menu_order
.
Вот jQuery-код, который делает список сортируемым и передает данные через AJAX в WordPress:
jQuery(document).ready(function($) {
var itemList = $('#sortable');
itemList.sortable({
update: function(event, ui) {
$('#loading-animation').show(); // Показываем анимацию загрузки во время ожидания
opts = {
url: ajaxurl, // ajaxurl определяется WordPress и указывает на /wp-admin/admin-ajax.php
type: 'POST',
async: true,
cache: false,
dataType: 'json',
data:{
action: 'item_sort', // Указываем WordPress, как обработать этот AJAX-запрос
order: itemList.sortable('toArray').toString() // Передаем ID элементов списка в формате 1,3,2
},
success: function(response) {
$('#loading-animation').hide(); // Скрываем анимацию загрузки
return;
},
error: function(xhr,textStatus,e) { // Можно расширить для получения более подробной информации
alert(e);
// alert('Произошла ошибка при сохранении изменений');
$('#loading-animation').hide(); // Скрываем анимацию загрузки
return;
}
};
$.ajax(opts);
}
});
});
Вот функция WordPress, которая обрабатывает AJAX-запрос и вносит изменения в базу данных:
function my_save_item_order() {
global $wpdb;
$order = explode(',', $_POST['order']);
$counter = 0;
foreach ($order as $item_id) {
$wpdb->update($wpdb->posts, array( 'menu_order' => $counter ), array( 'ID' => $item_id) );
$counter++;
}
die(1);
}
add_action('wp_ajax_item_sort', 'my_save_item_order');
add_action('wp_ajax_nopriv_item_sort', 'my_save_item_order');
Ключевой момент для отображения записей в сохраненном порядке — добавление свойства menu_order
в аргументы запроса:
$args= array(
'meta_key' => 'c3m_shown_on',
'meta_value'=> 'home'
'orderby' => 'menu_order',
'order' => 'ASC'
);
$box_query = new WP_Query($args);
Затем запустите ваш цикл и выведите каждый элемент... (первая строка — это стандартная анимация загрузки WordPress, которую нужно изначально скрыть через CSS, а затем функция jQuery будет отображать её при обработке)
<img src="<?php bloginfo('url'); ?>/wp-admin/images/loading.gif" id="loading-animation" alt="Анимация загрузки" title="Загрузка..." />
<ul id="sortable">
<li id="{echo post ID here}">{echo title or other name here}</li>
</ul>
Код вдохновлен отличным руководством soulsizzle.

Большое спасибо за это - очень помогло с тем, над чем я работал. Одна проблема, с которой я столкнулся, заключается в том, что каждый элемент в сортируемом списке должен иметь ID, соответствующий ID записи. Это было в уроке soulsizzle, но не в оригинальном посте.

Еще далеко до завершения, но идея заключается в отправке AJAX-запроса при перетаскивании. Также можно настроить отправку AJAX-запроса только после нажатия кнопки "сохранить" или подобной. Будет отправляться массив, содержащий ID записей и новый порядок.
Затем на стороне сервера нужно обновить записи в базе данных. Наконец, добавьте параметр order
в ваш цикл WP_Query
.
Надеюсь, это поможет вам начать. Любой может продолжить дорабатывать пример.

/**
* Подключение javascript и css файлов
*/
function uc_enqueue_my_assets() {
wp_enqueue_script( 'jquery-ui-sortable');
wp_register_script( 'order', plugins_url( '/js/order.js', __FILE__ ), array( 'jquery' ) );
wp_enqueue_script( 'order' );
}
function uc_is_user_logged_in()
{
if ( is_user_logged_in()) {
add_action( 'wp_enqueue_scripts', 'uc_enqueue_my_assets' );
add_action( 'admin_enqueue_scripts', 'uc_enqueue_my_assets' );
}
}
add_action('init', 'uc_is_user_logged_in');
/**
* Обновление порядка записей через ajax при срабатывании события перетаскивания
*/
function uc_sort_post_items() {
$order = wp_parse_id_list(explode(',', $_POST['order']));
write_log($order);
global $wpdb;
$list = join(', ', $order);
$wpdb->query('SELECT @i:=0');
$wpdb->query(
"UPDATE wp_posts SET menu_order = ( @i:= @i+1 )
WHERE ID IN ( $list ) ORDER BY FIELD( ID, $list );"
);
wp_die();
}
add_action('wp_ajax_uc_sort_post_items', 'uc_sort_post_items');
add_action('wp_ajax_nopriv_uc_sort_post_items', 'uc_sort_post_items');
/**
* Отображение отсортированных записей
*/
function uc_pre_get_posts( $wp_query ) {
write_log(basename($_SERVER['PHP_SELF']));
$wp_query->set('orderby', 'menu_order');
$wp_query->set('order', 'ASC');
}
add_action( 'pre_get_posts', 'uc_pre_get_posts', 1 );
Javascript файл order.js
$('#the-list').sortable({
update: function(event, ui) {
$.ajax({
url: '/wp-admin/admin-ajax.php',
type: 'post',
dataType: 'json',
data:{
action: 'uc_sort_post_items', // Указываем WordPress как обрабатывать этот ajax запрос
order: '4567,4569,4565 ' // Передает ID элементов списка в формате 1,3,2. Напишите свой собственный js метод для получения списка id из фронтенда.
},
success: function(data, response) {
console.log(response);
},
error: function(xhr,textStatus,e) {
alert(e);
}
});
}
});
