1

I have default post type posts (post type: post) and custom post type posts (post type: cpt). The latter has a taxonomy (cpt_tag).

I would like to display on my front page all the default post type posts (unfiltered), and some of the cpt post type posts, only if they have a certain cpt_tag.

The code is:

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() ) {

        $taxquery = array(
            array(            
               'taxonomy' => 'cpt_tag',
               'field' => 'term_id',
               'terms' => 27
               )
            );      
        $query->set( 'tax_query', $taxquery );                 
        $query->set( 'post_type', array( 'post', 'cpt' ) );
    }
}

The problem is $query->set( 'tax_query', $taxquery ); is applied not only to cpt posts but post posts too. How could I use tax_query only for filtering cpt posts and leave post posts unfiltered?

Any help would be appreciated. Thanks!

(This is a clarified version of my previous question.)

  • You will need to do two queries, one for post and one for cpt, and then merge them for your loop. – TheDeadMedic May 6 '15 at 9:47
  • @TheDeadMedic, I'm not sure how can I achieve this, but thanks for the tip, I will looking into it! – pot63 May 6 '15 at 10:25
  • @TheDeadMedic you don't need two separate queries here. I think we all overthought a very easy solution here. cpt_tag is only assigned to cpt post type, so this makes the solution easy – Pieter Goosen May 6 '15 at 14:21
3

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_tags 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' ) );

    }
}
  • Any feedback on this. Please rechech my code, I had a bug – Pieter Goosen May 7 '15 at 4:35
  • Hey, thanks for your answer! I really like this approach, but the problem is, if I have cpt post type posts that have no cpt_tags at all, the tax_query doesn't filter them out, it only filters out cpt post type posts with at least one cpt_tag (if it is not 110). Is it possible to apply the tax_query to cpt post type posts with 0 cpt_tag too, without the mysterious query merging? :) – pot63 May 7 '15 at 7:43
  • But I accept your answer anyway, because with your help at least I can do a workaround by adding some tag to each cpt post type posts. (Yeah, I'm not proud of it...) – pot63 May 7 '15 at 7:46
  • Please see my EDIT 2. I have made a slight change to the code. This should work now without having the need to assign terms to the custom post type posts without terms :-). If you have tested it, let me know. Curious to see how it works :-) – Pieter Goosen May 7 '15 at 16:37
  • 1
    Wow, thank you very much, the code works beautifully! (There was a missing comma after the first array in $taxquery.) I love this solution, it seems pretty smart. :) Thanks again, I truly appreciate your help and detailed explanations! – pot63 May 8 '15 at 8:45

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.