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.