Wordpress - Getting the Intersection of Two Custom Taxonomy Terms for a Custom Post Type?

As of v1.3, the Query Multiple Taxonomies plugin works great with WP_Query. Your original args work as is.

$args = array(
'post_type' => 'portfolio',
'numberposts' => -1,
'project_type' => 'A',
'package' => 'A'
);

Then make a new query and check it:

$foo = new WP_Query($args);
var_dump($foo->posts);

I tested this on my own custom taxonomy setup, and it only returned posts which matched both terms in the query.

Another convenient method of grabbing multiple taxonomy terms with QMT is building simple URL queries:

site.com/?post_type=portfolio&package=foo&project=bar

I've used this method with the add_query_args() function to create links on a page that modify the current query, refining it by adding additional terms and taxonomies. The syntax also works great with a search input, as multiple words in the input field are posted as foo+bar, which works great with QMT:

site.com/?post_type=portfolio&project=alpha&colors=red+blue+green

Which returns only posts that meet all these criteria - Type: Portfolio / Project: Alpha / Colors: red + blue + green


Hi @shaun:

@Rarst is right in that they are adding this functionality to WordPress v3.1, but it won't be here for a while and I'm sure you need it now. You could try to grab the code they are working on and try to make it work, you could use the plugin both @Rarst and @somatic mention, or you could just include the code you need into your theme, below.

The following code is a posts_where hook to add a tax_terms query variable with a value of the format (i.e. colon-commas) where the values are term slugs:

"{$varname1}:{$value1},{$varname2}:{$value2},...{$varnameN}:{$valueN}"

This is a self-contained .php you can drop into the root of your website to test it out before pulling the add_action call and the tax_terms_where() function into your theme's functions.php file (or into the .php file of one of your own plugins.)

<?php
/*
* Adds "tax_terms" to WP_Query()
*
* See: http://lists.automattic.com/pipermail/wp-hackers/2010-October/035258.html
* See: http://wordpress.stackexchange.com/questions/2255/
*
*/

  header('Content-Type:text/plain');
  include "wp-load.php";
  add_action('posts_where','tax_terms_where',10,2);

  $result = new WP_Query('post_type=portfolio&tax_terms=project_type:A,package:A');
  foreach($result->posts as $post) {
    echo "{$post->post_title}\n";
  }

function tax_terms_where($where,$wp_query) {
  if (isset($wp_query->query)) {
    $query = $wp_query->query;
    if (is_string($query))
      parse_str($query,$query);
    if (is_array($query) && isset($query['tax_terms'])) {
      global $wpdb;
      $tax_terms = explode(',',$query['tax_terms']);
      foreach($tax_terms as $tax_term) {
        list($taxonomy,$term) = explode(':',$tax_term);
        $sql = <<<SQL
AND $wpdb->posts.ID IN (
  SELECT tr.object_id
  FROM $wpdb->term_relationships AS tr
  INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
  INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id
  WHERE tt.taxonomy='%s' AND t.slug='%s'
)
SQL;
        $where .= $wpdb->prepare($sql,$taxonomy,$term);
      }
    }
  }
  return $where;
}

If this isn't exactly what you need because, for example, you'd rather match the $term->term_id instead of matching the $term->slug then the code shouldn't be too hard to modify for your needs, assuming you are comfortable with SQL and .php.