Build Your Own Custom WordPress Search
The WordPress search function is much maligned and there are numerous plugins available to add enhancements but they don’t always provide what you want, especially if you are trying to build a secondary search engine that has specific requirements.
In this article, we’ll look at how easy it is to build your own custom WordPress search and discover some surprising WordPress search secrets on the way.
Despite the numerous plugins that will enhance the WordPress search from ordering by relevancy to adding facet searching to including custom fields in the search, there are occasions when any combination of plugins won’t do exactly what you want and you are left with no option but to get your hands dirty and write your own.
In this article, I will walk you through a custom search case study. You’ll find that it’s not actually that difficult to build-your-own search page and you’ll discover some hidden, perhaps, features of the built-in search function.
But before I do that, let’s take a look at what you can achieve simply by putting your own search form together.
Extending WordPress Search Through An Extended Search Form
Behind a WordPress search sits the WP_Query class. If you’ve done any previous work with your own custom loops then you’ll likely be familiar with WP_Query.
WP_Query has a stack of possible parameters, many of which can simply be specified in a search form (or directly on an URL) that will change the search behavior.
For example, to turn a normal search into a search on a custom post type called product:
This will only return results for product custom posts where the title or content contains the word football.
If you look through the WP_Query parameter list (it’s pretty extensive) you’ll find a whole host of string and integer based parameters that you can simply code on a URL to change the search behavior from including and excluding categories, to adding a taxonomy search to restricting the search to certain authors.
[Remember, it is only a search if the s parameter is included in the URL.]
There’s also a couple of extra parameters that you won’t find listed that can dramatically change the behavior and yet, amazingly, are not documented in the Codex.
Search By Phrase
By default, WordPress searches by keyword. What this means is that when passed the search query football boots, WordPress builds the following for the WHERE clause:
1 2 3 4 5
((wp_posts.post_title LIKE '%football%') OR (wp_posts.post_content LIKE '%football%')) AND ((wp_posts.post_title LIKE '%boots%') OR (wp_posts.post_content LIKE '%boots%'))
As you can see, this is not a phrase search but a search looking for football in the title or content and boots in the title or content. So, a post that has boot in the title and football in the content will match which is probably not what the searcher is really looking for.
You can, however, make WordPress do a phrase search, simply by adding sentence=1 to the URL which changes the WHERE clause to:
((wp_posts.post_title LIKE '%football boots%') OR (wp_posts.post_content LIKE '%football boots%'))
Now the search is for the phrase and the title or content has to actually contain football boots to be found. Try this on your own site by searching and then adding &sentence=1 to the URL and see what difference this makes to the results.
Finding An Exact Match
Related to sentence but even more specific is exact. Adding exact=1 to the URL changes the WHERE clause to:
((wp_posts.post_title LIKE 'football boots') OR (wp_posts.post_content LIKE 'football boots'))
Rather than playing spot the difference, I’ll tell you that the only difference between the sentence WHERE clause and the exact WHERE clause is the removal of the % around the phrase in the LIKE statements. That removal makes a big difference, though, because now the title or content has to match the search query, not just contain it.
So, if no product has the title football boots then there will be no results. Use exact with care.
Customizing The Search Form
The default WordPress search form is very simple:
1 2 3 4 5 6 7
<form role="search" method="get" id="searchform" class="searchform" action="http://www.test.dev/"> <div> <label class="screen-reader-text" for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
If we want to change the search behavior, then, all we need to do is add our own fields to the form.
1 2 3 4 5 6 7 8 9
<form role="search" method="get" id="searchform" action="http://www.test.dev/"> <div> <label for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="hidden" value="1" name="sentence" /> <input type="hidden" value="product" name="post_type" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
This search form, when submitted, will generate the following URL:
1.6 million WordPress Superheroes read and trust our blog. Join them and get daily posts delivered to your inbox - free!
It will still invoke the default search results page but those results will be for product custom post types that contain the search phrase in either their title or their content.
The easiest way to build your own search form, assuming that you want to leave the default form as is, is to create a new page template with the search form coded to your requirements and assign this to a specific page.
If, however, you want to update the default search form – to trigger phrase searching, for example – you have two options assuming that your template hasn’t hard-coded the search form into the template:
- The first is put your custom search form into a template file called searchform.php. Whenever the get_search_form() function is called it will look for, and use, this template first.
- The second is to use the get_search_form filter to replace to force WordPress to use your custom search form.
Both these techniques are described in detail in the WordPress Codex.
When A Custom Form Is Not Enough
Although you can do a great deal with a custom form, there are scenarios when you need to build your WP_Query and handle the results yourself, in particular when you are adding a second search function.
Case Study Background
This case study concerns an e-commerce site for an organisation that sells a mix of physical and digital products. Most of the digital products are back-issues of two magazines, along with booklets both in paper and digital formats.
The organisation wanted to provide a “library” search that would allow visitors to search just the magazines and the booklets for certain phrases (the magazines have a full list of contents in the content). Whilst the site already had a product search, the results were less satisfactory as:
- often had too many irrelevant results
- included all products
- gave no indication of where the search term was matched, search results were just the product image
In order to leave the current searches in tact, a new search function was built that:
- enforced searching by phrase rather than keyword
- restricted the search to just the magazine and booklet categories
- displayed and highlighted the text that contains the search phrase match
The first two requirements could actually be achieved with the following custom search form:
1 2 3 4 5 6 7 8 9 10
<form role="search" method="get" id="searchform" action="http://www.test.dev/"> <div> <label for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="hidden" value="1" name="sentence" /> <input type="hidden" value="product" name="post_type" /> <input type="hidden" value="product_cat" name="magazines,books" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
However, this wouldn’t help with the search listing layout and the highlighting of the search phrase, so a new page template was built and assigned to a new dedicated page.
Here’s the main logic in the template:
As you can see the form is pretty much the standard WordPress form as all the manipulation of the WP_Query call is done via the coding where:
- post_type is set to product
- sentence is set to 1 to trigger phrase searching
- a taxonomy parameter is added to restrict the search to the three product categories
- ordering is set to date and menu_order descending
- all posts are to be returned
It’s important to note that there is no paging in this solution. For a custom, specific search it won’t always be necessary for pagination, especially if phrase searching is triggered.
Once the WP_Query is executed it is a matter of outputting any search results. You’ll notice that if there are no search results then a few related products are shown instead to try and keep the visitor on the page.
Outputting the results requires two additional functions, one to pull out the text that contains the matching search phrase and another to highlight the phrase itself.
Highlighting the phrase is achieved just by using a simple regex expression.
Pulling out the text containing the search phrase was not so easy. It was greatly aided by the fact that each product’s content used a standard approach of listing the contents in an unordered list with a header and a description but trying to find a regex to pull out the list item proved beyond me, so I resorted to string manipulation.
The result, though, is pretty good:
Custom Perfect When You Want A Second Search
Custom search forms and functions are perfect for when you want to add a second search function to run alongside the main search function and have really specific requirements.
In a number of scenarios, you might be able to achieve what you want by simply having a custom search form which passes the appropriate parameters to built-in in search function. You can easily test this out before embarking on building your own custom search function by adding the appropriate query variables to the standard search URL and seeing if you get the results you require.
That said, coding your own search function is not that difficult and gives you access to an even greater range of parameters to control the search behavior.
Have you come across a situation where you need a customized search? Did you find a plugin to help you or did you build your own?