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

I’m with a non-profit that paid for development of a custom post type plugin that will help us track endangered plants that we’re keeping in cultivation, rescued from development. We’re mostly happy with it, but need a bit of help. Our plugin is called nasc_report and we have multiple “authors” who are generating reports. The plants are cared for over time, so each year we ask for a census to be taken. So I’d like the default sort order to be ruled first by author (each author’s posts will be grouped) and then by nasc_population_code (A to Z), and then by date (most recent on top). This will produce little groups of our cultivated plant material by population code but separate them by author.

(As an aside, our growers are assigned a separate role that only allows them to see their own entries – all entries are private – but admins unfortunately see all and can’t hide all but their own; admins are also usually growers. Any ideas on that would be welcomed as well. If we could allow admins the options: “All | Private | Mine” and make “Mine” the default screen, then I’d only need to default sort by nasc_population_code and then by date.)

Read More

I’ve searched around but haven’t found a direct answer to my question or if I have I haven’t been able to understand it very well. Would I use pre_get_posts? I’m just a little lost in the code, here. Any help would be appreciated.

Related posts

Leave a Reply

1 comment

  1. 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;
    }