Exclude the parent custom post-type posts only

Not sure if this is possible without some form of hard-coding but I have a custom post-type called ‘city’. Within the ‘city’ post-type I have some parent post (e.g. England, America and China) and some child posts (London, Washington DC and Beijing).

I have a query that displays all of the posts under ‘city’. But for this particular query I’d like to actually ignore ANY parent posts (ie. England, America and China) and only display child posts (London, Washington and Beijing).

Read More

Here is the way I am coding my query:

<?php $city = new WP_Query(array('post_type' => 'city', 'orderby' => 'name', 'order' => 'ASC', 'posts_per_page' => -1 )); ?>
                            <?php while ($city->have_posts() ) : $city->the_post(); ?>
                                <?php the_title(); ?>
                            <?php endwhile; ?>

So is this possible? I would have thought that doing the opposite is more of a popular question (displaying only the parents and excluding the children) but I’m hoping that my problem is possible!

Thanks

Related posts

Leave a Reply

2 comments

  1. According to the codex for WP_Query, the solution might be to use the post_parent as the id of the top level post. But as there are a whole lotta top level cpts in your case, I think this solution should help you: How to display only top level posts in loop via WP_Query?.

    Update:

    <?php
        $all = get_posts(array('post_type'=> 'city', 'posts_per_page' => -1));
        $parents = array();
        foreach ($all as $single)
        {
            $kids = get_children($single->ID);  
            if(isset($kids) && !empty($kids) && count($kids) >= 1)
            {
                $parents[] = $single->ID;
            }
        }
    
        $args = array('post_type' => 'city', 'orderby' => 'name', 'order' => 'ASC', 'posts_per_page' => -1, 'post__not_in' => $parents );
    
        $city = new WP_Query($args);
        while ($city->have_posts() ) : $city->the_post();
                echo get_the_title()."<br />";
        endwhile;
        wp_reset_postdata();
    ?>
    

    This is a long cut, but it works. Just make sure you check if the $parents array is not empty before including it in the query.

  2. What you want, I think, is the posts_where filter, which allows you to add additional WHERE clauses to the SQL query that get_posts generates. This will save you the trip to the database to fetch only parent posts.

    The hooked function will receive two arguments: the WHERE clause itself and the query object. Check to see if the query is for the city post type, then modify the where clause from there.

    <?php
    add_filter( 'posts_where', 'wpse29897_no_parents', 10, 2 );
    function wpse29897_no_parents( $where, $query )
    {
        if( isset( $query->query_vars['post_type'] ) && 'city' == $query->query_vars['post_type'] )
        {
            if( '' != $where )
            {
                $where .= ' AND post_parent !=  0';
            }
            else
            {
                $where .= ' post_parent != 0';
            }
        }
        return $where;
    }
    

    I’m guessing that you don’t want this to happen for EVERY query that involves the city post type, however. So you best bet is probably to stick the add_filter call right above where you’re creating the new WP_Query object. Then stick the wpse29897_no_parents function in your functions.php file or a plugin.

    <?php 
    add_filter( 'posts_where', 'wpse29897_no_parents', 10, 2 );
    $city = new WP_Query(array('post_type' => 'city', 'orderby' => 'name', 'order' => 'ASC', 'posts_per_page' => -1 ));
    while ($city->have_posts() ) : $city->the_post(); 
    ?>
        <?php the_title(); ?>
    <?php endwhile; ?>
    

    You could even remove the filter after your loop just to be sure.

    <?php remove_filter( 'posts_where', 'wpse29897_no_parents', 10, 2 ); ?>