Drupal

How to make a range selector filter in Views

Messing with your exposed filters for fun and profit.


Filed under:

Messing with your exposed filters for fun and profit.

Ah, to get the request from a client that just cannot be done via stock Views; they are rare but so choice indeed. This episode of Hacking Views will feature our latest request, making a select box with a range of values where all you have is a CCK number/float.

Impossible you say? Unlikely to happen you quip? Nay! It happens all the time. In fact, considering the options given to us when dealing with number CCK fields it's any wonder that Views doesn't already have an option for this case. Aside from a fancy javascript double-ended slider what is a person to do? Min/max are just ugly! Gaze farther for my solution.

The field in question: field_price
The options requested: 'Less than $1,000', '$1,000–$1,999', '$2,000–$3,999', '$4,000–$6,999', '$7,000 plus'.

Starting from the front I felt like this would be in the domain of a custom module. Since I basically do a custom module for Views on every project, I already had a views_tweaks module but lets quickly go through the process just in case you don't habitually create mini-modules.

First, we create a folder at drupal/sites/all/modules/custom/views_tweaks. I like to put it in a custom folder to make it blindingly obvious that it's my problem when it breaks. In addition, Drush is smart enough to not mess with this folder when you do a drush upc.

Inside the views_tweaks folder we need two files: the info file to tell Drupal what's up, and the actual module file to do the heavy lifting. The info file should be called views_tweaks.info and should look something like this:


name = Views Tweaks
description = Misc tweaks to Views
core = 6.x

dependencies[] = views

After that we need to put together the module file, this will contain all the actual logic. The next three large bits of code will all go in views_tweaks.module one after another.



/**
* Implementation of hook_form_alter().
*/
function views_tweaks_form_alter(&$form, $form_state, $form_id) {
// Load up a $view object like we would expect to work with
if (isset($form['#parameters'][1]['view'])) {
$view = $form['#parameters'][1]['view'];
}
switch ($form_id) {
case 'views_exposed_form':
if ($view->name == 'products_wall') {
views_tweaks_range_to_select('field_price_value', array(
'0,999' => 'Less than $1,000',
'1000,1999' => '$1,000–$1,999',
'2000,3999' => '$2,000–$3,999',
'4000,6999' => '$4,000–$6,999',
'7000,999999' => '$7,000 plus',
), $form, $form_state);
}
break;
}
}

This is the meat of the whole thing, generally this is all you're going to have to modify unless you're trying to build your own type of filter and using this as a sample.

All you need to do here is specify the machine name of the field you're acting on, and an array of options. To find your field name you can do so by doing `dpm($form)` on the first line inside the function and searching for things starting with field_.

When you expose a CCK Number field filter in Views you get a min/max textfield. Which is logical of course, but looks terrible. Basically all we're going to do is specify what will go in those two fields, and then some text to accompany it. So if you want one option, who's min is 0 and max is 10k, your options would look like this:


array('0,10000' => 'Maybe a few, maybe LOTS of widgets!',)

That's pretty much it if all you're looking for is implementation. Paste the other two functions below into your views_tweaks.module and enable the Views Tweaks module in the module admin.

If you want to learn more, continue reading.


/**
* Turn a range field into a select dropdown. This assumes that the $options array
* is going to be something like: array('5,9' => '5 to 9 lbs') where the index is
* a comma delimited string of the min/max values.
* Pass in $optional = TRUE if you want there to be an value at the top of
* the select dropdown. Defaults to TRUE.
*/
function views_tweaks_range_to_select($field, $options, &$form, &$form_state, $optional = TRUE) {
$form[$field]['#type'] = 'select';
if ($optional) {
$options = array_merge(array('All' => ' '), $options);
}
$form[$field]['#options'] = $options;
unset($form[$field]['min']);
unset($form[$field]['max']);
$f = $form_state['input'][$field];
$f ? $form[$field]['#value'] = $f : true;
$form[$field]['#element_validate'] = array('views_tweaks_range_validate');
}

The comment says a lot here but I'll try to summarize the functionality as well. If you're reading this I'm going to assume you've done hook_form_alter() before, this is largely the same syntax and uses FAPI like normal. What's we're doing here is destroying the two min/max fields and adding in the options the user specified above.

The real magic comes from #element_validate which will run a function before submission, we'll use this to change the form structure back to what Views expects before it gets to Views.


/**
* Turn values created by range_to_select back into ranges so that Views can process
* the request. This assumes that if the value passed in is 'All' the min/max array
* should be set to array('min' => '', 'max' => '')
*/
function views_tweaks_range_validate($element, &$form_state) {
if (($v = $element['#post'][$element['#name']])) {
if ($v == 'All') {
$min = $max = '';
}
else {
list($min, $max) = explode(',', $v);
}
$form_state['input'][$element['#name']] = array(
'min' => $min,
'max' => $max,
);
$form_state['values'][$element['#name']] = array(
'min' => $min,
'max' => $max,
);
}
}

This last function turns the dropdown back into two range fields in the form_state variable. For some reason Views uses $form_state['values'] instead of $form_state['input'] as I would normally expect. In addition, it has to respect the 'All' value in case selecting a value is optional.

So that's pretty much it. I hope that you learned something, I enjoyed writing it up.

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.