How to sort custom post type posts in default order by multiple fields?

Since I just wrote a pre_get_posts answer for someone else, I will modify it slightly and see if it works for you too.

When using pre_get_posts the $query variable is passed “by reference” which means we can directly manipulate the $query object with the shortcut functions such as set_query_var. Remove the query code from your taxonomy template, and try the following in your functions.php:

function wpa_66507( $query ) {
    if ( is_post_type_archive('nasc_report ') && is_main_query() ) { //where 'nasc_report' is assumed to be your post type name
        set_query_var( 'orderby', 'author meta_value date' );
        set_query_var( 'meta_key', 'nasc_population_code' ); 
    }
}
add_action( 'pre_get_posts', 'wpa_66507' );

Hope this helps. I’m a little unclear on sort orders (particularly whether they can be mixed), and I think you might need to set the posts_per_page parameter to -1 if you’d like to show all your posts on a single page without pagination.

EDIT #1:

I took another look at this and actually tested on some sample posts. It sorts by:

  1. Post author’s display name (instead of author ID) – ascending
  2. Post meta key nasc_population_code – ascending
  3. Post date – descending

To do this we can no longer use the relatively simple pre_get_posts and have to do more custom SQL. I apologize in advance that I won’t be able to explain it amazingly well because my own understanding is sort of limited. WordPress has a bunch of other filters that let you adjust the actual SQL query WordPress intends to perform on the database to retrieve your posts. The ones I’m using here are:

  1. posts_join – in general this links other tables to the wp_posts table
  2. posts_orderby – this controls the ordering
  3. post_limits – this controls how many posts to retrieve

The Codex entries are admittedly a little thin and I find them somewhat confusing.

edit: This solution also presumes that you have a meta key ( custom field, practically same difference ) called nasc_population_code.

So putting it all together, give this a try:

add_filter('posts_join', 'wpa_66507_join');

function wpa_66507_join( $join ) {
    if ( ! is_admin() && is_post_type_archive('nasc_report') && is_main_query() )  {

        global $wpdb;

        // join the users table so we can sort by author name
        $join .= " LEFT JOIN $wpdb->users AS author ON ( {$wpdb->posts}.post_author = author.id )";

        // join the postmeta table so we can sort by meta key
        $join .= " LEFT JOIN $wpdb->postmeta AS nasc_population_code ON ( {$wpdb->posts}.ID = nasc_population_code.post_id AND nasc_population_code.meta_key = 'nasc_population_code' )";
    }
    return $join;
}

add_filter('posts_orderby', 'wpa_66507_orderby');

function wpa_66507_orderby($orderby_statement) {
    if ( ! is_admin() && is_post_type_archive('nasc_report') && is_main_query() )  {
        global $wpdb;
        $orderby_statement = "author.display_name ASC, nasc_population_code.meta_value ASC";
    }
    return $orderby_statement;
}

add_filter('post_limits', 'wpa_66507_limits');

function wpa_66507_limits($limits) {
    if ( ! is_admin() && is_post_type_archive('nasc_report') && is_main_query() )  {
        $limits = "";
    }
    return $limits;
}