15

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.

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

please help. Thanks

2
  • 1
    Below you say you found an answer, what is it?
    – Drew Baker
    Commented Aug 17, 2012 at 18:41
  • 4
    Have you checked out get_page_children ?
    – t31os
    Commented Apr 13, 2013 at 13:17

6 Answers 6

6

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 );
?>
5
  • If we use get_pages() functionality we couldn't implement the sorting (sort_column ) for custom fields. It only accept the post table fields only. I need to implement the sorting for custom field. So only i use the wp query(). Is any alternate way?
    – phpuser
    Commented Dec 1, 2011 at 4:07
  • Did you see the second half of my answer, in which I use WP_Query()? Commented Dec 1, 2011 at 13:59
  • I tried this code but it returns the first level sub pages only. I need sub page >> sub's sub >> etc... (multiple lower levels of pages.). Finally i found the solution. Thanks for your reply
    – phpuser
    Commented Dec 3, 2011 at 5:15
  • 7
    what is your solution!?
    – JCHASE11
    Commented Mar 11, 2013 at 20:12
  • There are some semicolons inside of the array definitions above causing syntax errors.
    – ptrin
    Commented Jan 22, 2014 at 3:49
4
+50

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;
3

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.

2

Not sure if this is exactly what you are after, but you could use the wp_list_pages function, and use the 'child_of' and 'depth' parameters.

See the following page on the Codex for more info: http://codex.wordpress.org/Function_Reference/wp_list_pages

-2

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;   
}
1
  • 1
    This is A) not working ($post -> ID?), B) not what was asked for, C) not explained very well.
    – tfrommen
    Commented Sep 26, 2013 at 11:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.