I would like to only show posts that have "now and future" children using pre_get_posts action hook. Does anyone have an idea of how to achieve this?

function pre_get_posts($query) {        
    if(!is_admin() && $query->is_main_query()) {
        //check if the query matches this post type
        if(is_post_type_archive('event')) {
            $query->set('post_parent', 0); //only show parent posts
            //should only display posts that have children from now to future

All events with parent_id 0 should show but it should exclude events that have no children events linked to it.


  • Event 1 has 5 children. (include)
  • Event 2 has no children. (exclude)
  • Event 3 has 2 children. (include)
  • Event 4 has 1 child. (include)
  • Event 5 has no children. (exclude)

I have not yet found anything useful but will keep on looking for solutions.

Thank you for any responses.

  • All you need to do is find the ID of your "now to future" page and substitute the 0 for that. To find the ID, edit the page in the admin area and look in the address bar for post=xx - .../wp-admin/post.php?post=50&action=edit – David Gard Feb 23 '15 at 13:34
  • My apologies. I might not have posted it clear. All events with parent_id of 0 should show but it should exclude the events without child events linked to it. – Kobus Beets Feb 23 '15 at 13:39
  • Do you want the children listed as well, or just the parents? – David Gard Feb 23 '15 at 14:06
  • @DavidGard , i think he doesn't want to list children as he is setting post_parent to 0 – bravokeyl Feb 23 '15 at 14:46
  • That's my guess too (see answer), but it's always good to check ;-) – David Gard Feb 23 '15 at 14:48

Before amending $query we first have to find out which posts to exclude, which requres two querys (if I can think of a way of doing it in one I'll update the answer).

The first will grab a list of distinct ID's of all parent events, for example -

SELECT DISTINCT wp_posts.post_parent FROM wp_posts WHERE wp_posts.post_type = "event" AND wp_posts.post_type = "publish" AND wp_posts.post_parent != 0

The next will grab the ID of all top-level events that do not have any children (so those that are not in the list we generated with the previous query), for example -

SELECT fgw_2_posts.ID FROM wp_posts WHERE wp_posts.post_type = "event" AND wp_posts.post_type = "publish" AND wp_posts.post_parent = 0 AND wp_posts.ID NOT IN (1,2,3)

Finally we can set the $query as required -

$query->set('post__not_in', $loners);
$query->set('post_parent', 0);

Here is the full code -

add_action('pre_get_posts', 'my_exclude_parents_without_children');
function my_exclude_parents_without_children($query){

    global $wpdb;

    if($query->is_main_query() && !is_admin()) :

        if(is_post_type_archive('event')) :

            /** First grab the ID's of all post parents */
            $my_query = $wpdb->prepare('SELECT DISTINCT %1$s.post_parent FROM %1$s WHERE %1$s.post_type = "event" AND %1$s.post_status = "publish" AND %1$s.post_parent != 0', $wpdb->posts);
            $parents = $wpdb->get_col($my_query);

            /** Next grab the ID of all posts who do not have children (we'll call them 'loners') */
            $my_query = $wpdb->prepare('SELECT %1$s.ID FROM %1$s WHERE %1$s.post_type = "event" AND %1$s.post_status = "publish" AND %1$s.post_parent = 0', $wpdb->posts, join(',', $parents));
            if(!empty($parents)) : // Ensure that there are events with children to exclude from this query
                $my_query.= $wpdb->prepare(' AND %1$s.ID NOT IN (%2$s)', $wpdb->posts, join(',', $parents));
            $loners = $wpdb->get_col($my_query);

            /** Now exclude the 'loners' */
            $query->set('post__not_in', $loners);
            $query->set('post_parent', 0);



  • please make sure to check for admin pages ( !is_admin ) as it effects edit.php?... – bravokeyl Feb 23 '15 at 14:44
  • @bravokeyl - Ah yes, there is a bug if I remember correctly. is_post_type_archive() should only work front end, but I've edited my answer to take account of this any way. – David Gard Feb 23 '15 at 14:47
  • 1
    For anyone who is interested, this defect was closed as invalid because, while not typically used in the admin area, conditional tags such as is_post_type_archive() are expected to work - core.trac.wordpress.org/ticket/18993. – David Gard Feb 23 '15 at 14:57
  • is_post_type_archive('wpse') also returns true when you are in edit.php?post_type='wpse' . – bravokeyl Feb 23 '15 at 14:57
  • @DavidGard That is a perfect solution for me. You are awesome. I did correct one thing though but it a minor. WHERE %1$s.post_type = "event" AND %1$s.post_status = "publish". Thanks again to all who helped out. – Kobus Beets Feb 24 '15 at 5:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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