Tutorials 20 January 2021

WordPress Admin Search: Extending the Results

Tassos Antoniou

11 min read
Image for WordPress Admin Search: Extending the Results

Sometimes finding content on a website can be hard. As a WordPress Admin, the easier it is to find the content you’re after the better. Thankfully, WordPress provides a search function on most of the Admin screens you’ll use (for example the Posts screen). Whilst this search function is good, it it nonetheless limited in a number of ways.

For example, if you search for a post in the Posts admin screen, the search function will, by default, look for the search query text in the post title, post excerpt, and post content and then will return those results. But, what if you want to search with keywords that are related to a custom field? This information isn’t included in the admin post list table search results which can be frustrating.

In a previous article we learned how to add a custom field in our post types. Let’s take a look at how we can include a custom field in the search results as well.

In our worked example on custom fields, we created a custom field called ‘source’ in one of our Posts and inserted a URL value.

If you go in the Posts list table and use one of these ‘source’ values in the search bar you won’t get any results as WordPress simply doesn’t look at custom fields when searching by default.

In order to change this, we’ll need to extend the original hook that WordPress provides for the search feature. The hook we have to extend is the pre_get_posts that is included in the wp-includes/class-wp-query.php file around line 1779 inside the get_posts function.

The WordPress Query

Every time you visit a page, either in the front view or admin area, a chain of code scripts execute in order to create the WP_Query Object corresponding to your request, along with its properties.

Let’s create a temporary view of some of the query’s properties output in order to understand what changes occur in the WP_Query object in every case.

To view the query before it is executed, we will append the print in the pre_get_posts action hook. The pre_get_posts filter fires after the query variable object is created, but before the actual query is run.

In your active theme’s functions.php file insert this piece of code:

add_action( 'pre_get_posts', 'print_query' );
function print_query( ) {
	print_r ( $GLOBALS['wp_query']->query );
	echo "<hr>";
	print_r ( $GLOBALS['wp_query']->query_vars );
	echo "<hr>";
}

Now, if you visit the Posts admin screen, the query and query_vars outputs will be printed at the top.

To view the code more easily you can copy and paste it into a free online PHP beautifier.

The $GLOBALS['wp_query']->query array (first out) will look like this:

Array (
[order] =>
[orderby] =>
[post_type] => post
[posts_per_page] => 20
[post_status] =>
[perm] =>
)

In the case of a search in the Posts admin screen for example, the array output will change including the search string. If we search for ‘wordpress.org’ for example, it will look like this:

Array (
[m] => 0
[cat] => 0
[s] => wordpress.org
[paged] => 1
[order] =>
[orderby] =>
[post_type] => post
[posts_per_page] => 20
[post_status] =>
[perm] =>
)

Assemble the Code

With the above information in mind we will insert the correct code to achieve the search function we require. To do this, head back to your active theme’s functions.php and delete the code you inserted a few moments ago in the previous step. Then insert the code below:

add_action( 'pre_get_posts', 'extend_admin_search' );
function extend_admin_search( $query ) {
	$post_type = 'post';
	$custom_fields = array("source",);
    if( ! is_admin() )
    	return;
  	if ( $query->query['post_type'] != $post_type )
  		return;
    $search_term = $query->query_vars['s'];

    $query->query_vars['s'] = '';

    if ( $search_term != '' ) {
        $meta_query = array( 'relation' => 'OR' );
        foreach( $custom_fields as $custom_field ) {
            array_push( $meta_query, array(
                'key' => $custom_field,
                'value' => $search_term,
                'compare' => 'LIKE'
            ));
        }
        $query->set( 'meta_query', $meta_query );
    };
}

Next step is to change the $post_type and $custom_fields variables to values that apply to your requirements. Let’s see what we did in the code.

  • $post_type = 'post'; – Here we define the post type we want to search.
  • $custom_fields = array("source",); – Here we define the custom field(s) we want to search.

Because we are working with the pre_get_posts hook, it is important to make sure in our code that we apply our custom search extension while we are on the correct page. Τo tell WordPress to display our code under specific conditions, we use some conditional tags.

  • The if ( ! is_admin() ) return; condition ensures that if we are not in the admin area, none of our code will execute.
  • The if ( $query->query['post_type'] != $post_type ) checks whether we work on the post type we have defined in the $post_type variable. In our case, post.
  • The $search_term = $query->query_vars['s']; is where we keep the search string in a variable and then we set it to empty again $query->query_vars['s'] = ''; otherwise it won’t find anything.

Now, with these updates in place, if you refresh the last search you will get the correct results. In our case, we see the ‘Post 1’ post in the results which includes a ‘source’ custom field entry: ‘https://dev.wordpress.org/reference/functions/add_post_meta/’.

improve the wordpress admin search

Now we’ve ‘fixed’ our Admin search we can move on to improving our front-end search function so our website users can also search for custom fields.

In the same way that previously the Admin search would yield no results for custom fields, the front end search also fails to return any results for custom fields.

Ideally we want users to be able to search for these fields. In this example, we have a custom ‘source’ field in one of our posts that currently contains the url ‘https://dev.wordpress.org/reference/functions/add_post_meta/’. If a user searches for ‘wordpress.org’ then this post should show up in the results. Let’s find out how to do that.

The Code Required

Head to your functions.php file again and copy the code you pasted in earlier. Now, paste this directly below that code (so you have two copies of this code in functions.php) Now, change the name of the function in this second lot of code to something like extend_front_search. It should look like this:

add_action( 'pre_get_posts', 'extend_admin_search' );
function extend_admin_search( $query ) {
	$post_type = 'post';
	$custom_fields = array("source",);
    if( ! is_admin() )
    	return;
  	if ( $query->query['post_type'] != $post_type )
  		return;
    $search_term = $query->query_vars['s'];

    $query->query_vars['s'] = '';

    if ( $search_term != '' ) {
        $meta_query = array( 'relation' => 'OR' );
        foreach( $custom_fields as $custom_field ) {
            array_push( $meta_query, array(
                'key' => $custom_field,
                'value' => $search_term,
                'compare' => 'LIKE'
            ));
        }
        $query->set( 'meta_query', $meta_query );
    };
}

Next, we need to remove the if ( $query->query['post_type'] != $post_type ) return; condition.

add_action( 'pre_get_posts', 'extend_front_search' );
function extend_front_search( $query ) {
	$post_type = 'post';
	$custom_fields = array("source",);
    if( is_admin() )
    	return;
    $search_term = $query->query_vars['s'];

    $query->query_vars['s'] = '';

    if ( $search_term != '' ) {
        $meta_query = array( 'relation' => 'OR' );
        foreach( $custom_fields as $custom_field ) {
            array_push( $meta_query, array(
                'key' => $custom_field,
                'value' => $search_term,
                'compare' => 'LIKE'
            ));
        }
        $query->set( 'meta_query', $meta_query );
    };
}

We just need to maintain the condition if( is_admin() ) return; to ensure the code will apply only in the front end and we are good to go.

An Alternative Strategy

Another way to extend WordPress search to include custom fields is by altering the SQL query.

The WP_Query class helps you with that by breaking it into pieces as described around line 2897 of the wp-includes/class-wp-query.php file.

$clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );

$where    = isset( $clauses['where'] ) ? $clauses['where'] : '';
$groupby  = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
$join     = isset( $clauses['join'] ) ? $clauses['join'] : '';
$orderby  = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
$distinct = isset( $clauses['distinct'] ) ? $clauses['distinct'] : '';
$fields   = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
$limits   = isset( $clauses['limits'] ) ? $clauses['limits'] : '';

As we have mentioned before in the custom fields articles, all the custom field data is stored in the database in the ‘postmeta’ table which is not included in the WordPress search by default. So our first step is to join these two tables by using the code below:

function join_meta_table( $join ) {
    global $wpdb;

    if ( is_search() ) {    
        $join .=' LEFT JOIN '.$wpdb->postmeta. ' ON '. $wpdb->posts . '.ID = ' . $wpdb->postmeta . '.post_id ';
    }
    return $join;
}
add_filter('posts_join', 'join_meta_table' );

We used the posts_join hook of course as it filters the JOIN clause of the query.

Next, we will use the posts_where hook to alter the WHERE piece of the query to meet our needs.

function modify_where_clause( $where ) {
    global $pagenow, $wpdb;

    if ( is_search() ) {
        $where = preg_replace(
            "/\(\s*".$wpdb->posts.".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
            "(".$wpdb->posts.".post_title LIKE $1) OR (".$wpdb->postmeta.".meta_value LIKE $1)", $where );
    }
    return $where;
}
add_filter( 'posts_where', 'modify_where_clause' );

Last but not least we need to prevent duplicates. To achieve this we use the posts_distinct hook which filters the DISTINCT clause of the query.

function prevent_duplicates( $where ) {
    global $wpdb;

    if ( is_search() ) {
        return "DISTINCT";
    }
    return $where;
}
add_filter( 'posts_distinct', 'prevent_duplicates' );

Now let’s see what happens in the frontend if we search for the “wordpress.org” string.

wordpress search on the front end

As expected, the results are correct this time. WordPress search found “post 1” as a result as it contains the ‘source’ field with value ‘https://dev.wordpress.org/reference/functions/add_post_meta/

Custom Post Types in WordPress Search Results

Another thing you might want to do is to make Custom Post Types searchable (if you’re not sure how to create a custom post type then make sure you check out our articles on this here).

Let’s imagine we have created a custom post type named ‘books’ and we want this post type to show up in search results. This time we don’t have to recreate the SQL query, but only re-define the post types in the pre_get_posts hook as shown described below. The code has to be placed once again in the functions.php file.

function tg_include_custom_post_types_in_search_results( $query ) {
    if ( $query->is_main_query() && $query->is_search() && ! is_admin() ) {
        $query->set( 'post_type', array( 'post', 'books' ) );
    }
}
add_action( 'pre_get_posts', 'tg_include_custom_post_types_in_search_results' );

What we did here was to add the desired post types to be included in the search results as arguments to the post_type hook.

If you wish to exclude a type, just remove it from the array.

Conclusion

Improving the search on your WordPress website can be really helpful and a huge time saver. For your end user it’s also important that they can search for both custom post types and also custom fields. Hopefully the instruction above have given you an insight into how to achieve this on your own website.

Host your WordPress Website with Pressidium!

View our price plans

Host your website with Pressidium

60-DAY MONEY BACK GUARANTEE

SEE OUR PLANS

Do you like this article?

Subscribe to our blog and get awesome WordPress content straight to your inbox.

SUBSCRIBE

OUR READERS ALSO VIEWED:

Filter Content Using Tags in WordPress

Filter Content Using Tags in WordPress

Got a large website? Then you might be looking at ways of helping your users filter certain content. Tags might be the perfect solution.
Tassos Antoniou
Tassos Antoniou
9 min read
Using WebP Images in WordPress

Using WebP Images in WordPress

As you’re probably already aware, when it comes to websites, speed matters! One of the top things you can do to speed up your website is by reducing the page size. The smaller it is, the less data that has to be downloaded. The less data,…
Alexander Newnham
Alexander Newnham
8 min read
Adding Fields to WordPress Menu Items

Adding Custom Fields to WordPress Menu Items

Want to jazz up your WordPress menu with some custom fields? Find out how with this step-by-step tutorial.
Tassos Antoniou
Tassos Antoniou
7 min read
The Core Design Principles Behind the Pressidium Brand

The Core Design Principles Behind the Pressidium Brand

Brand guidelines can help shape a company. Take a look at our values, philosophy and (of course) how we design here at Pressidium!
Ioanna Aravani
Ioanna Aravani
6 min read
SUBSCRIBE