I simply want to list out the books (a custom post type) organized by their respective genre (the taxonomy made with the code below). I have all the code being used listed below.

I am using the following code in a page template to call the custom post type 'book':

    if ( get_query_var('paged') ) { $paged = get_query_var('paged'); } 
    elseif ( get_query_var('page') ) { $paged = get_query_var('page'); } 
    else { $paged = 1; }
    $myposts = array(
    'post_type' => 'book',
    'posts_per_page' => '40',
    'orderby' => 'id',
    'order' => 'ASC',
    'paged'=> $paged

This is the custom post type origin in the functions.php file:

add_action( 'init', 'register_cpt_book' );

function register_cpt_book() {

    $labels = array( 
        'name' => _x( 'Books', 'book' ),
        'singular_name' => _x( 'Book', 'book' ),
        'add_new' => _x( 'Add New', 'book' ),
        'add_new_item' => _x( 'Add New Book', 'book' ),
        'edit_item' => _x( 'Edit Book', 'book' ),
        'new_item' => _x( 'New Book', 'book' ),
        'view_item' => _x( 'View Book', 'book' ),
        'search_items' => _x( 'Search Books', 'book' ),
        'not_found' => _x( 'No books found', 'book' ),
        'not_found_in_trash' => _x( 'No books found in Trash', 'book' ),
        'parent_item_colon' => _x( 'Parent Book:', 'book' ),
        'menu_name' => _x( 'Books', 'book' ),

    $args = array( 
        'labels' => $labels,
        'hierarchical' => true,
        'description' => 'A collection of books',
        'supports' => array( 'title', 'editor', 'excerpt', 'author',  'custom-fields' ),
        'taxonomies' => array( 'author', 'genre' ),
        'public' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'show_in_nav_menus' => true,
        'publicly_queryable' => true,
        'exclude_from_search' => false,
        'has_archive' => true,
        'query_var' => true,
        'can_export' => true,
        'rewrite' => true,
        'capability_type' => 'post'

    register_post_type( 'book', $args );


Here is the custom taxonomy 'genres' in functions.php:

// Add new taxonomy, make it hierarchical (like categories)
    $labels = array(
        'name'              => _x( 'Genres', 'genre' ),
        'singular_name'     => _x( 'Genre', 'genre' ),
        'search_items'      => __( 'Search Genres' ),
        'all_items'         => __( 'All Genres' ),
        'parent_item'       => __( 'Parent Genre' ),
        'parent_item_colon' => __( 'Parent Genre:' ),
        'edit_item'         => __( 'Edit Genre' ),
        'update_item'       => __( 'Update Genre' ),
        'add_new_item'      => __( 'Add New Genre' ),
        'new_item_name'     => __( 'New Genre Name' ),
        'menu_name'         => __( 'Genre' ),

    $args = array(
        'hierarchical'      => true,
        'labels'            => $labels,
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'genre' ),

    register_taxonomy( 'genre', array( 'book' ), $args );

The code works great. Like I said, I just want to list out the books organized by their respective genre (the taxonomy made above).

So, the book loop would then look something like this:


Philosophy Book 1, Philosophy Book 2, Philosophy Book 3


Psychology Book 1, Psychology Book 2, Psychology Book 3


History Book 1, History Book 2, History Book 3

I have tried several methods I found when googling this, but haven't been able to find anything that works. I would be greatly indebted if anyone could give me a workable answer. Thanks.

  • 1
    I'm relatively certain can't order by a taxonomy. I think you are going to need multiple loops, but then I'm not sure about how you'd handle pagination. Also, query_posts() should be replaced by new WP_Query(). Commented Mar 3, 2014 at 13:10
  • Thanks for the update, albeit a depressing one. Is it possible to sort by category for the main loop? If so, perhaps I could use that as a base to figure out if there is a chance this could work.
    – N-WS
    Commented Mar 3, 2014 at 13:17
  • Still pretty sure the answer is no. Taxonomies (categories, etc) are for "grouping" posts together, not for sorting. I once had a client that wanted to "sort" his article by subject. I looped through all the terms, and did a new WP_Query() for each. This isn't as terrible if you use transients.. not sure I knew about transients then, but still. There is no pagination, but if this is remotely interesting, I can try to dig up the code later. Commented Mar 3, 2014 at 13:50
  • Sounds great! I won't need pagination I don't think.
    – N-WS
    Commented Mar 3, 2014 at 16:35
  • You need to invert your loop. You're getting all posts first. You need to get all categories first and then the posts for each of them.
    – gdaniel
    Commented Mar 3, 2014 at 16:36

1 Answer 1


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">


$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;


        //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";


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

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

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

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

        <li><a href="<?php echo get_permalink();?>" title="<?php echo __('Permalink to') . ' ' . get_the_title();?>"><?php the_title();?></a></li>

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

    $prev = $subhead;

endwhile; ?>


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.


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(
            '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() ) {
            echo '<li>' . get_the_title() . '</li>';
        echo '</ul>';


/* Restore original Post Data */
  • Would you know how to implement the second option in a manner which would have each loop (within the loop) be limited to their respective taxonomy?
    – N-WS
    Commented Mar 3, 2014 at 20:36
  • What do you mean? Each WP_Query should only be pulling the posts in that term. For example, if you have "fiction" and "non-fiction" as genres, the loop will query all fiction posts, and then all non-fiction posts. At least that is what I intended, I have to admit to not testing this. And I see that I borked the tax_query taxonomy, so I will edit that. Commented Mar 3, 2014 at 21:59
  • Thanks for the reply. It actually ends up listing all of the posts under each taxonomy. So, it does list each taxonomy, just all of the books under each, no matter the genre. What do you think?
    – N-WS
    Commented Mar 4, 2014 at 0:11
  • Looks like I had another error. This is what happens when I don't test. :) I've edited again, but you are highly encouraged to check out the Codex page on WP_Query taxonomy parameters Commented Mar 4, 2014 at 9:26
  • Thanks, but its still doing the same thing. I will check out the Codex page. Please let me know if you have any other advice.
    – N-WS
    Commented Mar 4, 2014 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.