How can I prevent certain custom roles from seeing other custom roles on the user list page?

Method 1, SQL

Notes about your SQL

[Unknown column 'wp_usermeta.meta_key' in 'where clause']

This is solved by adding this to the JOIN part of the query:

JOIN wp_usermeta ON ( wp_usermeta.user_id = wp_users.ID )

You could check the value of the JOINS and, if false === strpos( 'wp_usermeta', $joins ), adding it yourself.

When getting all users, my guess is that WordPress doesn’t join to wp_usermeta since it doesn’t need to deal with capabilities.


Method 2, altering the query object

Maybe using ::set is the correct way and I am calling it incorrectly?

Bingo! You’re calling ::set statically, when it should be called as a method of the object:

$user_search->set('exclude', $users);
$user_search->set('role', 'user');

In PHP, :: denotes a static method call, or functions that are part of the class definition, while -> is used to operate on methods that are part of the class instance. You often see this notation used to talk about method names SomeClass::Method(), which can be confusing – it’s not necessarily saying that you use ::Method to call the method, but rather it’s visually trying to represent it as a method of that class. Typically, on instantiation, you’d do this:

$obj = new SomeClass();
$obj->Method();

Unless the method is declared static, in which case

$obj::Method();

Would be correct.


Not good answer

You can do this with the load-(page) hook and CSS. Unfortunately, there doesn’t seem to be a good way to do this with filters that I’ve found yet.

To do this with CSS, you can do something like this:

add_action( 'load-users.php', function(){

    if ( can_see_editors() ) {
        return;
    }

    echo <<<HTML
<style>
#wpbody ul.subsubsub li.editor {
    display: none;
}
</style>
HTML;
});

Replace the first method in the if with your logic for users who can see the TDF Admin. If they can’t, it continues down to render CSS to the page hiding the element – in your case, highlight the “TDF Admin” filter and inspect it to find it’s CSS class name (as opposed to li.editor).

This fix is pretty weak – anyone can still inspect the page and reveal the link, and nothing is preventing them from altering the URL to point to the URL filtering TDF administrators.