Sviluppo WordPress: problema con le custom query nella dashboard.

Sviluppo WordPress: problema con le custom query nella dashboard.

Durante lo sviluppo di un plugin mi sono imbattuto in un bug (o feature?) di WordPress che mi ha fatto perdere non poco tempo in debug e ricerche.

Lo scenario è questo:

  • ambiente di test su WordPress 5.3.2
  • il plugin crea un custom post type.
  • dalla dashboard inserisco e modifico i post di questo CPT come un normale post, usando però l’editor classico (il CPT non è presente nelle REST-API, per cui Gutenberg non funziona).
  • il plugin aggiunge all’admin bar un menu personalizzato, al cui interno sono visualizzate informazioni legate ai post del CPT.
  • per costruire il contenuto del suddetto menu, il plugin esegue una custom query sui dati del CPT.
  • il menu nell’admin bar è visibile in quasi tutte le sezioni della dashboard, per cui la query viene eseguita in pagine diverse.

Per creare la custom query ho utilizzato, come vorrebbe la prassi, la classe WP_Query (doc), con un codice simile a questo (“book” è il nome di un ipotetico CPT):

// WP_Query arguments
$args = array(
	'post_type'      => 'book',
	'posts_per_page' => -1,
);

// The Query
$books = new WP_Query( $args );

// The Loop
if ( $books->have_posts() ) {
	while ( $books->have_posts() ) {
		$books->the_post();
		
		// do something
		the_title();
		the_content();
	}
} else {
	// no posts found
}

// Restore original Post Data
wp_reset_postdata();


Fin qui tutto normale e il risultato finale è quello aspettato: la query viene eseguita e il contenuto generato è mostrato nell’admin bar.

In un caso specifico, tuttavia, si manifesta un problema: quando creo un nuovo post del custom post type, o ne modifico uno esistente, i form di creazione e modifica (rispettivamente le pagine post-new.php e post.php) risultano precompilati con i dati dell’ultimo post caricato dalla custom query, invece di essere vuoti oppure valorizzati con i dati del post in modifica.

Dopo svariati tentativi di debug e ricerche in rete, ho trovato la soluzione in questo ticket ufficiale che documenta il comportamento anomalo: https://core.trac.wordpress.org/ticket/18408.

In sostanza, la funzione wp_reset_postdata() (doc), che serve per ripristinare la variabile globale $post dopo l’esecuzione di una custom query, non riesce a svolgere il suo compito perchè all’interno del backend le cose funzionano in modo leggermente diverso rispetto al frontend.

A causa di ciò la suddetta variabile, a cui fanno riferimento i form di creazione e modifica, resta valorizzata con le informazioni dell’ultimo post letto dalla custom query e non viene reimpostata con le informazioni di un nuovo post o con quelle del post da modificare.

Per avere una visione più dettagliata (e chiara) del comportamento, ti invito a leggere l’intera discussione all’interno del ticket.

La soluzione a questo problema è abbandonare la classe WP_Query in favore della funzione get_posts() (doc), che fortunatamente supporta gran parte dei parametri della classe e per questo la riscrittura del codice non risulta un incubo.

Partendo dall’esempio di prima, otteniamo quanto segue:

// Query arguments
$args = array(
	'post_type'      => 'book',
	'posts_per_page' => -1,
);

// The Query
$books = get_posts( $args );

// The Loop
if ( !empty( $books ) ) {
	foreach ( $books as $book ) {
		// do something
		echo $book->post_title;
		echo $book->post_content;
	}
} else {
	// no posts found
}


Come vedi, l’array con la configurazione è rimasto invariato (almeno in questo semplice caso), mentre il loop non è più quello classico con i template tags. Al posto del while c’è adesso un foreach con cui scorrere tutti gli oggetti restituiti dalla query, ognuno dei quali possiede proprietà che permettono di accedere ai dati del post.

In considerazione di tutto ciò, possiamo dedurre che:

per eseguire una custom query nel frontend è corretto usare la classe WP_Query.

per eseguire una custom query nel backend è corretto usare la funzione get_posts().

Tutto sommato non è un bug gravissimo, anche in virtù della poca differenza di implementazione dei due metodi. Resta il fastidio di dover ricordare due soluzioni diverse in base al contesto e di una scarsa documentazione di questo dettaglio. Per trasparenza ammetto che nella documentazione della classe WP_Query esiste una nota a riguardo, ma si tratta di due righe che si perdono nella quantità di informazioni presenti in quella pagina.

E poi forse, dopo quasi 10 anni, si potrebbe pensare si sistemare la cosa e permettere di usare un solo metodo in tutte le situazioni.

Scrivi ciò che cerchi e poi premi Invio!