Creating a simple frontend user search in WordPress

I recently had to create a custom user search in WordPress. My client’s website was having issues searching normally. I tried a plugin that allowed better searching but to no avail.

Alas, I created a custom page template and wrote a custom script in php that searches the database. This code is very basic because it only checks first and last name, but not both. In that regard, it can be used as a starting point on which to build on and improve even more so.

Creating the form

First things first, a form is needed to take in the search term. Let’s make a real basic text input with a submit button.

<?php $st = (isset($_GET['usersearch']) ? $_GET['usersearch'] : '' ); ?>
<form action="" method="get">
    <label for="usersearch">Search Term:</label>
    <input name="usersearch" id="usersearch" value="<?php echo $st; ?>" type="text">
    <input name="dosearch" type="submit" value="Submit">

The first line simply sets the variable $st to either the last searched item (inside the $_GET variable, or to an empty string using a simple ternary operation.

The form is pretty standard from there. You can see the method is set to get and the text input name value is usersearch, so the search term will be accessible through $_GET['usersearch']. The forms action value is set to blank meaning the same page including this code will also process the search and display results.

Search Logic

Next step is to create some code to search the database for the user’s input.
This code goes on top of the page before everything else.

<?php //check permissions first!
if (!current_user_can("create_users") ){
    wp_die("Access denied, must be logged in as admin.");
} else if( isset( $_GET['usersearch'] ) ){
    //$wpdb needs to be made global, this lets us use it on a page template
    global $wpdb;
    //some cleanup to the search term, as well as caching it to $usersearch
    $usersearch = stripslashes( trim($_GET['usersearch']) );
    //$wpdb->prepare() is a fast and safe method for performing a MySQL query
    $stmt = $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta AS um
        WHERE ( um.meta_key='first_name' AND um.meta_value LIKE '%%%s%%') OR
        (um.meta_key='last_name' AND um.meta_value LIKE '%%%s%%')
        ORDER BY um.meta_value 
        LIMIT 150",
        $usersearch, $usersearch );
    //results are cached in the variable $results using get_col()
    $results = $wpdb->get_col( $stmt );

Now I’m keeping this pretty tight with the function current_user_can(). The function checks permission based on whether not the current user can create new users. If the current user cannot create users, I am very bluntly letting them know with wp_die(). The error won’t likely look very nice, but I don not really care for this example. By default only users with Administrator role can create users, so Editors, Authors, Contributors, Subscribers and regular visitors are not going to be using my form today, sorry! Admittedly this is pretty strict for a front-end search tool, so feel free to change this however you see fit. A list of capabilities such as “create_users” can be found on the Codex.

As for the rest of the code, following else, we have a simple SQL query. The $wpdb variable needs to be globalized and then can be used to call prepare, which is important to use when dealing with user input. Most importantly, using prepare sanitizes the data and is used to prevent a sql injection. Prepared statements may also grant significant performance benefits as well.

For more information on working with $wpdb and interfacing with the Database in WordPress, see the Codex.

The query itself is pretty simple and has one big limitation: the query is essentially taking the search term and matching it against the first name OR last name. This means that if someone is going to look for Jonathan Smith, he or she would have to search Jonathan or Smith, as the entire string “Jonathan Smith” would have to match either the first name or last name. This can be improved by separating the search terms and performing multiple queries. For now we’re keeping it really simple and only searching for first or last name, but feel free to go beyond the call of duty here and make a better query.

Outputting the search results

The last bit of code searched our database for the search term and stored the results in the array $results. Avoiding anything too fancy here, I’m displaying results in a table.

        //$metanames a simple array containing the names of the meta values
        //It will allow us to loop through them to keep the code simple and clean
        <?php $metanames = array("first_name", "last_name") ?>
        <?php foreach($results as $u){
            echo "<tr>";
                echo "<td>";
                    //$u is going to be the users id
                    echo $u;
                echo "</td>";
                //now we can loop through the $metanames variable
                foreach($metanames as $m){
                    echo "<td>";
                        echo esc_attr( get_user_meta( $u, $m, true ) );
                    echo "</td>";
                echo "<td>";
                    //here we can provide a link to edit the user
                    echo "<a href='<?php echo admin_url() ?>/user-edit.php?user_id={$u}'>Edit</a>";
                echo "</td>";
            echo "</tr>";
        } ?>
<?php endif; ?>

First, an if statement checks if the $results variable is set and that it is not empty to avoid any unforeseen errors. After that we proceed to create the opening table tags.

The following line creates an array of meta value names.

$metanames = array("first_name", "last_name")

In this case I’m displaying first and last name so I’m using the the meta name as it appears in the meta_key column of the usermeta table in the WordPress database. The reason for the array is to loop through each item and grab them one by one. Looping and constantly using get_user_meta() on every iteration can be very taxing, feel free to explore other options for retreiving this information, this method is just the quick and easy route. This is part of the reason I’ve used LIMIT 150 in the SQL query.

Following the $metanames array is the foreach loop that goes through each meta value in the array to create a new row. The logic here is pretty simple, it loops through each user, and within each loop is yet another loop that goes through each of the meta values in the $metanames array. Each meta value name is pulled from the database and displayed in the table. The results of a search for “John” might look something like this:

69 John Ipsum Edit
66 Samantha Johnson Edit


So we’ve created a very basic user search for WordPress. There are a few advantages to this search. Firstly, it’s on the front end of the website and could be changed to allow anyone to search through your users (in which case you would probably link to an archive page rather than the edit screen like I did). The search also has the advantage of searching for first name and last name, which the default WordPress user search does not do. There is also lots of room for improvement, such as perfecting the SQL query and searching for other values like a users website, bio or password. Ok, maybe not password, but you get the idea. If anything it’s a good starting point for a robust search.

Additonal Links and Resources

WordPress Filter user_search_terms that can be used to add additional search terms to the backend search.

More info about $wpdb, WordPress’s database interface object.

This is the plugin I tried to use called Improved User Search In Backend. Although it didn’t work for me it is highty referenced and mine was a special case so I don’t blame the plugin. Worth a shot.

  • Hi, thank you for this tutorial,

    I am trying to use it on my own web, but im getting the next error:
    Warning: Invalid argument supplied for foreach()

    How could i fix it?

  • I wrote a plugin a while back to display a list of users and it includes a search function. Simple User Listing will let you customize the display output via templates and doesn’t require any custom SQL queries: