Sometimes we want to create a faceted search environment based on views. Unfortunately views exposed filters doesn't provide the options to display facets, or "filters", in the prettiest way.

In order to have user-friendly and attractive facets we will show a technique using Views, Better Exposed Filters and the Chaos Tool Suite. We will also show how an exposed text field can be transformed into checkbox-based facets. See a demo here.

Firstly we enable Views, Views UI, Better Exposed Filters and Chaos tools. We then create a view and expose a taxonomy term (id). Under the exposed filter settings we first like to change the "Filter identifier" to something more readable than "tid", for example "type" or "category".

We uncheck "Force Single" so that multiple filters can be applied at once. We then set "Display exposed filter as" to "checkboxes/radio buttons".

At this point we are ready to start customizing our filters. We do this in a custom module using hook_form_alter. Our goal is to use javascript to automatically submit the form when a checkbox is clicked. To do this we use ctools auto-submit.js script. Complete documentation exists within the file itself.

The default auto-submit.js actually does not support checkboxes but this patch allows for them.

Here is an example, this would exist in mymodule_form_alter(&$form, &$form_state, $form_id):

<?php function faceted_views_form_alter(&$form, &$form_state, $form_id) {     if ($form_id == 'views_exposed_form' && $form['#id'] == 'views-exposed-form-events-page-1') {         ctools_add_js('auto-submit');         $form['type']['#attributes'] = array('class' => 'ctools-auto-submit');         $form['submit']['#attributes'] = array('class' => 'ctools-use-ajax ctools-auto-submit-click');   } } ?>

At this stage we have our exposed filters displayed as checkboxes and they auto-submit when clicked. The only thing left to do is hide the "apply" button. We do this with javascript so that the entire form degrades gracefully if the user's browser does not have javascript enabled. We use the following code:

$(document).ready(function(){ $('.ctools-auto-submit-click').hide(); });

That is if for our initial goal.

Another drawback of views exposed filters is that textfields cannot be rendered as a select list. In this example we use a simple cck textfield. Again we use hook_form_alter in our custom module to provide the required functionality. In addition we will need to use hook_views_query_alter to support multiple selections. Note the comments in the code for explanations.

/* * Implementation of hook_form_alter() */ function faceted_views_form_alter(&$form, &$form_state, $form_id) {    if ($form_id == 'views_exposed_form' && $form['#id'] == 'views-exposed-form-getaway-page-2') {         ctools_add_js('auto-submit');         $form['type']['#attributes'] = array('class' => 'ctools-auto-submit');         $form['topic']['#attributes'] = array('class' => 'ctools-auto-submit');         $form['submit']['#attributes'] = array('class' => 'ctools-use-ajax ctools-auto-submit-click');                 // In this bit we want to convert a views exposed filter from a textbox         // to a select list. better_exposed_filters will then convert the         // select into checkboxes using javascript                 // this works fine on the form end but we also need to modify the query         // so that views accounts for multiple cities – the query expects a single filter                 // build a list of cities of all getaways         $query = "SELECT FROM {node} n                             LEFT JOIN {location_instance} li ON (n.nid = li.nid)                             LEFT JOIN {location} l ON (l.lid = li.lid)                             WHERE n.type = 'getaway'                             GROUP BY";         $result = db_query($query);                 $cities = array();         while ($row = db_fetch_array($result)) {             $cities[] = $row['city'];         }                 // re-do the form as select         $form['city']['#type'] = 'select';         // this is what better_exposed_filters looks for:         $form['city']['#theme'] = 'select_as_checkboxes';         $form['city']['#size'] = count($cities);                 foreach ($cities as $city) {             $form['city']['#options'][strtolower($city)] = ucfirst($city);         }                 $form['city']['#multiple'] = true;         // nothing works unless we change default_value from a string to an array         $form['city']['#default_value'] = array();         $form['city']['#attributes'] = array('class' => 'ctools-auto-submit');             } }

All modification to the form are done. The only problem now is that views will only use the first argument provided. To remedy this we add a views_query_alter hook as follows, this would appear in mymodule_views_query_alter(&$view, &$query):

function faceted_views_views_query_alter(&$view, &$query) {     if ($view->name == 'getaway' && $view->current_display == 'page_2') {         // if there are more than one city selected we need an alter         // the args already exist in the $query array but the where clause         // does not account for more than one.         if (count($_GET['city']) > 1) {             $parts = array();             foreach ($_GET['city'] as $city) {                 $parts[] = 'UPPER('%s')';             }             // edit the where clause to account for multiple cities             $query->where[0]['clauses'][2] = 'UPPER( IN ('. implode(', ', $parts) .')';         }     } }

That is it! Comments and questions are appreciated. We attached the module used on the demo site but remember it needs to be customized to your specific needs.

Here are screenshots of the views settings for the taxonomy terms:

A screenshot of views settings for the exposed text field.