Come evitare un loop infinito nel callback save_post in WordPress
Ho utilizzato molto questo sito per risolvere i miei problemi, ma questa volta non sono riuscito a trovare una risposta alla mia domanda.
Ottengo un loop infinito quando uso wp_update_post
all'interno di una funzione richiamata da save_post
. So che è un problema comune, ma non riesco a capire come evitarlo.
Voglio salvare l'ordine dei miei post (che sono di post-type 'section'). Quindi ho creato un meta box personalizzato che contiene elementi html ordinabili. In ogni elemento c'è un tag input nascosto con name='sectionorder[]'. Quando clicco sul pulsante standard 'Aggiorna' di WordPress, un array contenente tutti gli ID dei post (in ordine) viene inviato via POST. Ecco il codice con cui recupero l'array e voglio salvare l'ordine:
// Aggiorna l'ordinamento delle sezioni
$sectionorder = $_POST['sectionorder'];
if (isset($sectionorder)) { // Evita errori se non ci sono ancora sezioni
foreach( $sectionorder as $no => $sectionID ) {
$post_update = array();
$post_update['ID'] = $sectionID;
$post_update['menu_order'] = $no;
wp_update_post( $post_update );
}
}
Ma il problema è che si crea un loop infinito. Come posso evitarlo? Forse posso farlo in un modo completamente diverso?
Apprezzo il vostro aiuto!

Puoi rimuovere il callback dall'hook save_post
, aggiornare il post e poi riaggiungere il callback all'hook. La Codex fornisce un esempio.
add_action('save_post', 'wpse51363_save_post');
function wpse51363_save_post($post_id) {
//Verifica che non sia una routine di salvataggio automatico
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return;
//Esegui controlli di permesso! Ad esempio:
if ( !current_user_can('edit_post', $post_id) )
return;
//Verifica il tuo nonce!
//Se chiami wp_update_post, scollega questa funzione per evitare un loop infinito
remove_action('save_post', 'wpse51363_save_post');
// chiama wp_update_post per l'aggiornamento, che richiamerà save_post. Esempio:
wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
// riaggancia questa funzione
add_action('save_post', 'wpse51363_save_post');
}

Wow. Grazie per la rapida risposta. Funziona perfettamente! Non so perché non ho visto io stesso quell'esempio di codice..

@Stephen, io uso update_post_meta
in una funzione agganciata a save_post
, dovrei anche scollegare e riagganciare dopo update_post_meta
?

No, update_post_meta
generalmente non causa il trigger di save_post
.

Dopo aver perso un'ora ho trovato questo e mi ha fatto risparmiare ancora più tempo, grazie.

Non ho ancora abbastanza reputazione per commentare quindi aggiungo una risposta anche se quella di Stephen è eccellente e corretta. Semplicemente non gestisce i casi in cui vuoi impostare la priorità dell'azione.
Se imposti la priorità quando aggiungi l'azione ma non specifichi la priorità quando la rimuovi, otterrai comunque un loop infinito.
add_action('save_post', 'wpse51363_save_post', 25 );
// Il modo SBAGLIATO di gestire questo caso - porta a un loop infinito
remove_action('save_post', 'wpse51363_save_post');
wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
add_action('save_post', 'wpse51363_save_post');
// Il modo GIUSTO di gestire questo caso - viene eseguito solo una volta
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 );

Wow grazie! Stavo impazzendo cercando di capire perché continuavo ad avere il loop infinito, anche aggiungendo remove_action/add_action
.

WordPress Codex :: Plugin API/Action Reference/save post :: Evitare loop infiniti Lo dimostrano. Se guardi WordPress Codex :: Function Reference/remove action :: Utilizzo "La priorità della funzione (come definita quando la funzione è stata originariamente agganciata)." Se non specificata, utilizza la priorità predefinita (10). Ovvero - Devi specificare la STESSA priorità con cui l'azione è stata aggiunta, per RIMUOVERE effettivamente l'azione.

Questo è un trucco piuttosto rapido e sporco, ma un modo è creare una variabile globale come $my_plugin_name_saving
e impostarla a true prima di chiamare wp_update_post
e restituire se la variabile è già impostata.
<?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');
