Querying CPT with Two Taxonomies

Disclaimer: It won’t be very efficient solution and I hope you don’t have many terms in these 2 taxonomies (if so, then you should do it a little bit different and include some custom SQL queries).

OK, disclaimer done, we can go to the solution 😉 And here it goes:

<?php
    $post_type="reports";
    $weeks = get_terms( array(
        'taxonomy' => 'report-weeks',
        'orderby' => 'name',
        'order' => 'DESC'
    ) );
    $locations = get_terms( array(
        'taxonomy' => 'report-locations',
        'orderby' => 'name',
        'order' => 'ASC'
    ) );
    $results = array();

    foreach ( $weeks as $week ) {
        $week_results = array();
        foreach ( $locations as $location ) {
            $posts = new WP_Query( array(
                'post_type' => $post_type,
                'tax_query' => array(
                    array(
                        'taxonomy' => $week->taxonomy,
                        'terms' => $week->term_id,
                    ),
                    array(
                        'taxonomy' => $location->taxonomy,
                        'terms' => $location->term_id,
                    ),
                ),
                'posts_per_page' => '-1',
            ) );

            if ( $posts->have_posts() ) {
                $week_results[ $location->term_id ] = $posts;
            }
        }
        if ( ! empty( $week_results ) ) {
            $results[$week->term_id] = $week_results;
        }
    }

    foreach ( $weeks as $week ) :
        if ( ! array_key_exists( $week->term_id, $results ) ) continue;
    ?>
        <h2><?php echo $week->name ?></h2>
        <?php
            foreach ( $locations as $location ) :
                if ( ! array_key_exists( $location->term_id, $results[$week->term_id] ) ) continue;

                $query = $results[$week->term_id][$location->term];
        ?>
            <h3><?php echo $location->name; ?></h3>
            <?php while ( $query->have_posts() ) : $query->the_post(); ?>
                <h4><?php the_title(); ?></h4>
                <div><?php the_content(); ?></div>
            <?php endwhile; ?>
        <?php endforeach; ?>
<?php
    endforeach;
    wp_reset_postdata();
?>

So what do we do in there? First we iterate through every week and every location and prepare WP_Query for every pair . This way we can omit the pairs that don’t have any posts assigned to them.

After preparing these queries we can output the results. So we have to iterate one more time and print the posts assigned to given pair .