3

I have a post category structure like this:

  • news
  • news / conferences
  • news / newsletter

In Appearance > Menu, I have added 3 menu items.

One each for the categories News, Conferences, Newsletter.

However, the News page also displays posts from Conferences and Newsletter.

How do I exclude posts from subcategories from being displayed in the parent category's page?

Thanks

1
  • I'm so curious about this. It even seems to be the behaviour I'm experiencing in the backend admin. See: "Why does admin post list for a term also show posts for all child terms?" wordpress.stackexchange.com/questions/318175/… Is this really standard behaviour? Commented Nov 1, 2018 at 12:11

6 Answers 6

3

I rewrote the code from a post at WP Engineer:

function wpse_filter_child_cats( $query ) {

if ( $query->is_category ) {
    $queried_object = get_queried_object();
    $child_cats = (array) get_term_children( $queried_object->term_id, 'category' );

    if ( ! $query->is_admin )
        //exclude the posts in child categories
        $query->set( 'category__not_in', array_merge( $child_cats ) );
    }

    return $query;
}
add_filter( 'pre_get_posts', 'wpse_filter_child_cats' );

Give it a try by pasting the snippet in your functions.php. Please keep in mind that a post can't belong to both parent category and child category, or you won't get it displayed.

7
  • Hi @1fixdotio. Thanks for helping out. I added this to my child theme's functions.php, but the subcategory posts still display in the parent category.
    – Steve
    Commented Mar 3, 2014 at 13:07
  • Did you get any error messages? May I know did you enable the permalink?
    – 1fixdotio
    Commented Mar 3, 2014 at 14:34
  • @Steve I've tested the code and it could work on my server. Just updated the code a bit, hope that helps.
    – 1fixdotio
    Commented Mar 3, 2014 at 14:53
  • No errors. I've uploaded the updated code and it is still not working. My theme is a child theme of Twenty Twelve. See this page which contains "2012 WA Men’s Shed Conference Presentations" & "The Warbler – WA Men’s Shed newsletter". These two are not in the parent category.
    – Steve
    Commented Mar 4, 2014 at 11:33
  • @Steve I created a child theme for twentytwelve and paste my code in functions.php, it worked well. Could you disable the plugins like jetpack and try again?
    – 1fixdotio
    Commented Mar 4, 2014 at 14:20
3

Use category__in instead of cat in your query.

As you know already, cat will query a category and it's sub categories:

$category = get_category (get_query_var('cat'));
WP_Query( 'cat'=>$category, 'post_type' => 'post' );

However, when using category__in, you'll will not get results from the subcategory.

$category = get_category (get_query_var('cat'));
WP_Query( 'category__in'=>$category, 'post_type' => 'post' );

The documentation on querying based on categories is here.

1
  • Hi and welcome to WP.SE. Keep in mind that get_category() returns OBJECT, ARRAY_A, or ARRAY_N. Assigning it directly won't work. You will have to check the return value and either assign a value via it's assoc/numerical key or via the object property. Make sure to check for its existence before using it.
    – kaiser
    Commented Jan 17, 2015 at 12:41
1

If you want to include any and all posts that have the category that is currently being queried, you can use the following. This could potentially include posts that have sub/child categories of the queried category, but only if they also have the parent category. It will not include posts that only have a child category. In other words, given the following:

  • Category 1

  • Category 2, which is a child category of category 2

  • Post A, which has category 1 only

  • Post B, which has category 2 only

  • Post C, which has both category 1 and category 2

this query will return posts A and C, but not B

function cat_query_filter($query) {
    if (!is_admin() && $query->is_main_query() && is_category()) {
        $queried_cat_obj = get_queried_object();
        $queried_cat_id = $queried_cat_obj->term_id;
        $query->set('category__in', $queried_cat_id);
    }
}
add_action('pre_get_posts', 'cat_query_filter');

The answer provided by @createscape is a good one, but would exclude posts like post B, which may or may not be what you want. So, depending on what you want to be included in your list of results, you could either use that solution or this one. Additional info here: https://developer.wordpress.org/reference/classes/wp_query/#category-parameters

0

That works well and prevents posts in child categories from showing up in the parent category listing. However, make sure and have a post in that parent category, or you'll get a not found error if someone clicks on the parent category menu item.

0

I wrote my own function in order to exclude subcategory posts from the loop, as I found the code above didn't work for me.

In my theme archive.php file, above the loop, I list the subcategories:

    <?php
       $current_cat = get_queried_object();

       $args = array( 'parent'=>$current_cat->term_id, 'child_of' => $current_cat->term_id, );
        $categories = get_categories( $args );
        foreach($categories as $category) { ?>

           <h2><?php echo $category->name ;?></h2>
           <p> etc....</p>
      <?php } ?>

In my functions.php file, I've added the following custom function using pre_get_posts:

add_action( 'pre_get_posts', 'main_query_without_subcategory_posts' );

function main_query_without_subcategory_posts( $query ) {

if ( ! is_admin() && $query->is_main_query() ) {
    // Not a query for an admin page.
    // It's the main query for a front end page of your site.

    if ( is_category() ) {

   //Get the current category
        $current_category = get_queried_object();
        //get the id of the current category
        $current_cat_id = $current_category->term_id;

        //find the children of current category
        $cat_args = array( 'parent'=>$current_category->term_id, 'child_of' => $current_category->term_id, );
        $subcategories = get_categories( $cat_args );

        //Get a list of subcategory ids, stick a minus sign in front
        $subcat_id = array();         
        foreach($subcategories as $subcategory) { 
            $subcat_id[] = " -". $subcategory->term_id; 
        }

        //join them together as a string with a comma seperator          
        $excludesubcatlist = join(',', $subcat_id);

       //If you have multiple parameters, use $query->set multiple times
        $query->set( 'posts_per_page', '10' );
        $query->set( 'cat', ''.$current_cat_id.','.$excludesubcatlist.'' );
      }
    }
  }

Then in the archive.php, below the subcategories, I've added the regular WordPress loop which is now being modified by the above function:

<?php  while (have_posts() ) : the_post(); ?>
     <h2><?php the_title();?></h2>
     <p> etc....</p>
 <?php endwhile;?>

Though the WordPress codex says that using "category__in" will exclude posts from subcategories, that didn't work for me and subcategory posts were still showing.

https://codex.wordpress.org/Class_Reference/WP_Query#Category_Parameters

https://developer.wordpress.org/reference/hooks/pre_get_posts/

0

Okay, as of 2019 I had to use slightly modified code and it works with any taxonomy. Not only with standard category. I used it with my setup of woocommerce and wp, preventing products from nested categories being displayed in parent cat:

add_action('parse_tax_query', 'wpse_filter_child_cats'); 

function wpse_filter_child_cats( $query ) {
    if ( is_tax( 'product_cat' ) ) {

        $queried_object = get_queried_object();
        $child_cats     = (array) get_term_children( $queried_object->term_id, 'product_cat' );

        if ( ! $query->is_admin ) //exclude the posts in child categories
        {

            $query->tax_query->queries[] = [
                'taxonomy' => 'product_cat',
                'field'    => 'term_id',
                'terms'    => array_merge( $child_cats ),
                'operator' => 'NOT IN'
            ];
        }

    }

}

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.