Come salvare lo stato di un editor di layout drag and drop con jQuery UI Sortables lato frontend?
Sto creando un editor di layout di post lato frontend utilizzando jQuery UI Sortable.
I post sono disposti in box di 300px per 250px su un'immagine di sfondo. I post vengono creati e modificati tramite l'admin di WordPress ma voglio permettere all'amministratore del sito di regolare l'ordine dei box usando un'interfaccia drag and drop sul frontend.
Ho fatto funzionare la parte drag and drop sortable ma ho bisogno di trovare un modo per salvare lo stato (ordine) dei box. Idealmente vorrei poter salvare lo stato come opzione e integrarlo nella query.
La query per i post è una semplice WP_Query che ottiene anche dati da custom meta box per determinare il layout dei singoli box:
$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="Immagine via 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>
Il javascript è solo l'istruzione base di sortable predefinita
jQuery(document).ready(function() {
jQuery("#sortable").sortable();
});
Ci sono metodi disponibili usando i cookie per salvare lo stato ma ho anche bisogno di disabilitare il drag and drop sortable per gli utenti non amministratori quindi ho davvero bisogno di salvare nel database.
Sto cercando il metodo più creativo e utilizzabile e assegnerò una ricompensa di 100 punti alla migliore risposta.
Aggiornamento:
Ho fatto funzionare la risposta di somatic con una piccola modifica.
ajaxurl non restituisce il valore nelle pagine non admin quindi ho usato wp_localize_script( 'functions', 'MyAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
per definire il valore e ho modificato la riga javascript sotto options in:
url: MyAjax.ajaxurl,
Per limitare l'accesso al riordinamento solo agli amministratori ho aggiunto una condizione alla mia funzione 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' ) ) );
}
}
Farò un po' più di test e segnerò questa domanda come risolta e assegnerò la ricompensa.
Brady ha ragione nel dire che il modo migliore per gestire il salvataggio e la visualizzazione dell'ordine dei custom post type è utilizzare la proprietà menu_order
Ecco il codice jQuery per rendere la lista ordinabile e passare i dati via ajax a WordPress:
jQuery(document).ready(function($) {
var itemList = $('#sortable');
itemList.sortable({
update: function(event, ui) {
$('#loading-animation').show(); // Mostra l'animazione di caricamento durante l'attesa
opts = {
url: ajaxurl, // ajaxurl è definito da WordPress e punta a /wp-admin/admin-ajax.php
type: 'POST',
async: true,
cache: false,
dataType: 'json',
data:{
action: 'item_sort', // Indica a WordPress come gestire questa richiesta ajax
order: itemList.sortable('toArray').toString() // Passa gli ID degli elementi della lista nel formato 1,3,2
},
success: function(response) {
$('#loading-animation').hide(); // Nasconde l'animazione di caricamento
return;
},
error: function(xhr,textStatus,e) { // Può essere ampliato per fornire maggiori informazioni
alert(e);
// alert('Si è verificato un errore durante il salvataggio degli aggiornamenti');
$('#loading-animation').hide(); // Nasconde l'animazione di caricamento
return;
}
};
$.ajax(opts);
}
});
});
Ecco la funzione WordPress che ascolta il callback ajax ed esegue le modifiche sul database:
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');
La chiave per visualizzare i post nell'ordine salvato è aggiungere la proprietà menu_order
agli argomenti della query:
$args= array(
'meta_key' => 'c3m_shown_on',
'meta_value'=> 'home'
'orderby' => 'menu_order',
'order' => 'ASC'
);
$box_query = new WP_Query($args);
Poi esegui il tuo loop e mostra ogni elemento... (la prima riga è l'animazione di caricamento di WordPress - dovrai nasconderla inizialmente via CSS, e poi la funzione jQuery la mostrerà durante l'elaborazione)
<img src="<?php bloginfo('url'); ?>/wp-admin/images/loading.gif" id="loading-animation" alt="Animazione di caricamento" title="Caricamento in corso..." />
<ul id="sortable">
<li id="{echo post ID here}">{echo title or other name here}</li>
</ul>
Codice ispirato dall'eccellente tutorial di soulsizzle.

Grazie mille per questo - mi ha aiutato tantissimo anche su un progetto su cui stavo lavorando. Un problema che ho incontrato è che ogni elemento nella lista ordinabile deve avere un ID che corrisponda all'ID del post. Questo era presente nel tutorial di soulsizzle ma non nel post originale dell'OP.

Lontano dall'essere completato, ma l'idea è di inviare una richiesta ajax durante il drag and drop. Potresti anche voler attivare la richiesta ajax solo dopo aver cliccato un pulsante "salva" o qualcosa di simile. Verrebbe inviato un array contenente gli ID dei post e il nuovo ordine.
Poi dovresti aggiornare i post nel database lato server. Infine, aggiungi un parametro order
al tuo loop WP_Query
.
Spero che questo ti dia un inizio. Chiunque può continuare a sperimentare.

/**
* Carica i file javascript e 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');
/**
* Aggiorna l'ordine degli articoli via AJAX all'attivazione dell'evento di trascinamento
*/
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');
/**
* Visualizza gli articoli ordinati
*/
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 );
File 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', // Indica a WordPress come gestire questa richiesta ajax
order: '4567,4569,4565 ' // Passa gli ID degli elementi della lista in formato 1,3,2. Scrivi il tuo metodo js per accedere alla lista degli id dal frontend.
},
success: function(data, response) {
console.log(response);
},
error: function(xhr,textStatus,e) {
alert(e);
}
});
}
});

Per favore non limitarti a copiare il codice. Ti viene richiesto di aggiungere una spiegazione sul perché pensi che il codice sopra risponda alla domanda.
