1

Scenario: I have a bunch of resellers loaded under a "reseller" post type. Each of these resellers have a "rating" which is a string value like Gold, Bronze, Silver - this indicates their status as a reseller. Each reseller is also categorised into a country.

What I have: I have made use of the pre_get_posts action to sort by meta_value, asc or desc. This is working fine.

function my_pre_get_posts( $query ) {
    // do not modify queries in the admin
    if( is_admin() ) {
        return $query;   
    }

    if( isset($query->query_vars['country']) ) {  
        $query->set('orderby', 'meta_value');
        $query->set('meta_key', 'partner_status');   
        $query->set('order', 'ASC'); 
    }

    return $query;
}

add_action('pre_get_posts', 'my_pre_get_posts');

I want to instead be able to specify the sort order so that the Gold resellers are first, then the Silver resellers, then the Bronze ones, etc. So, pseudo code:

function my_pre_get_posts( $query ) {
    // do not modify queries in the admin
    if( is_admin() ) {
        return $query;   
    }

    if( isset($query->query_vars['country']) ) {  
        $query->set('orderby', ['Gold','Bronze','Silver']); // pseudo here
        $query->set('meta_key', 'partner_status');   
        $query->set('order', 'ASC'); 
    }

    return $query;
}

add_action('pre_get_posts', 'my_pre_get_posts');

Hope this makes sense to someone! :)

5
  • the query use data of the database so for this sorting, I would rather use a sorting with PHP after the query (hook the_posts), with php.net/usort e.g.
    – mmm
    Mar 17, 2016 at 11:03
  • +1 for using pre_get_posts. This type of complex sorting is doable by default since v4.2. You can use this to get you started Mar 17, 2016 at 11:17
  • Thanks, Pieter. I despise running another query when its not necessary. :)
    – RiaanP
    Mar 17, 2016 at 11:56
  • I just wonder, SQL is normally slower than PHP when it comes to complex sorting, and custom fields is cached, I wonder if using usort() to sort the posts via the_posts filter would be faster. To be honest, I haven't looked into performance testing this scenario. I know pre 4.2, I made use of usort() and the_posts alot to do custom sorting ;-) Mar 17, 2016 at 12:09
  • FYI, there is no need to return anything from the callback as the query is passed by reference ;) Mar 17, 2016 at 16:09

1 Answer 1

1

After some more testing it became apparent that the results weren't in fact doing natural sorting. I made a few changes. It's ugly... but it works.

Okidokes, so I wasn't able to get this to sort the way I wanted using Wordpress' sorting functions so I did go the usort route, and I think it turned out pretty nicely. (except for a PHP warning that is apparently a bug.)

Here's what I did. (I should have mentioned that I am using the ACF plugin.)

In archive.php, just before where I loop the query:

<?php if (have_posts()) : 
        // here we are going to populate the "menu_order"
        $statusArr = ['Gold','Silver','Bronze']; // set up the desired order in which the posts should arrive
        while (have_posts()) : the_post(); // start a loop
            $position = array_search(get_field('partner_status'), $statusArr); // find the index number of the post's "partner_status" relevant to everything in the array
            //$post->menu_order = $position; // update the menu order for the post with that index number (thus building a layered reference)
            if ( empty( $post->menu_order ) ) wp_update_post( array('ID'=>$post->ID,'menu_order'=>$position) ); // here we physically write a menu order into the post so that on subsequent visits, it will sort correctly
        endwhile;

        function home_orderby( $a, $b ) { // this function does the comparison for the usort
            global $posts;
            $apos = $a->menu_order; // compare menu_orders
            $bpos = $b->menu_order;
            return ( $apos < $bpos ) ? -1 : 1;
        }
        //@usort( $wp_query->posts, "home_orderby" ); // If all you want is to sort by the order, then you could still use this

    rewind_posts(); // rewind posts to use in the main loop that creates the HTML ?>

In functions.php I then use the pre_get_posts action hook like this:

function my_pre_get_posts( $query ) {
    // do not modify queries in the admin
    if( is_admin() ) {
        return $query;   
    }

    if( isset($query->query_vars['country']) ) {  
        $query->set('orderby',array( 'menu_order' => 'ASC', 'title' => 'ASC' ));
    }
}

add_action('pre_get_posts', 'my_pre_get_posts');

I now see my custom post type posts ordered in the order I specified at the top in my $statusArray. Happy days!

Thanks for your input, Pieter. It got me thinking along the right path. :)

2
  • 1
    If you are talking about the usort() bug, then yes, I see you did use the @ sign to suppress the error (which you should never use, just in this case only). That bug is now eventually being fixed in PHP 7 after many millenia Mar 17, 2016 at 15:16
  • No need for return $query as this is an action. It is only needed in filters ;-) Mar 18, 2016 at 7:13

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.