Sort a custom post type loop by a custom taxomomy instead of chronologically

So it turns out that I didn’t do a loop of loops at all. This is several years old now. I don’t even know how I came up with that because clauses/limits and DB stuff are still a bit over my head. Anyway, I hope it helps some… I thought for sure I had a loop of loops, but that shouldn’t be all that hard to write either.

//remove limits from article CPT archive
function kia_archive_limits( $limits ) {

  if( ! is_admin() && ( is_post_type_archive( 'article' ) ) ) {
     // remove limits
     $limits="";
  }

  return $limits;
}
add_filter('post_limits', 'kia_archive_limits' );


//complicated queries for articles depending on query var
function kia_article_clauses( $clauses, $wp_query ) {
    global $wpdb;

    if( is_post_type_archive( 'article' ))  {

        //join term_relationships to posts, and term_relationships to term_taxonomy and term_taxonomy to terms
        $clauses['join'] .= "LEFT OUTER JOIN {$wpdb->term_relationships} ON {$wpdb->posts}.ID={$wpdb->term_relationships}.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} USING (term_taxonomy_id)
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)" ;

        //group posts by term name
        $clauses['groupby'] = "object_id";

        //include posts with and without a subject term
        $clauses['where'] .= " AND (taxonomy = 'subject' OR taxonomy IS NULL)";
        $clauses['orderby']  = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) ";

    } 

    return $clauses;
}
add_filter( 'posts_clauses', 'kia_article_clauses', 10, 2 );

And my loop code looked something like this:

<div class="column">

<?php

$prev = ''; $counter = 1; $column = 1;

while ( have_posts() ) : the_post();  

    $subhead =  array_shift(wp_get_post_terms(get_the_ID(), 'subject', array("fields" => "names")));
    $subhead = $subhead ? $subhead : __('No Subject','peterwade');

    if($counter >= 20) { 
        //reset the counter
        $counter = 1;

        $column++;

        //what column are we on?
        if($column == 5 ){
            $column = 1; //reset column count
            $hr="<hr class="clear"/>";
        } else { 
            $hr = false; 
        }

        if ($column == 4) { 
            $class = "column last";
        } else {
            $class = "column";
        }

     ?>
    </div><!--.column-->

    <?php if($hr) echo $hr;?>

    <div class="<?php echo $class;?>">

    <h3 class="subhead"><?php printf('%s <span class="continued">%s</span>', $subhead, __('continued'));?></h3>
    <?php 
        }


    if($subhead != $prev) { 
        $counter++; ?>
        <h3 class="subhead"><?php echo $subhead;?></h3>
        <ul>
    <?php } ?>

        <li><a href="https://wordpress.stackexchange.com/questions/136602/<?php echo get_permalink();?>" title="<?php echo __('Permalink to') . ' ' . get_the_title();?>"><?php the_title();?></a></li>
    <?php 

    if($subhead != $prev) { ?>
        </ul>
    <?php }

    $prev = $subhead;
    $counter++;

endwhile; ?>

</div><!--.column-->

Now looking at the source on the page I linked you to, it would appear that he’s reverted to how he was doing things formerly…. aka… a giant table. Whether this was because my code stopped working I couldn’t say.

Alternative

This would be a loop of loops. Looping through the terms and then querying the posts in each term. You’d have to benchmark to determine which method is faster. I would highly suggested storing the queries in some kind of transient, but it is time for dinner!

$terms = get_terms( 'genre' );

if( $terms ):

foreach( $terms as $term ){

    // The Query
    $args = array(
      'post_type' => 'book',
      'nopaging' => true,
      'tax_query' => array(
         array( 
            'taxonomy' => 'genre',
            'field' => 'id',
            'terms' => $term->term_id 
         )
      )    
);

    $the_query = new WP_Query( $args );

    // The Loop
    if ( $the_query->have_posts() ) {
        echo '<h2>' . ucwords( $term->name ) . '</h2>';
            echo '<ul>';
        while ( $the_query->have_posts() ) {
            $the_query->the_post();
            echo '<li>' . get_the_title() . '</li>';
        }
        echo '</ul>';
    } 
}

endif;

/* Restore original Post Data */
wp_reset_postdata();