Restituire il post genitore con i suoi figli usando WP_Query?
In alcuni casi potrebbe essere utile utilizzare più parametri di post e pagine nel tuo oggetto WP_Query
. Nel mio caso, vorrei visualizzare i figli di una pagina genitore includendo la pagina genitore stessa.
Visualizzazione di ciò che voglio ottenere. Immagina le seguenti pagine ordinate gerarchicamente in questo modo:
- pagina A
- pagina B
- Pagina figlia A
- Pagina figlia B
- Pagina figlia C
- pagina C
Gli elementi in grassetto sono i post/pagine che voglio recuperare.
I miei primi pensieri vanno all'utilizzo di questi due parametri per WP_Query
:
$args = array(
'post_id' => $parent->ID,
'post_parent' => $parent->ID,
);
Sfortunatamente, qui verrà utilizzato solo un parametro. Con gli $args
sopra (correggetemi se sbaglio) verranno mostrati tutti i post figli del post genitore ma non il post genitore stesso.
Questo problema potrebbe essere risolto raccogliendo tutti i post necessari e inserendoli nel parametro post__in
così:
$args = array(
'post__in' => $children_and_parent_ids,
);
Tuttavia esiste wp_list_pages()
che ti permette di include
re uno o più post e specificare il post di cui vuoi includere i figli (child_of
). Perché questo non è possibile con WP_Query
?
Un esempio di ciò che sto cercando di ottenere usando wp_list_pages()
:
wp_list_pages(array(
'include' => $parent->ID,
'child_of' => $parent->ID,
));
Dai un'occhiata alla documentazione di WP_Query
.

Se tutto ciò che vuoi sono i risultati dal post_type "page", allora fai come suggerito da @birgire.
In alternativa, puoi adattare il seguente codice per ottenere un risultato simile non solo per il post_type
page
, ma per qualsiasi tipo di post personalizzato.
$parent = 2; //modifica come desiderato
$type = 'page'; //modifica come desiderato
$child_args = array(
'post_type' => $type,
'post_parent' => $parent
);
$ids = array( $parent );
$ids = array_merge( $ids, array_keys( get_children( $child_args ) ));
$query = new WP_Query(
array(
'post_type' => 'page',
'post_status' => 'publish',
'post__in' => $ids,
'posts_per_page' => -1
)
);
Il codice sopra è essenzialmente la stessa cosa che collegarsi al filtro posts_where
e analizzare la clausola SQL, tuttavia, questo raggiunge esattamente lo stesso risultato.

Possiamo filtrare la clausola posts_where
dell'SQL generato per includere anche il post/pagina genitore e non solo i suoi figli. Qui impostiamo un nostro argomento personalizzato chiamato wpse_include_parent
, che quando impostato a true
, modificherà l'SQL generato di conseguenza.
Tutto ciò che dobbiamo fare all'interno del nostro filtro posts_where
è verificare se il nostro argomento personalizzato è impostato e se è presente l'argomento post_parent
. Otteniamo quindi quel valore e lo passiamo al filtro per estendere la nostra query SQL. La cosa interessante qui è che post_parent
accetta solo un valore intero, quindi dobbiamo solo convalidare il valore come intero.
LA QUERY
$args = [
'wpse_include_parent' => true,
'post_parent' => 256,
'post_type' => 'page'
// Aggiungi altri argomenti
];
$q = new WP_Query( $args );
Come puoi vedere, abbiamo impostato 'wpse_include_parent' => true
per "attivare" il nostro filtro.
IL FILTRO
add_filter( 'posts_where', function ( $where, \WP_Query $q ) use ( &$wpdb )
{
if ( true !== $q->get( 'wpse_include_parent' ) )
return $where;
/**
* Ottieni il valore passato al post genitore e convalidalo
* post_parent accetta solo un valore intero, quindi dobbiamo solo convalidare
* il valore come intero
*/
$post_parent = filter_var( $q->get( 'post_parent' ), FILTER_VALIDATE_INT );
if ( !$post_parent )
return $where;
/**
* Includiamo anche il genitore nella nostra query
*
* Poiché abbiamo già convalidato il valore di $post_parent, non è
* necessario utilizzare il metodo prepare() qui
*/
$where .= " OR $wpdb->posts.ID = $post_parent";
return $where;
}, 10, 2 );
Puoi estendere questo codice come necessario e ritenuto opportuno, ma questa è l'idea di base. Questo restituirà il genitore passato a post_parent
e i suoi figli.

Il codice funziona, ma purtroppo restituisce due volte lo stesso post genitore. Quando esegui una WP_Query
con l'attributo personalizzato.

Ho ritestato il mio codice e non riesco a replicare il tuo problema

dai un'occhiata al mio codice e output. Spero che questo ti aiuti a capire meglio il problema. Prova a mettere la query sulla pagina genitore o figlio stessa.

Ho dato un'occhiata al var_dump()
. Ci sono due post restituiti in $q->posts
, gli ID dei post 2126 e 2116, il che sembra corretto, nessun post duplicato lì. Penso che quello che stai vedendo è il post 2116 disponibile anche in $q->post
, il che è altrettanto corretto. Questo è il valore globale $post
per quella specifica query e sarà sempre il primo post in $q->posts
per impostazione predefinita

Ok ma restituisce sempre due post genitori, anche quando si visualizza il post figlio con questo codice del genitore (quindi il global $post
contiene l'oggetto del post figlio). La domanda è: perché itera 3 volte quando ci sono solo 2 post da iterare?

perché itera 3 volte quando ci sono solo 2 post da iterare Allora questo non ha nulla a che fare con la query. Nell'oggetto query, ci sono due post. Se c'è un terzo post, viene iniettato in questa query tramite qualche filtro personalizzato. Tutto nella tua query sembra OK, anche se la query SQL sembra strana a causa di altri filtri che agiscono sulla query. Dovresti fare debug di questo, assicurarti che $post
sia quello che ti aspetti, verificare che il tuo loop sia corretto e che gli altri filtri utilizzati vengano usati correttamente nel contesto. Su un'installazione vanilla, tutto funziona come previsto dalla mia parte.

$args = array(
'post_type' => 'tribe_events',
'posts_per_page' => '-1',
'orderby' => 'ID',
'order' => 'ASC',
'post_parent' => $postID,
);
$children = new WP_Query($args);
$parent[] = get_post($postID);
$family = array_merge($parent, $children->get_posts());
Sembra funzionare. Commenti?

Vero, ma non sarà efficiente come la risposta accettata. L'approccio della risposta accettata richiede l'esecuzione di una sola query, il tuo approccio comporterà l'esecuzione di due query (se non sono già memorizzate nella cache).

Penso che la tua risposta sia molto meglio!!! Molto più semplice/leggibile!!! E per risparmiare cosa... 1 query? su centinaia nel contesto di WordProut... ed è già stata memorizzata nella cache 30 volte prima di arrivare al tuo codice...
Inoltre la query risparmiata è una WHERE ID=123
.... 0.0001s

Utilizzare global $wpdb
combinato con [get_results()][1]
è un'altra opzione valida. In termini di prestazioni, penso che questa sia la soluzione migliore poiché esegue solo una query.
Ecco il mio codice finale.
<ul class="tabs"><?php
global $wpdb, $post;
$parent = count(get_post_ancestors($post->ID))-1 > 0 ? $post->post_parent : $post->ID;
$sql = "SELECT ID FROM `{$wpdb->prefix}posts`";
$sql.= " WHERE ID='{$parent}' OR post_parent='{$parent}' AND post_type='page'";
$sql.= " ORDER BY `menu_order` ASC";
$tabs = $wpdb->get_results($sql);
$output = '';
foreach ($tabs as $tab) {
$current = $post->ID == $tab->ID ? ' class="active"' : '';
$output .= '<li'.$current.'>';
$output .= empty($current) ? '<a href="'.get_permalink($tab->ID).'">' : '';
$output .= get_the_post_thumbnail($tab->ID, 'menu-24x24');
$output .= '<span>'.get_the_title($tab->ID).'</span>';
$output .= empty($current) ? '</a>' : '';
$output .= '</li>';
}
print $output;
?></ul>

Se ho capito bene, vuoi ottenere gli ID sia della pagina genitore che di tutte le pagine figlie successive. WordPress ha funzioni che recuperano le pagine figlie, come questa:
https://codex.wordpress.org/Function_Reference/get_page_children
Da quello che ho capito, visto che stai eseguendo una WP_Query, stai già recuperando gli ID delle pagine genitore, quindi tutto ciò che devi fare è passare l'ID relativo alla funzione sopra citata per ottenere ciò che desideri.
Nota: dovrei far notare che questa funzione non esegue una query al database, quindi è migliore dal punto di vista delle prestazioni visto che stai facendo una sola query al DB.

Capisco perché menzioni questa funzione, ma questa risposta è più un commento che una risposta. Comunque non riesco a trovare un motivo per cui questa funzione potrebbe essere utile, dato che WP_Query
ti permette già di specificare una lista di post e i loro figli con l'argomento: post_parent
. Quindi perché questa funzione non è deprecata?

Non è quello che voglio ottenere, non voglio usare wp_list_pages
perché l'output non soddisfa le mie esigenze.

Grazie per l'aggiornamento, purtroppo questo non include una spiegazione su come includere il post genitore.

puoi fare riferimento a https://codex.wordpress.org/Function_Reference/get_pages per una spiegazione dettagliata
