Come ottenere tutti i figli e nipoti di un custom post type gerarchico?
Ho bisogno di ottenere tutti i sotto-post di un ID genitore specifico (root).
get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $root_parent_id, 'suppress_filters' => false ) );
WP-Codex: la funzione get_post() ha il parametro post_parent ma non child_of.
Il vantaggio della funzione get_pages() in combinazione con il parametro child_of è "... Nota che il parametro child_of recupererà anche i 'nipoti' dell'ID dato, non solo i discendenti diretti."*
Dovrai iterare su quei post e poi eseguire più query per ogni post, ripetendo finché non trovi più post in una query.
Ad esempio:
function get_posts_children($parent_id){
$children = array();
// recupera i figli del post
$posts = get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $parent_id, 'suppress_filters' => false ));
// ora recupera i nipoti
foreach( $posts as $child ){
// ricorsione!! evviva
$gchildren = get_posts_children($child->ID);
// unisci i nipoti nell'array dei figli
if( !empty($gchildren) ) {
$children = array_merge($children, $gchildren);
}
}
// unisci i discendenti diretti trovati prima
$children = array_merge($children,$posts);
return $children;
}
// esempio di utilizzo, chiamiamola e stampiamo i risultati
$descendants = get_posts_children($post->ID);
echo '<pre>';
print_r($descendants);
echo '</pre>';
Sì, la funzione sopra richiama sé stessa, è una funzione ricorsiva. Continuerà a chiamarsi finché non raggiunge un punto in cui il post esaminato non ha figli, poi tornerà senza chiamarsi e l'intero stack risalirà costruendo l'array dei figli. Faresti bene a fare ulteriori ricerche in questo ambito.
Nota che c'è un costo intrinseco in ciò che vuoi fare, indipendentemente dal fatto che tu usi funzioni ricorsive o meno, legato a quanti livelli di post hai. 5 livelli di post saranno più costosi di 2, e la scalabilità non è lineare. Potresti voler usare i transient per memorizzare nella cache l'output a seconda di come lo implementi.
Un altro modo per ridurre il costo è limitare la profondità dell'albero dei post da esaminare, ad esempio solo fino ai nipoti ma non ai pronipoti. Questo può essere fatto passando un parametro di profondità e decrementandolo ad ogni chiamata ricorsiva, assicurandosi di restituire un array vuoto all'inizio se la profondità è 0 o inferiore. Molti tutorial sulle funzioni ricorsive usano questo come esempio.
Il problema con questo codice è che non fornisce un ordinamento corretto; ottengo prima tutti gli elementi di primo livello, poi tutti quelli di secondo livello in un unico array appiattito. Qualche idea su come risolvere?
dama_do_bling
Avevo un requisito simile ma dovevo preservare la gerarchia, quindi la risposta di Tom mi ha dato un buon punto di partenza:
function get_post_offspring($post)
{
// Funzione per scorrere programmaticamente i discendenti di un post
// Ottieni i figli diretti del post corrente
// e aggiungili a un nuovo elemento dell'oggetto chiamato children
$post->children = get_children(array("post_parent" => $post->ID, "post_type" => "page", "post_status" => "publish"));
// se il post non ha figli, restituisci semplicemente il post
// con children come array vuoto
if (empty($post->children)) {
return $post;
}
foreach ($post->children as $child) {
// Per ogni figlio di questo post...ottieni i figli
$children = get_children(array("post_parent" => $child->ID, "post_type" => "page", "post_status" => "publish"));
if (!empty($children)) {
// se questo post ha figli...allora chiama nuovamente questa funzione
// per assegnare l'elemento children a questo post e scendere ulteriormente
// nella discendenza di questo post
$child = get_post_offspring($child);
}
}
return $post;
}
Ora avevo anche bisogno di attraversare la lista con un mantenendo l'ordine e il livello.
function render_list_items($children)
{
foreach ($children as $child) {
$has_children = !empty($child->children);
echo "<li>";
echo "<a href='" . get_permalink($child) . "'>" . $child->post_title . "</a";
if ($has_children) {
// Se il post ha figli, crea un nuovo <ul> dentro l'attuale
// <li> e richiama la funzione nuovamente
echo "<ul>";
echo render_list_items($child->children);
echo "</ul>";
}
echo "</li>";
}
}
Quindi, per mettere tutto insieme ho qualcosa come:
global $post;
$post_id = $post->ID;
// Ottieni gli antenati del post corrente dove l'ultimo elemento dell'array
// è il genitore più in alto
$ancestors = get_post_ancestors($post->ID);
$root_id = array_pop($ancestors);
// Ora abbiamo il post radice di questo post e scendiamo per prendere tutta la sua discendenza
$root_post = get_post($root_id);
$root_children = get_post_offspring($root_post);
Basta usare get_page_children(). Funziona per ogni tipo di post (non solo per le pagine) ed è essenzialmente quello che @TomJNowell ha mostrato nell'altra domanda, ma già implementato nel core.
$children = get_page_children( $post->ID, $GLOBALS['wp_query'] );
L'esempio sopra è come nel Codex. Ecco perché puoi semplicemente prendere l'oggetto query globale (o qualsiasi altro oggetto query) da usare come base di ricerca.
Utilizza questo shortcode per visualizzare tutti i figli e nipoti in una vista gerarchica. Utilizzo: [my_children_list] oppure [my_children_list page_id=123]
function my_children_list_func($atts, $content = null) {
global $post;
$a = shortcode_atts( array(
'page_id' => ''
), $atts );
$args = array(
'numberposts' => -1,
'post_status' => 'publish',
'post_type' => 'microsite',
'post_parent' => (isset($a['page_id']) && $a['page_id']) ? $a['page_id'] : $post->ID,
'suppress_filters' => false
);
$parent = new WP_Query( $args );
ob_start();
if ( $parent->have_posts() ) :?>
<ul>
<?php while ( $parent->have_posts() ) : $parent->the_post(); ?>
<li><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a>
<?php echo do_shortcode('[tadam_children_list page_id='.get_the_ID().']') ?>
</li>
<?php endwhile;?>
</ul>
<?php endif; wp_reset_postdata();
return ob_get_clean();
}
add_shortcode( 'my_children_list', 'my_children_list_func' );