Display Only Posts From Parent Term In WordPress
The client had a related posts loop on the single page template that was displaying posts from the same term as the current post. Everything was working fine, until they hit a snag. It seemed that the related loop was pulling in posts from another term. What was the problem? Well for the sake of simplicity let’s call our two terms ‘parent’ & ‘child’. We were displaying the posts from the ‘parent’ term, but it was also pulling in posts from the ‘child’ term as it is it’s child.
It eventually turned out that the term had been mistakenly made a child, but not before I’d figured out away around it. So to stop it going to waste I thought I’d share it.
N.B. Please note that you will require a fair knowledge of WP queries to continue, although if you are stuck please don’t hesitate to drop me an email or a comment.
The Standard ‘Tax Query’
First let’s look at a standard WordPress Taxonomy query.
1 2 3 4 5 6 7 8 9 10 |
array( 'posts_per_page' => '5', 'tax_query' => array( array( 'taxonomy' => 'music', 'field' => 'slug', 'terms' => 'pop' ) ) ); |
This is the array you would hand to either query_posts()
or WP_Query()
to return posts from the terms you have listed.
So, let’s say that we have pulled in posts from the term ‘pop’ under the taxonomy ‘music’. Now let’s pretend for a second there are terms which are the names of sub-genres (dance-pop, Jpop, etc) that are children of the term ‘pop’. If you use the query above it will show posts for all of those terms too.
Now you might want to show all the posts from the children, but what if you don’t? As of this moment (as far as I’m aware) there is no option/parameter to allow you to stop this from happening, so we do a little bit of query trickery instead. Take a look at this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
array( 'posts_per_page' => '5', 'tax_query' => array( array( 'taxonomy' => 'music', 'field' => 'slug', 'terms' => 'pop' ), array( 'taxonomy' => 'music', 'field' => 'slug', 'terms' => array('jpop', 'dance-pop'), 'operator' => 'NOT IN' ) ) ); |
It’s actually fairly obvious when you think about it. If there is no parameter to auto exclude posts from child terms then why don’t we just tell the query we don’t want those posts using the child terms & the NOT IN
operator.
Now there is a big problem with that query & that is the fact it is hard coded. If we add any more children we’ll have to manually add them into the query. Well let’s change that by automating the query. Take a look at this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$tax = 'music'; $oterm = 'pop'; $term = get_term_by('slug', $oterm, $tax); $termChildren = get_term_children($term->term_id, $tax); $wp_query = new WP_Query(); $wp_query->query( array( 'posts_per_page' => '5', 'tax_query' => array( array( 'taxonomy' => $tax, 'field' => 'slug', 'terms' => $oterm ), array( 'taxonomy' => $tax, 'field' => 'id', 'terms' => $termChildren, 'operator' => 'NOT IN' ) ) ) ); |
There we have it. You may wonder why I’ve added two variables & assigned the name of the taxonomy & parent term to it. Well it’s is simply because if we ever need to change the taxonomy or parent term we can by only changing 1 or 2 lines.
We’ve already been through the query & what that does, so I’ll explain a little about what the two extra lines do. First we have get_term_by()
this allows us to get all the information about our parent term, including it’s ID. We then use that ID in the function get_term_children()
to bring back any children the term may have. It returns an array of IDs of the children. That is perfect to hand straight into the query.
That’s all there is to it. Hopefully this tutorial has been helpful. Although the code didn’t end up being used on the site, I did get the chance to test it so I know it works. If you have any questions or comments please let me know via the comments below, or the contact page.
17 Comments
kathy
Hi Paul,
I was just looking for some complicated hierarchical tax query stuff, so this is helpful getting me on my way. I need to show all the parent posts and 1 featured post from each child that links to the child tax page. have any random ideas for that? this project has literally been the project from hell, but i hope that is the last bit.
Paul Robinson
Hmmm. That is a difficult one. I can’t think of anything off the top of my head.
Maybe you could do a tax query that grabs all posts using
'posts_per_page' => -1
from the parent & the children. Then grab the tax children usingget_term_children()
.Then you could create a blank array outside the post loop, and for each post you could use something like
in_array()
to see if the posts term id is inside the array given byget_term_children()
. If it is display the post & add it’s ID to the blank array. You could then also add an extra condition to the previousin_array()
that checks to see if the term ID of the post is also in your other array. If it is just continue on without displaying the post usingcontinue;
.I’m not entirely sure of the code or even if it will work, but it’s the best I can come up with right now. Hopefully, even if I’ve been writing crap, it’s inspired you with how to get it working, lol.
Kathy
Been thinking of this all day (even while at handball training it’s been on the brain). I would have thought that the query would automatically pull in ALL posts from the child taxonomies, but this doesn’t seem to be the case.
for instance i have a child taxonomy called Branding. Under it I have lets say Nike and Adidas. When I am on the archive for Branding it doesn’t consistently pull in the posts from the Nike and Adidas children.
i’ve just tried doing your post in reverse.. add the get_term_children, but it’s late and my brain is fuzzy.
as always, thanks for the great info!
Kathy
get_term_children is giving me terms that don’t even exist in my DB. that has to be a sign i need to quit
Paul Robinson
Wow! Really. I’ve never come across that problem. I honestly have no idea why it would pull non existent terms up.
The child thing annoys me. I was working on a site recently & it was showing all child posts from the parent taxonomy, but if I tried on my local testing server it would only pull parent posts?!
Kathy
i think i got it! well i had to delete all the terms and posts from the DB. some wires must have just gotten crossed somewhere.
Paul Robinson
Very strange. Glad you seem to have sorted something out though, love to know what the cause was though.
On a side note Internet Explorer is giving me grief again. Managed to fix it, but it just didn’t want to read HTML5 elements properly event with modernizer or a Shiv loaded. I wish people would just upgrade their version of IE so we could build for the latest version and be done with it.
kathy
i think i had posts in a term that somehow didn’t exist? very strange.
and man do i feel your pain about IE. i want to build in giant pop-ups to ever site and have it say “freaking upgrade to a REAL browser already”
Paul Robinson
Okay… Very odd indeed.
Yep, would love to do something like that. I mean you can use things like Explorer Destroyer to ‘encourage’ people to upgrade, but still I always thought upgrading was a common sense thing. 🙁
Okyere Adu-Gyamfi
strangely I tried your technique to show a list of courses (custom post type) for a client website that has course category (taxonomy) and the logic on the taxonomy.php was modified using query_posts to make it return only courses under the current course area and still getting courses under sub course areas displaying as well…
Please find code below:
global $wp_query;
$current_area = get_query_var( ‘term’ );
$term = get_term_by(‘slug’, $current_area, ‘courseareas’);
$sub_areas = get_term_children($term->term_id, ‘courseareas’);
$args = array_merge( $wp_query->query, array(
‘tax_query’ => array(
array(
‘taxonomy’ => ‘courseareas’,
‘field’ => ‘slug’,
‘terms’ => $current_area,
),
array(
‘taxonomy’ => ‘sotskurs_courseareas’,
‘field’ => ‘slug’,
‘terms’ => $sub_areas,
‘operator’ => ‘NOT IN’
),
)
)
);
query_posts($args);
Paul Robinson
Hi,
This is going to sound awful, but did you have a question or were you just sharing your code?
Okyere Adu-Gyamfi
Sorry, I tried the code in the above article and it still did not give me the expected results, the posts shown still belong to the sub categories of the current taxonomy term, so my question is how do I achieve this: get only posts of the current taxonomy term to display?
Paul Robinson
The only thing I can think of is that merging the original query onto the new query is overriding it somehow.
Try the query without merging the original onto it and see if that works.
Okyere Adu-Gyamfi
I finally solved it, a mistake on my part:
the second tax_query array should have ‘id’ instead of ‘slug’…
my bad, anyway thanks guys…
A fresh pair of eyes always does the trick…
Paul Robinson
Oh man… I should have spotted that, sorry. 🙁
I blame not enough coffee, lol. Glad you managed to fix it though.
Cata
Looks nice, thanks. However will not work for posts assigned to both parent and child taxonomies. Any idea how to fix this?
Paul Robinson
Hi Cata,
Unfortunately, as far as I can tell, that can’t be fixed. The only way I can think of is not have a post listed under both child & parent taxonomies.
It may be possible if you can have nested tax queries, since you could have the parent, but not children query, with an additional query that says get parents but not those parents that are also children. I don’t think that is possible at the minute though. Sorry.