Using the Loop to show all levels of subpages under a parent page? Halfway there

I have a need to make the Loop run through all of the children of a parent, all of the children of each child, etc. The reason I need the Loop instead of a listing tag like wp_list_pages is so that I can display any information associated with the pages using WP template tags and functions. It’s for a showcase-style display that has hierarchical content and gets rearranged, so pages > posts for this usage.

I’ve gotten as far as getting the loop to show children of the current page and an inner loop going to show grandchildren, but this is just a brute-force prototype, and I need to take it to the next level.

What I think I need to do is use 1 loop that starts with post_parent (so that it shows child pages) but insert a function to change the post ID to each child page found so that the loop continues to drill down until it runs out, then backs out 1 level, goes to the next available ID, etc.

My problem is that I’m not enough of a WP programmer (or real programmer at all) to get that going. If it were all out in code I bet I could work it out, but WP has obviously got hidden functions going with have_post and the_post to cycle through the available post IDs, and that has me lost as to how to integrate what I need.

Here’s where I am at the moment. Any help will be greatly appreciated!

<?php   
$args = array(
'posts_per_page' => -1,
'post_parent' => $post->ID,
'post_type' => 'page',
'post_status' => '',
'orderby' => 'title',
'order' => 'ASC',);

query_posts($args); ?>

<?php if(have_posts()) : while (have_posts()) : the_post(); ?>

<a href="https://wordpress.stackexchange.com/questions/13669/<?php the_permalink();?>"><?php the_title();?>:</a>

<?php     $inner_query = new WP_Query("post_type=page&posts_per_page=-1&post_parent={$id}&order_by=title&order=ASC"); ?>

<?php while ($inner_query->have_posts()) : $inner_query->the_post(); ?>

<a href="https://wordpress.stackexchange.com/questions/13669/<?php the_permalink();?>"><?php the_title();?>:</a>

<?php endwhile; // end inner loop ?>
<?php endwhile; //end outer loop ?>     
<?php endif; // end outer if have_posts?>

1
1

Sounds like you’re looking for a recursive function, ie a function that calls itself. Here’s a rough outline of how it can be done:

function wpse13669_show_all_children( $post_id, $current_level ) {
    $children = get_posts( array(
        'post_type' =>'page',
        'posts_per_page' =>-1,
        'post_parent' => $post_id,
        'order_by' => 'title',
        'order' => 'ASC' ) );
    if ( empty($children) ) return;

    echo '<ul class="children level-'.$current_level.'-children">';

    foreach ($children as $child) {

        /* Here would be the point where you
            do whatever you want to display the 
            posts. The variable $current_level can
            be used if you want to style different 
            levels in the hierarchy differently */

            echo '<li>';

        echo '<a href="'.get_permalink($child->ID).'">';
        echo apply_filters( 'the_title', $child->post_title );
        echo '</a>';

        // now call the same function for child of this child
        wpse13669_show_all_children( $child->ID, $current_level+1 );

            echo '</li>';

        }

    echo '</ul>';

    }

Note: edited my code to show the kind of nested <ul> lists it sounds like you’re looking for. If you want to see how WordPress does stuff like this internally (its a lot more complicated than this, but worth figuring out if you need to do anything really custom in your code), you should look through the source code for the class-wp-walker.php file, where the Walker class that handles all the various nested lists (menus, comments, page lists, etc) throughout WP.

If you define that function with the output structure you want and just call it from within your loop, it should do what you’re looking for. I put a $current_level variable in there just so that you can easily style children different from grand-children, and so on.

(within your main loop)

wpse13669_show_all_children( $post->ID, 1 );

Leave a Comment