How to Add an Advanced Search to Your WordPress Site

We teamed up with SiteGround
To bring you up to 65% off web hosting, plus free access to the entire SitePoint Premium library (worth $99). Get SiteGround + SitePoint Premium Now

The majority of WordPress search forms set a single querystring parameter named ‘s’:

<form method="get" action="<?php bloginfo('url'); ?>">
<fieldset>
<input type="text" name="s" value="" placeholder="search&hellip;" maxlength="50" required="required" />
<button type="submit">Search</button>
</fieldset>
</form>

There’s nothing wrong with that and I thought it was the only option for many, many years.

However, more advanced queries are possible without the aid of plugins (although plenty are available). Deep within the WordPress core, the application parses several other querystring parameters and uses them to return a more focused set of search results. There is some rudimentary documentation which lists the parameter names:

  • attachment
  • attachment_id
  • author
  • author_name
  • cat
  • category_name
  • comments_popup
  • day
  • error
  • feed
  • hour
  • m
  • minute
  • monthnum
  • name
  • p
  • page_id
  • paged
  • pagename
  • post_parent
  • post_type
  • preview
  • second
  • static
  • subpost
  • subpost_id
  • tag
  • tag_id
  • tb
  • w
  • year

I’m not convinced all these work as expected and some are a little pointless, but they match the parameters you can pass to WP_Query. We can therefore create an advanced search form using HTML with a smidgen of PHP to automate the options.

Refine Search by Category

You can limit results to a category by passing its slug to the category_name parameter, e.g.

http://yoursite.com/?s=search+term&category_name=kittens

Our search form can allow the user to refine their search to specific categories:

<form method="get" action="<?php bloginfo('url'); ?>">
<fieldset>
<input type="text" name="s" value="" placeholder="search&hellip;" maxlength="50" required="required" />
<select name="category_name">
<option value="">all categories</option>
<option value="kittens">cute kittens</option>
<option value="puppies">adorable puppies</option>
</select>
<button type="submit">Search</button>
</fieldset>
</form>

If you’d rather list all categories, add the following code between the <select> and </select> tags:

<?php
// generate list of categories
$categories = get_categories();
foreach ($categories as $category) {
	echo '<option value="', $category->slug, '">', $category->name, "</option>\n";
}
?>

Refine Search by Tag

Search results can be limited to a tag by passing its slug to the tag parameter, e.g.

http://yoursite.com/?s=search+term&tag=cockroach

Your search from could therefore limit results to certain tags, e.g.

<form method="get" action="<?php bloginfo('url'); ?>">
<fieldset>
<input type="text" name="s" value="" placeholder="search&hellip;" maxlength="50" required="required" />
<select name="tag">
<option value="">any tag</option>
<option value="cockroach">cockroaches</option>
<option value="snake">snakes</option>
</select>
<button type="submit">Search</button>
</fieldset>
</form>

Similarly, you can generate a list of all tags for the select field:

<?php
// generate list of tags
$tags = get_tags();
foreach ($tags as $tag) {
	echo '<option value="', $tag->slug, '">', $tag->name, "</option>\n";
}
?>

Advancing Advanced Search

What if you want to refine the search by multiple values? For example, the user could choose two or more tags and resulting pages must have them all set. We cannot achieve this using URL parameters alone but let’s start by defining an HTML search form:

<form method="get" action="<?php bloginfo('url'); ?>">
<fieldset>
<input type="text" name="s" value="" placeholder="search&hellip;" maxlength="50" required="required" />
<p>Refine search to posts containing chosen tags:</p>
<?php
// generate list of tags
$tags = get_tags();
foreach ($tags as $tag) {
	echo 
		'<label>',
		'<input type="checkbox" name="taglist[]" value="',  $tag->slug, '" /> ',
		$tag->name,
		"</label>\n";
}
?>
<button type="submit">Search</button>
</fieldset>
</form>

Note that I’ve used a PHP array parameter named taglist. You can use any name other than those already reserved by WordPress (see the list above).

We can now intercept a search submission in our WordPress theme’s functions.php file. The advanced_search_query function detects whether a search is active then sets the WP_Query tag_slug__and parameter accordingly.

// advanced search functionality
function advanced_search_query($query) {

	if($query->is_search()) {
		
		// tag search
		if (isset($_GET['taglist']) && is_array($_GET['taglist'])) {
			$query->set('tag_slug__and', $_GET['taglist']);
		}
	
		return $query;
	}

}

Finally, we use the pre_get_posts action hook to run our advanced_search_query function before a query is executed:

add_action('pre_get_posts', 'advanced_search_query', 1000);

Adding advanced search facilities to WordPress is remarkably easy yet few developers realize it’s possible … perhaps because documentation and examples are a little sparse. I discovered it by accident so I hope you find this code useful in your next WordPress project.

We teamed up with SiteGround
To bring you up to 65% off web hosting, plus free access to the entire SitePoint Premium library (worth $99). Get SiteGround + SitePoint Premium Now