Order by meta value, pro first, then free

Before I fire away, just one note, NEVER (my emphasis) make use of query_posts to create custom queries

Note: This function isn’t meant to be used by plugins or themes. As explained later, there are better, more performant options to alter the main query. query_posts() is overly simplistic and problematic way to modify main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination).

Rather make use of WP_Query or get_posts to create custom queries, but ONLY if you can’t modify the main query with pre_get_posts. For more info, check out this post

Assuming that there are only two values for your meta_key (PLEASE NOTE: For testing purposes, I have used my own meta key, you should replace this with your own), namely pro and free, you can just retrieve posts that has this specific meta_key. Your query will then look something like this:

$args = [
    'meta_key'          => 'packages',
    'orderby'           => 'rand',
    'posts_per_page'    => 5,
];

$the_query = new WP_Query( $args );

If you however have more values than just these two, then you will need to adjust your query to include the meta_value and then make use of the meta_compare parameter. Your adjusted query will then become:

$args = [
    'meta_key'          => 'packages',
    'meta_value'        => 'free, pro',
    'orderby'           => 'rand',
    'meta_compare'      => 'IN',
    'posts_per_page'    => 5,
];

(PS! Before I go on, you should note that the orderby parameter has changed in version 4.0, but this will not work in this instance.

Great, you will now have 5 posts that was retrieved randomly which has either a meta_value of pro or free

Now we need to sort them. The best possible solution is to make use of usort to sort the returned array of posts in $the_query->posts according to the value of the posts meta_value. You will then need to unset $the_query->posts and then reset it with the reordered post array.

Here is the complete code:

<?php
$args = [
    'meta_key'          => 'packages',
    'meta_value'        => 'free, pro',
    'orderby'           => 'rand',
    'meta_compare'      => 'IN',
    'posts_per_page'    => 5,
];

$the_query = new WP_Query( $args );
$post_metas = $the_query->posts;

usort( $post_metas, function( $a, $b ){
    $a = get_post_meta( $a->ID , 'packages', true ) ;
    $b = get_post_meta( $b->ID, 'packages', true ) ;

    if ( $a == $b ) {
        return 0;
    }
        return ( $a > $b ) ? -1 : 1;
    }
);

unset($the_query->posts);
$the_query->posts = $post_metas;

if ( $the_query->have_posts() ) {
    echo '<ul>';
    while ( $the_query->have_posts() ) {
        $the_query->the_post();

        echo '<li>' . get_post_meta( get_the_ID(), 'packages', true ). '</br>' . get_the_title() . '</li>';

    }
    echo '</ul>';
} 
wp_reset_postdata();
?>

You will just need to adjust the query variables to suit your needs and also adjust the loop to display what is needed

This is not the only way to achieve it though, you can also move your sorting function to your functions.php and specifically target this custom query

Leave a Comment