Using WP_User_Query To Retrieve Users Across Multiple Roles

I’ve covered WP_User_Query in a previous post. In short, WP_User_Query is the preferred method for retrieving user information from the database when working with custom queries.

One of the shortcomings of this API method is that it doesn’t allow you to query across multiple roles. So, for example, if you want to retrieve users that meet a certain criteria but may span across multiple roles – say editors and administrators – the API doesn’t support it.

That said, there is a simple strategy that can be used with WP_User_Query to retrieve users across multiple roles.

A Real World Scenario

Before setting up the query, we need to define a scenario in which this may be useful. In one of my recent projects, all users accounts had a piece of meta data associated with them.

Specifically, users had a meta key of ‘featured’ and a value of ‘yes’ or a value of ‘no.’ In code, it worked something like this:

add_user_meta( $user_id, 'featured', 'yes' );

From there, users could either be set as administrators or as editors and I needed to pull back five featured users from each role and then render their display name on the frontend.

Ultimately, the final loop looked something like this:

foreach( $users as $user ) {
  echo $user->display_name;
} // end foreach

Simple, right? But there’s a bit that actually goes into collecting all of the users into that single array.

WP_User_Query and Multiple Roles

Given the state of some of the other API methods, I think that it’s a reasonable assumption that you could setup a call to WP_User_Query like this:

$editor_query = new WP_User_Query(
	array(
		'role'			    =>	array( 'editor', 'administrator' ),
		'meta_key' 		  => 'featured',
		'meta_value'	  =>	'yes',
		'meta_compare'	=>	'=',
		'number'		    =>	5
	)
);

But that’s not the case. The role parameter actually only accepts a single string, not an array.

Instead, you have to actually setup two separate queries and then merge the results into a single array:

// get the featured editors
$editor_query = new WP_User_Query(
	array(
		'role'			    =>	'editor',
		'meta_key' 		  => 'featured',
		'meta_value'	  =>	'yes',
		'meta_compare'	=>	'=',
		'number'		    =>	5
	)
);
$editors = $editor_query->get_results();

// get the featured admins
$administrator_query = new WP_User_Query(
	array(
		'role'			    =>	'administrator',
		'meta_key' 		  => 'featured',
		'meta_value'	  =>	'yes',
		'meta_compare'	=>	'=',
		'number'		    =>	5
	)
);
$admins = $administrator_query->get_results();

// store them all as users
$users = array_merge( $admins, $editors );

Obviously, it’s not the most elegant solution since you have to setup several independent queries, but it does provide a straightforward way of querying users across multiple roles, and then accessing them all from a single, independent query.

As usual, I’m always interested in how others address this issue especially if there’s a more optimal solution so let me know in the comments.

39 Replies to “Using WP_User_Query To Retrieve Users Across Multiple Roles”

    1. Of course, Elliot – glad to help!

      When it comes to stuff like this, I generally think it’s better to do as much work on the server side since any clever end user that’s comfortable with a debug console could end up displaying some information that you intended to hide.

      In your case, it’s nothing too drastic though, so maybe I’m being a bit facetious ;).

  1. I found this solution by Andy Adams on the wordpress stackexchange site. It uses a meta query to pull users by their associated capabilities from the usermeta table. I think it works quite well for this task.


    global $wpdb;

    $user_query = new WP_User_Query( array(
    'meta_query' => array(
    'relation' => 'OR',
    array(
    'key' => $wpdb->prefix . 'capabilities',
    'value' => 'role_one',
    'compare' => 'like'
    ),
    array(
    'key' => $wpdb->prefix . 'capabilities',
    'value' => 'role_two',
    'compare' => 'like'
    )
    )
    ) );

    1. Andy’s a great developer so thanks for sharing this. Love having a couple of different ways to perform the same task.

      The only thing with the code above is that I dislike the concatenation of the $wpdb->prefix with the string in the array. Honestly, it’s not that big of a deal – it’s a matter purely of personal preference. I’m picky like that :).

      Nonetheless, I do appreciate you sharing this snippet as it does provide a bit of a more concise way of doing exactly this.

    1. Thanks for sharing this Julien!

      I can’t necessarily say why it’s faster without digging deeper and benchmarking, but I suspect that it has to do with the query optimization that comes with using WP_Query where as Andy’s method is using a combination of that and the $wpdb object.

      My general rule of thumb is to avoid $wpdb unless WP_Query or WP_User_Query absolutely cannot perform what I need.

  2. Tom, thanks a lot for this very useful article! I was after that! And now thanks to you I know that I’m not crazy…and querying users by several roles just impossible :) In some way. And thanks for the advice of using array’s merging method!

  3. I know this is an old post, but it still turns up pretty early in search results related to this subject. I would like to point out that anybody who uses Andy’s or Ben’s “User Meta” method will run into an issue in the event they have two roles which contain one another in the name.

    For example, my site has both “administrator” and “regional-administrator”. If I tried to use either of those queries for ‘administrator’, it would find it within the wp_capabilities meta value:

    a:1:{s:22:”regional-administrator”;b:1;}

    If you are using something like Ben’s, you should wrap it in double quotes. If there are dozens of roles or an unknown number of roles, and you’d like to exclude one, meta queries are the way to go. However, if you want specific roles, use the array_merge method, because an OR in meta_query is much more expensive.

    So the exclusion code should go: (note the double quotes in ‘value’)

    $meta_query = array( array(

    ‘key’ => $wpdb->prefix . ‘capabilities’,

    ‘value’ => ‘”‘ . $role . ‘”‘,

    ‘compare’ => ‘like’,

    ) );

  4. Hey Tom,

    I ran into this article myself recently, as I was searching up a way to target multiple roles, and in the process of researching, had found a discussion on StackExchange that proved interesting:

    http://wordpress.stackexchange.com/questions/39315/get-multiple-roles-with-get-users/209593#209593

    The role__in parameter mentioned there would be a way of achieving your desire for a more elegant code snippet. :)

    The first code block you offered in your article would now be possible, just by changing ‘role’ to ‘role__in’, tested it out before posting here:

    $editor_query = new WP_User_Query(

    array(

    ‘role__in’ => array( ‘administrator’, ‘editor’ ),

    ‘meta_key’ => ‘featured’,

    ‘meta_value’ => ‘yes’,

    ‘meta_compare’ => ‘=’,

    ‘number’ => 5

    )

    );

    $editors = $editor_query->get_results();

    foreach( $editors as $editor ) {

    echo $editor->display_name;

    } // end foreach

    Hope this helps, keep up the good work Tom! :)

    1. Thanks Michael for this, it solves the problem I had trying to get info on multiple roles. I posted a comment yesterday asking how to get rid of duplicates in the case where you have to run a separate query per role and then use merge_array. But your code does it all in one, very neat.

      The issue I am left with is how to generate the roles on the fly from selections made in a checkbox form.

      So instead of the code for the ‘role__in’ line being hardcoded for the (maximum) 4 roles I may want to search for, I need to allow someone to select just two roles say (using a checkbox in a form), and then the ‘role__in’ line needs to be populated with a variable.

      i.e. :

      This works now for me:

      ‘role__in’ => array(‘role1’, ‘role2’, ‘role3’, ‘role4’)

      But someone may just want users associated with role2 and role4 say. Hence I would want to create a variable such as:

      $var1 = ” ‘role1’, ‘role2’ “;

      And then have a line like this:

      ‘role__in’ => array($var1)

      For some reason I just can’t get it to work. Is it possible to pop a variable in that query? I’ve tried reading the PHP online help for arrays and couldn’t see anyone do this.

      Thanks,

      Jeremy.

      1. Hey Jeremy,

        The role__in parameter must be fed an array with a role specified at each index. This here:

        $var1 = "'role1', 'role2'";

        Would actually insert a single string into the array, like this:

        'role__in' => array("'role1', 'role2'"),

        So role_in’s stuck, as it doesn’t have information it can work with.

        This, on the other hand:

        $var1 = array( 'role1', 'role2', 'role3', 'role4' );

        Would give role__in something it could work with:

        'role__in' => $var1,

        Now, since you’re only wanting to specify certain roles in the array, based on what a user has chosen, that’s going to take more work. I don’t know how this form of yours returns it’s data on what roles a user has chosen, but know that the data provided from it will need to be fed to an array (the array_push method can be your friend here). And then, said array would need to be fed to role_in, as specified earlier.

        Consider the following:

        $var1 = array();

        array_push($var1, 'administrator', 'editor');

        This would work with my earlier code example:

        $var1 = array();

        array_push($var1, 'administrator', 'editor');

        $editor_query = new WP_User_Query(

        array(

        'role__in' => $var1,

        'number' => 5

        )

        );

        $editors = $editor_query->get_results();

        foreach( $editors as $editor ) {

        echo $editor->display_name;

        } // end foreach

        You’d probably be looking to have a for loop wrapped around the array_push() call here, with each role from your form submission added to the array one by one. Like I said earlier, without knowing what the form’s giving us to work with, it’s kinda tough to specify what should be done here.

        That said, hope this provides some added insight on the matter!

        1. Hi Michael,

          Many thanks for your reply … since I posted I have been hovering around array_push as it just sounds like what I needed … and I eventually got it working, so you were correct that it would be my friend! Looks like I managed to not properly add data into the array before I used it. Sometimes you just can’t see the wood for the trees eh.

          Thanks so much for the support on this site, knowing there are willing and able minds out there makes a huge difference in those dark, on your own, at the keyboard moments!

          Jeremy.

  5. Maybe via SQL:

    SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->prefix}_users

    LEFT JOIN {$wpdb->prefix}_usermeta AS mt1 ON ( {$wpdb->prefix}_users.ID = mt1.user_id )

    WHERE ( mt1.meta_key = ‘{$wpdb->prefix}_capabilities’ AND mt1.meta_value REGEXP ‘role1|role2’ )

    GROUP BY {$wpdb->prefix}_users.ID

    1. Usually one of the availbale APIs can achieve what you need without using direct SQL, but if you end up using it, make sure you’re leveraging the $wpdb API.

      Specifically, make sure that you’re parameterizing your queries, and that you’re sanitizing the incoming data properly so to avoid any injection attempts.

  6. Hi,

    I am looking for a solution in this area … I have some code that follows your opening code suggestion up to the point where the array_merge takes place to get users from more than one role.

    If I do a foreach loop for each individual array I can access the contents.

    If I do an array_merge on the arrays I can similarly do a foreach loop and access the contents.

    However, I have duplicates in there and could not work out how to remove the duplicates. and then loop through a foreach to print out the user’s details.

    I tried code like:

    $users2 = array_unique($users)

    … and then tried to run the same foreach loop on $users2 … but I get an error message “Catchable fatal error: Object of class WP_User could not be converted to string”

    Any advice?

    Thanks,

    Jeremy.

  7. There’s still something I’m missing on the code I’m using, think I’ve been staring at it too long now & can’t see the wood for the trees!

    The code below uses the suggestion above to use ‘role__in’ and it works:

    | $array_var=array(‘role1’, ‘role2’, ‘role3’, ‘role4’);

    | $args5 = array(

    | ‘role__in’ => $array_var,

    | ‘orderby’ => ‘user_nicename’,

    | ‘order’ => ‘ASC’

    | );

    |
    | $all_users = get_users($args5);

    But I would like to have the bit in brackets for $array_var created dynamically as a variable. i.e. have some code with a checkbox in a form allows people to select which roles they wish to get info for and then those roles are put into a variable like this:

    | $var=”‘role1’, ‘role2’, ‘role3’, ‘role4′”

    Then use $var and proceed as before:

    | $array_var=array($var);

    | $args5 = array(

    | ‘role__in’ => $array_var,

    | ‘orderby’ => ‘user_nicename’,

    | ‘order’ => ‘ASC’

    | );

    |
    | $all_users = get_users($args5);

    Except it returns an empty result set.

    Any ideas?

    Appreciate any assistance.

    Thanks,

    Jeremy.

  8. Jeremy, it looks like your $var is already an array. Try it like this:

    `
    <?php

    $args5 = array(

    ‘role__in’ => $var,

    ‘orderby’ => ‘user_nicename’,

    ‘order’ => ‘ASC’

    ); ?>

    `

    Also thanks to everyone who’s contributed to this post. Glad to have finally stumbled upon an answer to the multiple user role query.

  9. Hey Tom,

    I scanned the threads above but I didn’t fully understand everything. I’m looking to write a WP_User_Query that will fetch all users except super admins. Can I use something like:

    ‘role__not_in’ => ‘super admin’,

    ?

    1. Super Admins are those who have access to all sites as part of a network sites. This is different than all of the other roles because the other roles have slugs such as administrator and contributor and subscriber.

      Unfortunately, I don’t do any work with multisites; however, I do know you could look in the wp_usermeta table at the wp_capabilities key and then see what value is stored.

      For example, for a standard installation, you’re going to see wp_capabilities' set equal to, perhaps,a:1:{s:6:”author”;b:1;}`, if they are an author.

      Since you’re looking at user metadata, you may be able to run a meta query like this and use the given key and value for Super Admins. But this is just via deduction – not experience so good luck :).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.