Sort WordPress users by custom meta value with get_users

I’ll be honest. This one took me a while. I spent a lot of time looking for a solution but there doesn’t seem to be a whole bunch of discussion around user queries in plain ol’ WordPress. I guess most of the big user action has moved on to bbPress and BuddyPress.

For those of us with a sizable user base contained only inside WordPress however, actually doing things with users takes a bit of elbow grease. I’m sure all the same possibilities exist, but people don’t seem to talk or write about them as often, which is where this guide comes in.

Today we’re going to look at getting WordPress to sort users by a custom meta value, something that would be useful if your users have any sort of scores, totals, or custom profile fields.

Over on Android and Me, all users have a point total. We then rank all users by that point total and assign everyone a percentile rank. To call up thousands of users based on a custom meta value seemed daunting, and it was, until I got a bit more familiar with WP_User_Query.

WP_User_Query was introduced in WordPress 3.1 and brings us an easy and flexible way to query our user database. We’ll be using the function get_users, which is basically a wrapper for WP_User_Query. You’ll notice the function includes an ‘orderby’ parameter, but the only options given are:

orderby – Sort by ‘nicename’, ‘email’, ‘url’, ‘registered’, ‘display_name’, or ‘post_count’.

Notice we’re missing ‘meta_key’ or ‘meta_value’, so we’re on our own when it comes to ordering these guys by a custom field. Also of note is ‘post_count’, which I could totally see coming in handy on certain community blogs (MAKE A NOTE!). Now that we know what we’re after and what tools we’ll need to make it happen, it’s time we get started.

The Code

<?php 
//these are the arguments for the get_users function below
$args  = array(
  'fields' => 'all_with_meta',
  'meta_query' => array(
    array(
    'key' => 'points', // the meta field (or key) we want to target
    'value' => '2',    // the value we want to target (optional)
    'compare' => '>='  // comparison method (optional:  =, >, <, etc)
    )
));

//get_users calls WP_User_Query and returns an array of matching users
$users = get_users($args);

//custom function for comparing the data we want to sort by
function cmp($a, $b){
  if ($a->points == $b->points) {
    return 0;
  }
  return ($a->points > $b->points) ? -1 : 1;
}

//usort sorts our $users array with our function cmp()
usort($users, 'cmp');

//leaving an array of $users sorted by the value of meta 'points'
foreach ($users as $user) {  
  // do something rad!
}

I've commented the code above so hopefully you were able to follow along in the actual markup, but if you'd like a short summary: here we go!

I start off with a short list of arguments, or options, that get_users() needs to return the users I want. A full list of parameters can be found on the get_users page in the Codex. The key here is meta_query, which lets me specify the user meta field I'd like to target and compare. I so happen to be calling up users based on their point totals, so I call any user with points above or equal to 2 (any below that I can assume is a scrub and just rank at the bottom of the pile to save some query juice).

Once I've got my $args, I go ahead and run get_users($args). Doing so will return a full list of users that match the arguments, all handily stored in the array $users.

But what's that? They aren't sorted? While we were allowed to compare points while calling the users, no actual sorting took place. This is where we get a bit tricky. Instead of relying on WordPress to sort the users for us, something the Codex says isn't possible right now, we're going to bust out PHP's usort along with a simple custom function for comparing the points.

Our custom function, cmp(), is just a little check that runs that compares the point total of a user with the other point totals in the array. If the totals are the same, the function returns '0'. If they are different, the function returns a '1' or '-1'. That simple math is enough to let usort crunch through thousands of users in seconds, something I'd spent a couple weeks trying to figure out how to make WordPress do for me. Oh well, you know how that goes.

Now that we've got $users in order by the meta field 'points', all that's left is to do something rad. Simply use a foreach (PHP's version of The Loop) to run through the users in the array and accomplish something. In my case, it was doing even a bit more math to calculate each user's percentile in order to show what everyone's point totals really meant in relation to one another.

I know not everyone's blog has a point system, but if you've got any amount of users or even multiple authors this might come in handy. I used WP_User_Query all over the place on the WordUp Austin site. On Android and Me, I list the author pictures in the sidebar. Now, if I wanted to, I could order them by post count or point totals.

Slowly but surely I'm accumulating bits of community-focused solutions like this one and soon I'll be able to say with complete certainty that my WordPress install would slay any bbPress or BuddyPress install you can toss at it.