To get a list of all terms of taxonomy X whose posts are associated to terms from taxonomy Y too, we have to:
- Get all term IDs for both taxonomies
- Create a tax query to fetch all posts, because we don’t want to show empty term archives.
- Format the result in a hierarchical list.
Let’s go!
Getting the term IDs is simple:
get_terms( $taxonomy_name, array( \'fields\' => \'ids\' ) )
We just have to do that two times (for each taxonomy once).
The taxonomy query needs a relationship AND
to make sure all posts we get are in both taxonomies:
\'tax_query\' => array (
\'relation\' => \'AND\',
array(
\'taxonomy\' => $first,
\'field\' => \'id\',
\'terms\' => get_terms( $first, array( \'fields\' => \'ids\' ) )
),
array(
\'taxonomy\' => $second,
\'field\' => \'id\',
\'terms\' => get_terms( $second, array( \'fields\' => \'ids\' ) )
),
),
For formatting, we create an array where the terms of the first taxonomy are the keys and the terms of the second taxonomy are the values. Then we build a nested list with plain <ul>
elements.
Now the function: I have used tags and categories as taxonomies, just because it was easier to test.
function double_term_tree(
$post_types = array( \'post\', \'page\' ),
$first = \'category\',
$second = \'post_tag\'
)
{
$query = new WP_Query(
array (
\'numberposts\' => -1,
\'suppress_filters\' => TRUE,
\'posts_per_page\' => -1,
\'post_type\' => $post_types,
\'tax_query\' => array (
\'relation\' => \'AND\',
array(
\'taxonomy\' => $first,
\'field\' => \'id\',
\'terms\' => get_terms( $first, array( \'fields\' => \'ids\' ) )
),
array(
\'taxonomy\' => $second,
\'field\' => \'id\',
\'terms\' => get_terms( $second, array( \'fields\' => \'ids\' ) )
),
),
)
);
if ( empty ( $query->posts ) )
return;
$result_list = array();
$output = \'<ul>\';
foreach ( $query->posts as $post )
{
$first_terms = get_the_term_list( $post->ID, $first, \'\', \'|\' );
$second_terms = get_the_term_list( $post->ID, $second, \'\', \'|\' );
$f_term_array = explode( \'|\', $first_terms );
$s_term_array = explode( \'|\', $second_terms );
foreach ( $f_term_array as $f_term )
{
if ( ! isset ( $result_list[ $f_term ] ) )
$result_list[ $f_term ] = array();
$result_list[ $f_term ] = array_merge( $result_list[ $f_term ], $s_term_array );
}
}
foreach ( $result_list as $k => $v )
{
$result_list[ $k ] = array_unique( $v );
$output .= "\\n<li>$k\\n\\t<ul>\\n\\t\\t<li>"
. join( "</li>\\n\\t\\t<li>", array_unique( $v ) )
. "</li>\\n\\t</ul>\\n</li>";
}
$output .= \'</ul>\';
return $output;
}
You can call this function like this:
echo double_term_tree( \'product\', \'brand\', \'category\' );
And then you get that tree.