As I can understand, cpt_tag
is only assigned to the custom post type cpt
. This makes things much easier and less complicated. You do not need two queries and you don't need to merge anything
Here is the plan:
We need to know which terms you need to show on the homepage from the cpt
post type
Using get_terms()
and the exclude
parameter, we will exclude the terms we need to show posts from from the cpt
post type. The returned array from get_terms()
will hold all the terms we don't want to show. We are also going to set the fields
parameter to ids
so that we get an array of term ids
This array will be passed to the tax_query
in pre_get_posts
. As this is terms we need to exclude, we will set the operator
parameter to NOT IN
to exclude posts with these terms
You need only minor adjustments to your code to make this work
Here is an example
add_action( 'pre_get_posts', 'get_posts_plus_cpt_with_certain_tag' );
function get_posts_plus_cpt_with_certain_tag( $query ) {
if ( $query->is_home() && $query->is_main_query() ) {
/*
* Change 110 to the term id you need to display posts from from cpt post type
*/
$terms = get_terms( 'cpt_tag', array( 'exclude' => 110, 'fields' => 'ids' ) );
if ( $terms && !is_wp_error( $terms ) ) {
$taxquery = array(
array(
'taxonomy' => 'cpt_tag',
'field' => 'term_id',
'terms' => $terms,
'operator' => 'NOT IN'
)
);
$query->set('tax_query', $taxquery);
}
$query->set( 'post_type', array( 'post', 'cpt' ) );
}
}
EDIT 1
I did not include safety, you should first check if you get any results from get_terms
before using it. I have included that in my code now
EDIT 2
The above solution works, but there is a small glitch as pointed out in comments
...if I have cpt
post type posts that have no cpt_tag
s at all, the tax_query
doesn't filter them out, it only filters out cpt
post type posts with at least one cpt_tag
...
The problem arises in the fact that custom post type posts by default doesn't have a default term assigned to it when no term is assigned to them in the same way as normal post type post
have the term uncategorized
assigned to them by default when no term is assigned to it
Because there is no relation to any term if no term is assigned to them, custom post type posts does not appear in the wp_term_relationships
table, so they don't appear in a normal tax_query
To work around this issue, we need to slightly alter our original workflow. Here is how we are going to tackle this issue
Get all the terms assigned to the post
post type. By default, all posts from the post
post type will have at least one term assigned to it unless this behavior is altered by some other drastic code. So we are 99.999% safe here.
We are going to use the same strategy as previous but with a slight modification. We will still use get_terms()
with the fields
parameter set to ids
, but here we will get all the terms. I will assume that you are using the default build-in taxonomy category
for the post
post type.
We can simply just pass the term ids we need to include for the cpt_tag
to our tax_query
, nothing special here
Remember to set the relation
parameter to OR
as we need posts with all terms in category
taxonomy or all posts that has the specific term from the cpt_tag
taxonomy. Omitting the relation
parameter will default to AND
which will fail your query as it will look for posts that have terms in category
taxonomy and cpt_tag
taxonomy
So lets put that in code:
(I will again use term id 110
for the term for cpt_tag
, just change accordingly)
add_action( 'pre_get_posts', 'get_posts_plus_cpt_with_certain_tag' );
function get_posts_plus_cpt_with_certain_tag( $query ) {
if ( $query->is_home() && $query->is_main_query() ) {
/*
* Change 110 to the term id you need to display posts from from cpt post type
*/
$terms = get_terms( 'category', array( 'fields' => 'ids' ) );
if ( $terms && !is_wp_error( $terms ) ) {
$taxquery = array(
'relation' => 'OR',
array( // Includes all terms (categories) for post post type
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $terms,
),
array( // Include the terms you need to display from cpt_tag
'taxonomy' => 'cpt_tag',
'field' => 'term_id',
'terms' => array( 110 ),
),
);
$query->set('tax_query', $taxquery);
}
$query->set( 'post_type', array( 'post', 'cpt' ) );
}
}
post
and one forcpt
, and then merge them for your loop.cpt_tag
is only assigned tocpt
post type, so this makes the solution easy