I’m building a site that publishes reviews of hosting companies. For each review I store Price, Rating, and a “Top Pick” flag as post meta. Visitors can sort the listings by Price, Rating, or Post Date.
The client requested that Top Picks always appear first. That means if a user sorts by Price (Low to High), the result set should list all Top Picks sorted by price low to high first, followed by the remaining reviews sorted by price low to high.
WordPress 4.0 introduced the ability to specify multiple orderby parameters and to set the order for each independently, but it still required a single meta_key when ordering by meta and therefore limited ordering by multiple meta keys at once. An example of that older approach looked like this:
'be_rating',
'orderby' => array(
'meta_value_num' => 'DESC',
'post_date' => 'ASC',
),
) );
With WordPress 4.2 the query system was improved and now supports ordering by multiple meta keys. Using a meta_query that defines each meta key you want to sort by, you can pass an orderby array that references those keys. Below is the approach I use for the sort options shown in the screenshot.
'review',
'posts_per_page' => 10,
'paged' => get_query_var( 'paged', false ),
'meta_query' => array(
'relation' => 'AND',
'be_top_pick' => array(
'key' => 'be_top_pick',
'compare' => 'EXISTS',
),
'be_price' => array(
'key' => 'be_price',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
),
'be_rating' => array(
'key' => 'be_rating',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
),
)
);
// Sort Results
$current_sort = isset( $_GET['hosting-sort'] ) ? esc_attr( $_GET['hosting-sort'] ) : 'most-recent';
switch ( $current_sort ) {
case 'most-recent':
$args['orderby'] = array(
'be_top_pick' => 'DESC',
'post_date' => 'DESC',
);
break;
case 'price-high':
$args['orderby'] = array(
'be_top_pick' => 'DESC',
'be_price' => 'DESC',
);
break;
case 'price-low':
$args['orderby'] = array(
'be_top_pick' => 'DESC',
'be_price' => 'ASC',
);
break;
case 'rating-high':
$args['orderby'] = array(
'be_top_pick' => 'DESC',
'be_rating' => 'DESC',
);
break;
case 'rating-low':
$args['orderby'] = array(
'be_top_pick' => 'DESC',
'be_rating' => 'ASC',
);
break;
}
$loop = new WP_Query( $args );
Explanation of the approach:
- Start by building the base query arguments (post type, posts per page, paged).
- Include a meta_query entry for every meta key you might sort by. Using ‘compare’ => ‘EXISTS’ ensures posts that have the key are included without restricting values.
- Assign a unique key name to each meta_query entry. When ordering by numeric meta values, include ‘type’ => ‘NUMERIC’ so WordPress uses meta_value_num for numeric sorting.
- In the theme, the sort dropdown adds a GET parameter (hosting-sort). Read that GET variable and set a sensible default (most recent) if it isn’t present. Then modify $args[‘orderby’] according to the selected sort method. For meta-based ordering, reference the meta_query keys you defined above.
This pattern keeps Top Picks at the top of the list while still honoring the chosen secondary sort (price, rating, or date) for both Top Picks and the remaining posts. It leverages WordPress 4.2+ improvements to order by multiple meta keys cleanly and reliably.