Building a custom “search by category” wordpress plugin

GFX9.COM share Building a custom “search by category” wordpress plugin, you can download now.

With an ever increasing amount of content building on your Wordpress site, your users will inevitably need to search your site to find that specific helpful article from way back. To help narrow the search results, I'm going to show you how to code a plugin that allows the user to search based on category.

This tutorial includes a screencast available to Tuts+ Premium members.

The first step in our plugin's development is to decide on the feature set it'll have, plus the amount of customization available. We want our plugin to have the following features and to allow for complete user customization:

  1. Reconfigure search based on selected category
  2. Drop-down list of categories
  3. Ability to hide categories with no posts from list
  4. Ability to exclude child categories from list
  5. Opton to disable our inbuilt styling
  6. Ability to exclude specific categories from list
  7. Ability to use custom value in search box
  8. Ability to use custom value for "In all categories" selection

Creating the Skeleton for the Plugin

Before we can do anything to our plugin we first need to build the skeleton for the plugin to work in. We'll begin by creating a new folder in the Wordpress "plugins" directory (/wp-content/plugins) and title it "search-by-category" (by convention, all plugin folders should be all lowercase and use dashes in place of spaces so that when linking to files in the folder URLs are easily readable to the human eye). Create a new PHP file in the folder titled "sbc.php", this file will serve as the foundation for our entire plugin structure. Next fill, in the plugin with the required Wordpress info so that it will appear in our WP-Admin panel.


Set-up the Options page

Now that Wordpress is recognizing our plugin, we can begin our development. First thing we need to do is setup an Options page so that we can let the user configure our plugin to their liking. How this works is a three-step process:

first we create a class for all of our configurations to operate inside.

if ( ! class_exists( 'SBC_Admin' ) ) { 
	class SBC_Admin { 

Second we run a function to create the config page.

// prep options page insertion 
function add_config_page() { 
	if ( function_exists('add_submenu_page') ) { 
		add_options_page('Search By Category Options', 'Search By Category', 10, basename(__FILE__), array('SBC_Admin','config_page')); 

Setting up the Personalization

Now that we have our config page up, we can start adding in our config options for us to apply later in the plugin. To display our config page we're going to have to create a new function called "config_page()" which we'll fill with the rest of our admin section code. First we'll go ahead and write the HTML for all our sections.


Seach By Category Options

/> * Styling doesn't display correctly in IE7 and earlier *

You'll notice that we're already using PHP to reference the variables: $search_text, $focus, $hide_empty, $exclude_child, $sbc_style, and $raw_excluded_cats (in the function "wp_category_checklist"). We'll create and expand these variables in the next step.

Full Screencast

Adding our Options to the Database

Now that the admin page is up and running we can start using it to add options to the Database. To do this, we're simply just going to create a variable before the "if ( ! class_exists( 'SBC_Admin' ) ) {" and then use the Wordpress function "add_option('entry-title','value')" to insert it into the wp_options table of the DB. Here's the list of the variables we'll be using in our plugin:

  • $focus - The default text the viewer sees in the selection drop-down
  • $hide_empty - true or false, removes categories with 0 posts from the drop-down
  • $excluded_cats - a comma separated array of the excluded categories
  • $raw_excluded_cats - array of the excluded categories
  • $search_text - The default text in the search box of the form
  • $exclude_child - true or false, removes child categories from the drop-down
  • $sbc_style - true or false, use the custom SBC stylesheet
// Some Defaults 
$focus					= 'In All Categories'; 
$hide_empty				= '1'; // 1 means true 
$excluded_cats			= array(); 
$search_text			= 'Search For...'; 
$exclude_child			= '0'; // 0 means false 
$raw_excluded_cats		= array(); 
$sbc_style				= '1'; 
// Put our defaults in the "wp-options" table 
add_option("sbc-focus", $focus); 
add_option("sbc-hide-empty", $hide_empty); 
add_option("sbc-excluded-cats", $excluded_cats); 
add_option("sbc-search-text", $search_text); 
add_option("sbc-selected-excluded", $raw_excluded_cats); 
add_option("sbc-exclude-child", $exclude_child); 
add_option("sbc-style", $sbc_style);

Now that we have our defaults set and added to the database, we can use Wordpress' "get_option('entry-title')" function in our config_page function so that our values are reflected inside the forms.

function config_page(){ 
	$focus					= get_option("sbc-focus"); 
	$hide_empty				= get_option("sbc-hide-empty"); 
	$search_text			= get_option("sbc-search-text"); 
	$excluded_cats			= get_option("sbc-excluded-cats"); 
	$exclude_child			= get_option("sbc-exclude-child"); 
	$raw_excluded_cats 		= get_option("sbc-selected-excluded"); // For Checklist 
	$sbc_style				= get_option("sbc-style");

Now that our Options page is using the data from the database, let's setup the update process. To begin with we're going to start a new if statement checking if our submit button is set. Next we'll compare our wpnonce to verify that the user visited the page before trying to update the options. Then we'll run another if statement to check and make sure the current is allowed to change the settings by running the WP function "current_user_can('manage_options')", if they can't, then we'll kill the script.

if ( isset($_POST['submit']) ) { 
	$nonce = $_REQUEST['_wpnonce']; 
	if (! wp_verify_nonce($nonce, 'sbc-updatesettings') ) die('Security check failed'); 
	if (!current_user_can('manage_options')) die(__('You cannot edit the search-by-category options.')); 

Now we'll update the variables we defined earlier with the input value from the form. Since we're using checkboxes for the excluded categories list, the best way to get the return we want from it is to declare another if statement and check that "post_category" (the id given to the list by the WP function) is set. If it is present, then we're going to take its raw form and place back into the "$raw_excluded_cats" variable from earlier so that the checkboxes will remain checked. We're also going to use those same return values to create a "pretty" comma separated array for later use by adding one extra value to the start of the array (This is a fix for a glitch later on in another function) and then implode the array.

// Get our new option values 
$focus					= $_POST['focus']; 
$hide_empty				= $_POST['hide-empty']; 
$search_text			= $_POST['search-text']; 
$exclude_child			= $_POST['exclude-child']; 
$sbc_style				= $_POST['sbc-style']; 
	$raw_excluded_cats 		= $_POST['post_category']; 
	// Fix our excluded category return values 
	$fix					= $raw_excluded_cats; 
	array_unshift($fix, "1"); 
	$excluded_cats			= implode(',',$fix); 

Because of the very nature of checkboxes, they only have a return value if they're checked, so we need to write a catch for when they're not checked. To do this we're going to use a simple if statement that checks to see if our variables are empty (since there's no return value from the form to fill it with), if it is empty, we're going set the value to "0" (false).

// Make sure "$hide_empty" & "$exclude_child" are set right 
if (empty($hide_empty)) $hide_empty = '0'; // 0 means false 
if (empty($exclude_child)) $exclude_child = '0'; // 0 means false 
if (empty($sbc_style)) $sbc_style = '0'; // 0 means false

Now our last step is to update the database with our new values using the Wordpress function "update_option('entry-title','new-value')". We're also going to wrap the values we're inserting into the DB with the function mysql_real_escape_string() to help prevent any SQL injection.

// Update the DB with the new option values 
update_option("sbc-focus", mysql_real_escape_string($focus)); 
update_option("sbc-hide-empty", mysql_real_escape_string($hide_empty)); 
update_option("sbc-selected-excluded", mysql_real_escape_string($raw_excluded_cats)); 
update_option("sbc-excluded-cats", mysql_real_escape_string($excluded_cats)); 
update_option("sbc-search-text", mysql_real_escape_string($search_text)); 
update_option("sbc-exclude-child", mysql_real_escape_string($exclude_child)); 
update_option("sbc-style", mysql_real_escape_string($sbc_style));

Our code So far:


Seach By Category Options

/> * Styling doesn't display correctly in IE7 and earlier *

Creating the Form

Now that we've got our back end all set and ready to deploy, it's time to start writing the front-end form that all the visitors will see and use. So that our user can place our form anywhere on his site that he wants, we're going to encase our form within a new function titled "sbc()" placed just outside of our class declaration. The first bytes of code we need to add are declaring a global $wp_query and $post so we can have access to those functions if we desire later on, but for our purposes we won't need them. Next step is to re-obtain our variables values from the database.

// Base function 
function sbc() { 
	$focus					= get_option("sbc-focus"); 
	$hide_empty				= get_option("sbc-hide-empty"); 
	$excluded_cats			= get_option("sbc-excluded-cats"); 
	$search_text			= get_option("sbc-search-text"); 
	$exclude_child			= get_option("sbc-exclude-child"); 

Once we've done that, we can create another variable called "$list" with its value being the WP function wp_dropdown_categories($settings) (read more on this function here). That variable "$settings" is where most of our customization is being applied.

$settings = array('show_option_all' => $focus, 
					'show_option_none' => '', 
					'orderby' => 'name', 
					'order' => 'ASC', 
					'show_last_update' => 0, 
					'show_count' => 0, 
					'hide_empty' => $hide_empty, 
					'child_of' => 0, 
					'exclude' => "'".$excluded_cats."'", 
					'echo' => 0, 
					'selected' => 0, 
					'hierarchical' => 1, 
					'name' => 'cat', 
					'class' => 'postform', 
					'depth' => $exclude_child); 
$list = wp_dropdown_categories($settings);

Now that the form's dropdown is configured, we can create another variable, "$form" which will hold our form HTML via a hypertext preprocessor. then we'll echo our new $form variable.

$blog_url = get_bloginfo("url"); 
$form = <<< EOH 
EOH; echo $form;

Adding in the Custom Styling

Earlier we gave the user an option of using our custom styling for the form. If they left this option enabled in the settings menu, we need to add our stylesheet to the header. Easiest way to do this is to create a new if statement checking for our variable "$sbc_style" to be true (in our code: 1). Inside that check, we'll add in a new function "style_insert()" that echoes out the URL to our stylesheet. Also in the if (but outside the function), we'll write in a new action for "wp_head" to add in our function "style_insert()".

if($sbc_style == '1'){ 
	// Add our styling 
	function style_insert() { 
		$current_path = get_option('siteurl') . '/wp-content/plugins/' . basename(dirname(__FILE__)); 

As for our styling, create a new file called sbc-style.css fill it with:

form#sbc-search {clear: both;} 
form#sbc-search * {margin: 0px;} 
form#sbc-search input#s {background: #f5f5f5; 
						border: 1px solid #bbbbbb; 
						padding: 4px 10px; 
						width: 80%; 
						margin-bottom: 10px;} 
form#sbc-search select#cat {display: block; 
							background: #fbfbfb; 
							height: 30px; 
							width: 63%; 
							border: 1px solid #bbbbbb; 
							float: left; 
							padding: 4px 20px; 
							border-right: none; 
							-khtml-appearance: none; /* fix default safai styling */ 
							border-radius: 15px 0px 0px 15px; 
							-webkit-border-bottom-left-radius: 15px; 
							-webkit-border-top-left-radius: 15px; 
							-moz-border-radius: 15px 0px 0px 15px;} 
form#sbc-search select#cat option {padding: 2px 4px;} 
form#sbc-search input#sbc-submit {display: block; 
									height: 30px; 
									width: 37%; 
									border: 1px solid #bbbbbb; f 
									loat: right; 
									border-radius: 0px 15px 15px 0px; 
									-webkit-border-bottom-right-radius: 15px; 
									-webkit-border-top-right-radius: 15px; 
									-moz-border-radius: 0px 15px 15px 0px;}

In Safari you'll notice that the standard downward triangle is missing from our dropdown box, this is because we used -khtml-appearance: none on the dropdown to stop safari from forcing it's "snow" theme onto it. a way to fix this is to use an HTML character to simulate the triangle, and there happens to be one very similar called "∇ Nabla". We'll use a combination of spaces, non-breaking-spaces, and this nabla in the dropdown settings to fix this issue.

$settings = array('show_option_all' => $focus.'       ∇', 
						'show_option_none' => '', 
						'orderby' => 'name', 
						'order' => 'ASC', 
						'show_last_update' => 0, 
						'show_count' => 0, 
						'hide_empty' => $hide_empty, 
						'child_of' => 0, 
						'exclude' => "'".$excluded_cats."'", 
						'echo' => 0, 
						'selected' => 0, 
						'hierarchical' => 1, 
						'name' => 'cat', 
						'class' => 'postform', 
						'depth' => $exclude_child);

Returning the Search Results

Once we got our form up and going, we can finally start getting the search results we're looking to give our visitors. To start us off, we'll create a new function called return_only_selected_category(), in which we'll create a new if statement checking that our search submit button is set. Inside that we need to create a new global $wp_query. Next we'll take the selected category return and place it into a variable called $desired_cat. In case the user selected the option for all categories, we need to run a check for the value "*" and reset it to "".

// Get results only from selected category 
function return_only_selected_category() { 
	if (isset($_POST['sbc-submit'])){ 
		global $wp_query; 
		$desired_cat = $_POST['cat']; 
		if ($desired_cat == '*') $desired_cat = ''; 

Now we need to create another variable, $excluded, whose value is the WP function get_categories(). We're going to be adding 2 arguments to this function. First is "hide_empty=false" so that all categories are brought into the list, and second is "exclude={$desired_cat}" so that any categories that the category the user wants to view posts from is removed from the list.

$excluded = get_categories('hide_empty=false&exclude={$desired_cat}');

Once that variable is set, we can finally make it so that only posts from the selected category will appear in the results. For this effect, we're going to alter the Query Vars so that Wordpress removes posts from every category we list (and it just so happens we have a variable full of categories to exclude).

$wp_query->query_vars['cat'] = '-' . $excluded;

By adding a dash in front of the list of categories, we're telling Wordpress to remove those from the query. A very effective method for us. Now the only thing left to do is add a new WP filter for "pre_get_posts" adding in our new function.

// Highjack the search 
add_filter('pre_get_posts', 'return_only_selected_category');

How to Insert our Form

				...your standard form code here... 

Our Finished Code


Seach By Category Options

/> * Styling doesn't display correctly in IE7 and earlier *

$focus.' ∇', 'show_option_none' => '', 'orderby' => 'name', 'order' => 'ASC', 'show_last_update' => 0, 'show_count' => 0, 'hide_empty' => $hide_empty, 'child_of' => 0, 'exclude' => "'".$excluded_cats."'", 'echo' => 0, 'selected' => 0, 'hierarchical' => 1, 'name' => 'cat', 'class' => 'postform', 'depth' => $exclude_child); $list = wp_dropdown_categories($settings); $blog_url = get_bloginfo("url"); $form = <<< EOH
EOH; echo $form; } // Get results only from selected category function return_only_selected_category() { if (isset($_POST['sbc-submit'])){ global $wp_query; $desired_cat = $_POST['cat']; if ($desired_cat == '*') $desired_cat = ''; $excluded = get_categories('hide_empty=false&exclude={$desired_cat}'); $wp_query->query_vars['cat'] = '-' . $excluded; } } if($sbc_style == '1'){ // Add our styling function style_insert() { $current_path = get_option('siteurl').'/wp-content/plugins/'.basename(dirname(__FILE__)); echo ''; } // insert custom stylesheet add_action('wp_head','style_insert'); } // Highjack the search add_filter('pre_get_posts', 'return_only_selected_category'); // insert into admin panel add_action('admin_menu', array('SBC_Admin','add_config_page')); ?>

submit to reddit

Similar content