Restituire il post genitore con i suoi figli usando WP_Query?

23 mar 2016, 12:57:14
Visualizzazioni: 26.7K
Voti: 8

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 includere 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.

0
Tutte le risposte alla domanda 6
0

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.

23 mar 2016 14:10:26
7

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.

23 mar 2016 20:18:49
Commenti

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

luukvhoudt luukvhoudt
30 mar 2016 12:22:31

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

Pieter Goosen Pieter Goosen
30 mar 2016 14:58:26

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.

luukvhoudt luukvhoudt
30 mar 2016 15:13:25

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

Pieter Goosen Pieter Goosen
30 mar 2016 15:54:17

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?

luukvhoudt luukvhoudt
30 mar 2016 16:14:33

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.

Pieter Goosen Pieter Goosen
30 mar 2016 16:42:38

Purtroppo non posso aiutarti qui perché, come ho detto, tutto funziona correttamente dal mio lato. Inizia disattivando i plugin, esamina tutte le tue variabili e assicurati che siano quelle che dovrebbero essere, e passa a un tema incluso di default

Pieter Goosen Pieter Goosen
30 mar 2016 16:44:07
Mostra i restanti 2 commenti
2
    $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?

22 ott 2017 19:31:35
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).

luukvhoudt luukvhoudt
23 ott 2017 14:21:23

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

Antony Gibbs Antony Gibbs
28 apr 2022 09:49:02
1

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>
23 mar 2016 14:45:45
Commenti

Si potrebbe pensare che sia così, tuttavia WP_Query sotto il cofano memorizza nella cache i risultati delle query, quindi ad esempio, una chiamata a get_children() o get_post_ancestors() oltre a WP_Query non dovrebbe comportare alcun impatto aggiuntivo sulle prestazioni.

Adam Adam
23 mar 2016 14:56:22
1

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.

23 mar 2016 21:11:04
Commenti

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?

luukvhoudt luukvhoudt
23 mar 2016 21:37:12
5
-2

Puoi utilizzare get_pages() con il parametro child_of come mostrato di seguito:

<?php
   get_pages( array(
     'child_of' => $parent_page_id;
   ) );
?>
23 mar 2016 13:45:41
Commenti

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

luukvhoudt luukvhoudt
23 mar 2016 13:48:30

Vedi la mia risposta aggiornata. Spero che questo ti sia d'aiuto.

Lalji Nakum Lalji Nakum
23 mar 2016 13:54:49

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

luukvhoudt luukvhoudt
23 mar 2016 14:00:39

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

Lalji Nakum Lalji Nakum
23 mar 2016 14:06:38

Secondo la documentazione: Il parametro child_of non viene applicato alla query SQL per le pagine. Viene applicato ai risultati della query. Inoltre non restituirà il genitore.

luukvhoudt luukvhoudt
23 ott 2017 14:17:34