0

If I have a list of term ids, like 1,2,3,4,5 that correspond to tags, categories and custom taxonomies, how can I get all the posts that contain all these terms?

Let's say from that list of term ids, they belong to these taxonomies: 1 and 2 are tags, 3 is a category, 4 is custax1, and 5 is custax2.

I want to pass array(1,2,3,4,5) in get_posts() or something else so that it returns only posts that contain all of these term ids, regardless what taxonomy those ids belong to. How do I do this?


Details, in case they help:

I am building a site that utilizes the categories, tags, and two custom taxonomies, a total of four taxonomies. I am building a filter page that allows the user to sift though all four of these taxonomies and select which posts to show. The user clicks on labels for different taxonomy names then the term_ids are passed into a URL variable and the page is reloaded (e.g. the url contains ?terms=1,2,3,4,5). After the page reloads, the filter shows only posts that contain all the selected term_ids (whether tags, categories, or custom taxonomies).

I'm struggling on the part where the filter actually displays the results. There doesn't seem to be any way to fetch posts filtered on term_id without knowing what taxonomy a term_id corresponds to.

In looking through all the WP Query Class arguments I see that I can target term_ids, but cannot seemingly target all taxonomies at the same time. It looks like I have to target tags, categories, and custom taxonomies with 'tax_query' => array(), but there's still an issue there. You cannot pass term ids that are not part of the stated taxonomy or you'll get an empty array returned.

Here's some example arguments for get_posts():

$args = array(
    'numberposts' => -1,
    'post_type' => array('post'),
    'post_status' => 'publish',
    'tag__and' => array(), 
        // gets posts with these tag term ids,
        // but cannot pass non-tag term ids.
    'category__and' => array(), 
        // gets posts with these cat term ids, 
        // but cannot pass non-category term ids.
    'tax_query' => array(
         'relation' => 'AND',
         array(
              'taxonomy' => 'custax1',
              'field'    => 'term_id',
              'terms'    => array(),
                   // gets posts with these custom tax term ids, 
                   // but cannot pass non-custax1 term ids
    ),
         array(
              'taxonomy' => 'custax2',
              'field'    => 'term_id',
              'terms'    => array(),
                   // gets posts with these custom tax term ids,
                   // but cannot pass non-custax2 term ids term ids
    ),

I can't just pass the URL variable as an array to any of these unless it contains only term ids for within that taxonomy. If I pass an array of term ids that consist of tags and categories, I will get zero results on all the arrays in the above code. I'm hoping I'm missing something simple here, and can easily pass the URL variable to a function that then gets the posts that contains all the term_ids.

  • I just discovered I can get_terms(array('include'=>array(),'taxonomy'=>'post_tag',)); which will give me the term ids belonging to 'post_tag' taxonomy. I can use that later in the code, but it complicates things. – 38365 Jul 25 '20 at 0:35
  • If I understand it properly, your best or a simple and standard way is by using <taxonomy query>=<term IDs> in the URL, e.g. ?tags=1,2&cats=3&custax1=4. Then in the WordPress query args, you can do something like 'tag__and' => explode( ',', $_GET['tags'] ) for filtering by the tags. – Sally CJ Jul 25 '20 at 2:29
1

I suggest that that @SallyCJ's approach makes much more sense if you can do it this way: simply break down wherever these ID's get written to store the tax names at that point.

However it seems like figuring out which Taxonomy each Term ID is in and then using that to generate the tax_query part of the WP_Query args isn't so hard. get_term allows you to find the taxonomy name from a term ID, so with that you're half way there.

This is untested code, some of it may contain errors and uses your $args as a base so you may need to double check the structure of the tax_query part, but it contains all the pieces you could use to build the code if you wanted to do it this way:

$arrTermIDs = [ 1,2,3,4,5 ];
$arrTaxAndTerms = []; // make a 2d array with the term ID's broken down by tax name

foreach ($arrTermIDs as $termID) {
    $term = get_term($termID);
    if (!in_array($term->taxonomy, $arrTaxAndTerms)) 
        $arrTaxAndTerms[$term->taxonomy] = [] ; // create array for this tax if we don't have one already
    
    $arrTaxAndTerms[$term->taxonomy][] = $termID; // add this term id to array for this tax
}

// Now we have all the info we need to build the tax_query items for each taxonomy 
$arrTaxQueryItems = [];

foreach($arrTaxAndTerms as $taxName => $termIDs) {
    $arrTaxQueryItems[] = 
        array(
              'taxonomy' => $taxName,
              'field'    => 'term_id',
              'terms'    => $termIDs
        );
}

// Put that together into your $args:
$args = array(
    'numberposts' => -1,
    'post_type' => array('post'),
    'post_status' => 'publish',
    // any other conditions you want here
    'tax_query' => array(
         'relation' => 'AND',
         $arrTaxQueryItems
    )
)
0

Unfortunately, you can't just pass an array of term ids into a WP function and get all those posts returned if the term ids are of different taxonomies. You'll have to do it the hard way with tax-query key passed in get_posts(). Fortunately, even though it is the hard way, you have complete control over the returned posts list, and a well designed tax-query can give you virtually any customization you can think of.

In your example tax-query arguments, you are calling category__and and tag__and. It would be better to call all desired taxonomies in a single tax-query so that you can design the result according to your needs.

The way I would do this is build the arrays within the tax-query separately and call them by variable later, like this:

$get_posts_args['tax_query'] = array(
            'relation' => 'AND',
            $tag_array,
            $cat_array,
            $custax1_array,
            $custax2_array, ); }

The first benefit is that if any of these arrays are empty, they are simply ignored and the query runs anyway. The second benefit is that you can set further operator logic within these arrays. For example, $tag_array may look like this:

$tag_array = array(
        'taxonomy' => 'post_tag',
        'field'    => 'term_id',
        'operator' => 'AND',
        'terms'    => $tag_ids,); }

The operator key allows a few options that work within the taxonomy, while the relation key in the outer array works on the logic between the taxonomies. If you design these arrays programmatically, it becomes trivial to change the logic as needed for both circumstances for all taxonomies. For hierarchical taxonomies, you probably want to consider your preferences for the include_children key.

The somewhat unique problem of having an integer array containing term ids from unknown taxonomies is pretty easily resolved with:

$tag_ids = get_terms(array(
     'include'=>$term_ids_list,
     'taxonomy'=>'post_tag',
     'fields'=>'ids'));

If $term_ids_list is your integer array of term ids, then calling get_terms() for a specific taxonomy will return only the terms within that taxonomy. This gives you $tag_ids as an integer array consisting only of term ids belonging to the tag taxonomy. You'd simply change the keys accordingly for the taxonomy you want to target.

In hindsight for resolving this problem, I find that it is better to work with the taxonomies separately, even if they will be presented to the user as a single list. Presumably, we make taxonomies to separate kinds and types of content. Not keeping them separate undoes whatever purpose you originally had for making the separation in the first place. In my case, I wanted to have small urls, so decided that a single url variable like ?terms=1,2,3,4,5 would be better than ?cats=1,2&tags=3,4&custax1=5. Indeed, the url is smaller, but upon reload the taxonomy key is lost. In this specific case however, it's not too difficult to put them back together.

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.