Sorting posts and custom post types in WordPress has been made pretty easy when compared to the old days of having to write custom SQL queries thanks to the WP_Query class. Later updates to the class methods to allow arrays and multiple values have helped even further. But when using the class in such a way, you can still end up in a bit of a muddle.
I was recently working on a project that included listing classified ads that were initially added by the administrator but could subsequently be managed by a user if they joined the site and paid a subscription. I was under an NDA for this one, so I’ll need to create a new scenario to illustrate what was needed here, let’s say we’re talking about camp sites.
What were the sorting requirements?
On the site, the client wanted to, generally, list the camp sites that appeared in a given set of search results in a random order, so as not to give specific priority to any of them.
One of the benefits of signing up and paying to manage your own listing, however, was that you would be prioritised in any search results you appeared in and so would need to be listed at the top.
Subsequently, the client found that camp site owners were not always adding a featured image to their listing, and so to further encourage this, listings with a featured image would appear higher than those without.
So, the requirements were to sort like this:
- Show listings for paying clients above listings with no paying client
- Show those with featured images above those without featured images
- Otherwise, show them in a random order
The Code
For this, we need to use the good old pre_get_posts
hook to alter the query.
First off, we set up the function and hook and ensure that this is only run when we want it to be:
function custom_sorting( $query ) { //don't edit the query if we're not dealing with the main query for //the post-type-slug post type and we're not in the admin area if( ! is_post_type_archive( 'post-type-slug' ) || is_admin() || $query->is_main_query() ) { return; } } add_action( 'pre_get_posts' , 'custom_sorting' , 100 );
We’re then going to add our sorting for those who have paid. NB. it’s elsewhere in the code that listings of paying members have a “1” stored in the wp_posts_meta table under the meta_key “is-active”, and ask the order by attribute to sort by this value.
Happy days.
function custom_sorting( $query ) { //don't edit the query if we're not dealing with the main query for //the post-type-slug post type and we're not in the admin area if( ! is_post_type_archive( 'post-type-slug' ) || is_admin() || $query->is_main_query() ) { return; } //sort by the meta value is-active in a descending order, i.e. those with a 1 before those without $query->set( 'meta_key' , 'is-active' ); $query->set( 'orderby' , 'meta_value_num' ); $query->set( 'order' , 'DESC' ); } add_action( 'pre_get_posts' , 'custom_sorting' , 100 );
OK, so that was nice and easy, but, now that we’ve used the order and orderby parameters, how are we going to add more criteria and sort orders for them?
There’s a couple of steps to this:
- Update the meta query array to filter for posts with AND without featured images
- Update the order parameter to the array format
- Remove the order parameter
function custom_sorting( $query ) { //don't edit the query if we're not dealing with the main query for //the post-type-slug post type and we're not in the admin area if( ! is_post_type_archive( 'post-type-slug' ) || is_admin() || $query->is_main_query() ) { return; } //add a query to filter for posts that do AND don't have a _thumnail_id value //if you don't add the NOT EXISTS clause you will lose any posts without featured images $meta_query = array( 'relation' => 'OR', //note that you must name elements you wish to filter by 'has-image' => array( 'key' => '_thumbnail_id', 'compare' => 'EXISTS', ), 'has-no-image' => array( 'key' => '_thumbnail_id', 'compare' => 'NOT EXISTS', ), ); $query->set( 'meta_query' , $meta_query ); $query->set( 'meta_key' , 'is-active' ); $query->set( 'orderby' , array( 'meta_value_num' => 'DESC', 'has-image' => 'DESC'*, 'rand' => 'rand' )); } add_action( 'pre_get_posts' , 'custom_sorting' , 100 );
And there you have it, you can now sort by multiple criteria in WordPress.
Links:
Featured Image Credit: