Cómo obtener todos los hijos y nietos de un tipo de contenido personalizado jerárquico
Necesito obtener todos los sub-posts de un ID de padre (raíz) específico.
get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $root_parent_id, 'suppress_filters' => false ) );
WP-Codex: la función get_post() tiene el parámetro post_parent pero no child_of.
La ventaja de la función get_pages() en combinación con el parámetro child_of es "... Ten en cuenta que el parámetro child_of también obtendrá los 'nietos' del ID dado, no solo los descendientes directos."*

Necesitarás recorrer esas publicaciones y luego hacer más consultas para cada publicación, repitiendo hasta que no encuentres publicaciones en una consulta.
Por ejemplo:
function get_posts_children($parent_id){
$children = array();
// obtener los hijos de las publicaciones
$posts = get_posts( array( 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => 'microsite', 'post_parent' => $parent_id, 'suppress_filters' => false ));
// ahora obtener los nietos
foreach( $posts as $child ){
// recursión!! hurra
$gchildren = get_posts_children($child->ID);
// fusionar los nietos en el array de hijos
if( !empty($gchildren) ) {
$children = array_merge($children, $gchildren);
}
}
// fusionar los descendientes directos que encontramos antes
$children = array_merge($children,$posts);
return $children;
}
// ejemplo de uso de lo anterior, llamémoslo e imprimamos los resultados
$descendants = get_posts_children($post->ID);
echo '<pre>';
print_r($descendants);
echo '</pre>';
Sí, la función anterior se llama a sí misma, es una función recursiva. Seguirá llamándose a sí misma hasta que llegue a un punto donde la publicación que se está mirando no tenga hijos, luego retornará sin llamarse a sí misma, y toda la pila volverá hacia arriba construyendo el array de hijos. Sería bueno que investigaras más en esta área.
Ten en cuenta que hay un costo inherente en lo que quieres hacer, independientemente de si usas funciones recursivas o no, que está ligado a cuántos niveles de publicaciones tengas. 5 niveles de publicaciones serán más costosos que 2, y no es una escala lineal. Es posible que desees usar transients para almacenar en caché tu salida dependiendo de cómo lo hagas.
Otra forma de reducir el costo es mirando solo un cierto número de niveles hacia abajo en el árbol de publicaciones, por ejemplo, nietos pero no bisnietos. Esto se puede hacer pasando un parámetro de profundidad y decrementándolo en cada llamada recursiva, asegurándose de retornar un array vacío al inicio si la profundidad es 0 o menor. Muchos tutoriales sobre funciones recursivas usan esto como ejemplo.

El problema con este código es que no proporciona un orden correcto; obtengo todos los elementos del primer nivel primero, luego todos los del segundo nivel en un solo array aplanado. ¿Alguna pista sobre cómo solucionarlo?

Tuve un requisito similar pero necesitaba preservar la jerarquía, así que la respuesta de Tom me dio un buen punto de partida:
function get_post_offspring($post)
{
// Función para recorrer programáticamente los descendientes de un post
// Obtener hijos directos del post actual
// y agregarlos a un nuevo elemento del objeto llamado children
$post->children = get_children(array("post_parent" => $post->ID, "post_type" => "page", "post_status" => "publish"));
// si el post no tiene hijos, simplemente retornar el post con
// children como un array vacío
if (empty($post->children)) {
return $post;
}
foreach ($post->children as $child) {
// Por cada hijo de este post... obtener sus hijos
$children = get_children(array("post_parent" => $child->ID, "post_type" => "page", "post_status" => "publish"));
if (!empty($children)) {
// si este post tiene hijos... entonces llamar esta función nuevamente
// para asignar el elemento children a este post y seguir bajando
// en la descendencia de este post
$child = get_post_offspring($child);
}
}
return $post;
}
También necesitaba recorrer la lista con un manteniendo el orden y nivel.
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) {
// Si el post tiene hijos, crear un nuevo <ul> dentro del
// <li> actual y llamar la función nuevamente
echo "<ul>";
echo render_list_items($child->children);
echo "</ul>";
}
echo "</li>";
}
}
Entonces, para unirlo todo, tengo algo como:
global $post;
$post_id = $post->ID;
// Obtener ancestros del post actual donde el último elemento del array
// es el padre superior
$ancestors = get_post_ancestors($post->ID);
$root_id = array_pop($ancestors);
// Ahora tenemos el post raíz de este post y descendemos para obtener toda su descendencia
$root_post = get_post($root_id);
$root_children = get_post_offspring($root_post);

Simplemente usa get_page_children()
. Funciona para cualquier tipo de publicación (no solo páginas) y es básicamente lo que @TomJNowell mostró en la otra pregunta, pero ya implementado por el núcleo de WordPress.
$children = get_page_children( $post->ID, $GLOBALS['wp_query'] );
El ejemplo anterior es como aparece en el Codex. Por eso puedes simplemente tomar el objeto de consulta global (o cualquier otro objeto de consulta) para usarlo como base de búsqueda.

Utiliza el siguiente shortcode para mostrar todos los hijos y nietos en una vista jerárquica. Uso: [my_children_list] o [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' );
