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.

18 thoughts on “Sort WordPress users by custom meta value with get_users

  1. Thanks, It’s sorting my users by last name, but in the wrong order, I want it to start at A, it’s starting from Z. I did try reversing the ‘compare’ method to ‘ array(
    array(
    ‘key’ => ‘last_name’
    ‘value’ => ”,
    ‘compare’ => ‘>=’ // comparison method (optional: =, >, <, etc)
    )
    ));

  2. Hi there,
    Fortunately and unfortunately, what you looking for does exist in WP core, but the documentation doesn’t let you know it does ;)

    I fount that :

    $args = array(
    ‘meta_key’ => ‘points’,
    ‘orderby’ => ‘meta_value_num’,
    ‘order’ => ‘DESC’,
    ‘number’ => 100,
    ‘fields’ => ‘all_with_meta’
    );

    Worked very well. Hope it helps.

  3. Please ignore my last comment and remove both, I thought it worked but it doesn’t… It appears that the default order was corresponding to my meta_field order, so I thought that was working… Until I updated the meta_field values and noticed my mistake.

    Sorry about that, thanks for your post ;)

  4. That’s me again, I know I made a mistake and I wanted to give you my solution.

    If you look at my code here > http://img.saika.li/KTra … you’ll see that I used $wpdb with a custom query to sort my users based on my “points” meta_field value. The trick was to use CAST(… AS SIGNED) to convert the value to an integer.

    Credits to http://stackoverflow.com/questions/4256271/wordpress-wpdb-usermeta-sort-by-meta-value

    See ya!

  5. Thanks for the handy tip! As Patrick says, perhaps not the most popular blog topic, but enough people seem to have found this very handy to warrant its existence.

    I personally spend way too much time trying to make WordPress do all of the heavy lifting with its core functions, forgetting that I can use some actual, regular PHP to achieve the result I’m after.

    Thanks!

  6. Thanks so much for this! Very handy. For folks that are trying to do use something like last_name, where you need to sort alphabetically, you can do this:

    `
    //these are the arguments for the get_users function below
    $args = array(
    ‘exclude’ => ’1′,
    ‘fields’ => ‘all_with_meta’,
    ‘meta_query’ => array(
    array(
    ‘key’ => ‘last_name’, // the meta field (or key) we want to target
    )
    ));

    //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 sort_by_last_name($a, $b){
    return strcasecmp($a->last_name, $b->last_name); //THIS IS THE CHANGE THAT MAKES ALPHA SORTING HAPPEN
    }

    //usort sorts our $users array with our function sort_by_last_name()
    usort($users, ‘sort_by_last_name’); ?>

    foreach ($users as $user) {
    // do something rad!

    `

  7. I’m sorting by a custom user field, job_title. This got me pretty close, but outputs by job_title in alphabetical order, so now I just need to figure out how to define the order, such as: 1) executive, 2) manager, 3) employee. Thanks for moving me in the right direction!

  8. Thanks Clark, with a few tweaks to your code I successfully was able to sort by last name:

    Query for selecting the users (note in my case I only wanted people with the Contributor role):

    $blogusers = get_users( array( ‘role’ => ‘Contributor’, ‘fields’ => ‘all_with_meta’ ,
    ‘meta_query’ => array(
    array(
    ‘key’ => ‘last_name’,
    ),
    ) ) );

    Function for sorting by last name (note that I changed the comparison below to be “” — this sorts A to Z; keep it “>” if you want Z to A):

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

    usort($blogusers, ‘cmp’);

    • Looks like the second chunk of code got stripped in the comments.

      In any case, take Clark’s cmp function and simply reverse the comparison operator to be less than instead of greater than if you want to sort A to Z.