Wp get all the sub pages of the parent using wp query

Here is my code

$my_wp_query = new WP_Query();
$all_wp_pages = $my_wp_query->query(array('post_type' => 'page','post_parent'=>$parid,'orderby'=>'title','order'=>'ASC' ));

It displays the first level sub pages only. I need all the sub page, sub’s sub page … and all. I searched for a solution and i can get all sub pages using get_pages and wp_list_pages.

Read More

But i really need to sort the order by custom post meta value. So i have to use custom query.

please help. Thanks

Related posts

Leave a Reply

6 comments

  1. Why not just use get_pages()?

    e.g.

    <?php
    // Determine parent page ID
    $parent_page_id = ( '0' != $post->post_parent ? $post->post_parent : $post->ID );
    // Get child pages as array
    $page_tree_array = get_pages( array(
        'child_of' => $parent_page_id;
    ) );
    ?>
    

    But if it really must be as a WP_Query() object, use a similar method:

    <?php
    // Determine parent page ID
    $parent_page_id = ( '0' != $post->post_parent ? $post->post_parent : $post->ID );
    // Build WP_Query() argument array
    $page_tree_query_args = array(
        'post_parent' => $parent_page_id;
    );
    // Get child pages as a WP_Query() object
    $page_tree_query = new WP_Query( $page_tree_query_args );
    ?>
    
  2. The Problem

    What you’re having problems grasping is “How do I do X?” This isn’t a 1 step action, it’s a multistep process, and it needs to be broken apart.

    You don’t need to do this:

    get all the posts that are a child of X ordered by meta
    

    You need to do this:

    get all the posts that are a child of X
        for each child, get all the posts that are a child
            foreach child of that child get all the posts that are a child
                ...
                    hmmm we don't have any more children left
    
    Take our list of posts and order them by meta
    

    The General Solution

    So, to understand how to infinitely do it until you reach the end, without hardcoding it, you need to understand recursive functions.

    e.g.

    function make_zero( $amount ) {
        $amount = $amount - 1;
        if ( $amount > 1 ){
            return make_zero( $amount );
        }
        return $amount;
    }
    

    Applying Recursion To This Problem For a Solution

    So your parent is $parid, and your post meta has a key of $metakey.

    Lets pass it into a function to grab its children.

    $children = get_children_with_meta( $parid, $metakey );
    

    Then we’ll sort the $children array, the keys will be the post IDs, and the values will be the meta values.

    asort($children);
    

    and lets define the function as:

    function get_children_with_meta( $parent_id, $metakey ) {
        $q = new WP_Query( array( 'post_parent' => $parent_id, 'meta_key' => $metakey ));
        if ( $q->have_posts() ) {
            $children - array();
            while ( $q->have_posts() ) {
                $q->the_post();
                $meta_value = get_post_meta(get_the_ID(), $metakey, true );
                $children[get_the_ID() ] = $meta_value;
            }
            return $children;
        } else {
            // there are no children!!
            return array();
        }
    }
    

    This gives you an array of post IDs and values, ordered from lowest to highest. You can use other PHP sorting functions to do it from highest to lowest.

    Now What About the Childrens Children?

    In the middle of our loop, we need to make a recursive call, passing in the child rather than the parent ID.

    So this:

    $q->the_post();
    $meta_value = get_post_meta(get_the_ID(), $metakey, true );
    $children[get_the_ID() ] = $meta_value;
    

    Becomes this:

    $q->the_post();
    $meta_value = get_post_meta(get_the_ID(), $metakey, true );
    $children[get_the_ID() ] = $meta_value;
    
    // now get the childrens children
    $grandchildren = get_children_with_meta( get_the_ID(), $metakey );
    
    // merge the grandchildren and the children into the same list
    $children = array_merge( $children, $grandchildren );
    

    With this modification the function now retrieves the children, the childrens children, the childrens childrens children….. etc

    At the end you can trim off the values on the array to get IDs like this:

    $post_ids = array_keys( $children );
    $q = new WP_Query( array( 'post__in' => $post_ids );
    // etc
    

    Using this strategy you can replace the meta key value with any other metric, or use recursive functions in other ways.

    Since the full code requires only a few seconds of basic comprehension and a quick copy paste, I shan’t insult your intelligence with a full copy paste block of code.

    Advantages

    • With modification works for any post type and form of data
    • Can be modified to generate nested markup
    • Easily cache to speed up by putting the returned arrays in transients
    • Can be setup with paging by applying paging to the end WP_Query

    Problems You Will Encounter

    • You have no way of knowing how many children there are until you’ve found them, so performance costs don’t scale
    • What you want will generate a lot of queries, and is inherently costly because of the potential depths involved.

    My Recommendation

    I would recommend you either flatten your page hierarchy or use a taxonomy instead. E.g. if you’re rating posts, have a Page Rating taxonomy with the terms 1,2,3,4 and 5 etc. This will provide you with a post listing out of the box.

    Alternatively, use nav menus and bypass this problem entirely

  3. I’ve made a recursive function that gets all the children ids of a parent page. After we have the ids, we make query for the pages and can order the results by meta key/value.

    // Gets all the children ids of post_parent
    function _get_children_ids( $post_parent ) {
        $results = new WP_Query( array(
            'post_type' => 'page',
            'post_parent' => $post_parent
        ) );
    
        $child_ids = array();
        if ( $results->found_posts > 0 )
            foreach ( $results->posts as $post ) // add each child id to array
                $child_ids[] = $post->ID;
    
        if ( ! empty( $child_ids ) )
            foreach ( $child_ids as $child_id ) // add further children to array
                $child_ids = array_merge( $child_ids, _get_children_ids( $child_id ) );
    
        return $child_ids;
    }
    
    $children_ids = _get_children_ids( 9 ); // use your numeric page id or get_the_id()
    
    $results = new WP_Query( array(
        'post_type'   => 'page',
        'post__in'   => $children_ids
        #'meta_key'   => 'meta_key', // your meta key
        #'orderby'    => 'meta_key',
        /* 'meta_query' => array( // optional meta_query
            array(
                'key' => 'meta_key', // key
                'value' => array(3, 4), // values
                'compare' => 'IN', // operator
            )
        ) */
    ) );
    
    var_dump( $results );
    

    If you need to sort the children by meta key/value in a hierarchical manner, you should pass the meta_key and order_by values to the WP_Query in the _get_children_ids function (instead of the final WP_Query).

    If not, a simpler method to get all the child id is:

    $children = get_pages( 'child_of=9');
    
    $children_ids = array();
    if ( ! empty( $children ) )
        foreach ( $children as $post )
            $children_ids[] = $post->ID;
    
  4. Recursively get all current sub-pages

    Here is a recursive approach using get_children. Put the following in your functions.php:

    function get_all_subpages($page, $args = '', $output = OBJECT) {
        // Validate 'page' parameter
        if (! is_numeric($page))
            $page = 0;
    
        // Set up args
        $default_args = array(
            'post_type' => 'page',
        );
        if (empty($args))
            $args = array();
        elseif (! is_array($args))
            if (is_string($args))
                parse_str($args, $args);
            else
                $args = array();
        $args = array_merge($default_args, $args);
        $args['post_parent'] = $page;
    
        // Validate 'output' parameter
        $valid_output = array(OBJECT, ARRAY_A, ARRAY_N);
        if (! in_array($output, $valid_output))
            $output = OBJECT;
    
        // Get children
        $subpages = array();
        $children = get_children($args, $output);
        foreach ($children as $child) {
            $subpages[] = $child;
    
            if (OBJECT === $output)
                $page = $child->ID;
            elseif (ARRAY_A === $output)
                $page = $child['ID'];
            else
                $page = $child[0];
    
            // Get subpages by recursion
            $subpages = array_merge($subpages, get_all_subpages($page, $args, $output));
        }
    
        return $subpages;
    }
    

    How to use it

    Use the above function wherever you want, for instance like this:

    $all_current_subpages = get_all_subpages(0);
    

    The function does support an args parameter (query string or array) and an output type (see above).

    So you could also use it like so:

    $args = array(
        'post_status' => 'private',
        'order_by' => 'post_date',
        'order' => 'DESC',
    );
    $all_current_subpages = get_all_subpages(42, $args, ARRAY_A);
    

    And due to the dependency get_children => get_posts => WP_Query you can use meta values, as initially requested by the author of this question.

  5. I MAKE THIS WORKING, JUST COPY PASTE THE CODE TO YOUR PAGE.PHP FILE

    //REDIRECT TO FIRST CHILD FROM PARENT PAGE
    
    // Build WP_Query() argument array
    $page_tree_query_args = array(
        'post_parent' => $post -> ID,
        'post_type' => 'page',
        'order' => 'asc'
    );
    // Get child pages as a WP_Query() object
    $page_tree_query = new WP_Query( $page_tree_query_args );
    if(!empty($page_tree_query -> posts)){
        $first_subpage = $page_tree_query -> posts[0] -> ID;
        wp_redirect( get_permalink( $first_subpage ) );
        exit;   
    }