How to make a range selector filter in Views
by Chuck Vose, Web Developer
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 <any> 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' => '<Any>'), $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.
Comments
doesn't work
Posted by Yohanes on . [Reply]
Hi,
Thank you for the great post. The Select box appears but whenever I filter by it, the URL doesn't have any min or max query string, thus not filtering against the values I set. Thank you
Very well done
Posted by Mathew on . [Reply]
Works beautifully. I'm packaging this up as a module with full credits to you sir.
need help!
Posted by Leon on . [Reply]
Hi, nice work! I'm trying to implement this but for some reason whenever I invoke
unset($form[$field]['min']);
unset($form[$field]['max']);
I'll get a blank screen! Any idea why?
Hey hi,
Posted by Prerns on . [Reply]
Thanks for the amazing writeup. It solved most of my problems. Well I had one quick question as to where do I add the label for the range?( 'label' => 'Price)
Thanks
Works beautifully... I had
Posted by Prerns on . [Reply]
Works beautifully... I had one questions as to how do I change multiple fields in the same form.
Hello,
Posted by Ayoub on . [Reply]
Hello,
Thanks a lot for the awesome writeup, I followed your instructions it works fine with my range filter however when I activate this hack the other fields of my exposed form filter stops showing, could you please give me a lead or hint on how to fix that ??
Thanks again
It doesn't seem to work
Posted by tamamoshiro on . [Reply]
This function should be great.
I fallowed the instruction but it is not working though..
The dropdown selection appears right after 'apply' button but it doesn't filter anything.
Watchdog reports an error when displaying the exposed filter
Posted by Dennis Tran on . [Reply]
Thanks for the code. I was able to make it work but I'm having some strange watchdog errors reported.
Warning: explode() expects parameter 2 to be string, array given in mymodule_range_validate() (line 62 of /var/www/mywebsite.com/htdocs/sites/all/modules/mymodule/mymodule.module).
Which is this line of code in question:
list($min, $max) = explode(',', $v);
It reports this error anytime my exposed filter is displayed on the site but without a parameter in the url. For example, I'm on my listings page which the url would be /listings and the error hits. But after using the exposed filter, the new url would be /listings?&field_size_value=All which does not generate an error. Any ideas on how to fix this?
Thanks.
How do the same in D7?
Posted by lara on . [Reply]
How do the same in D7?
Drupal 7.x
Posted by Jorge Díaz on . [Reply]
Awesome post!!!
Does this works on Drupal 7.x?
drupal 7.x
Posted by nikitas on . [Reply]
here is my solution with drupal 7.x based on this article !
http://pixelthis.gr/blog/how-make-range-selector-filter-views
p.s: thank you !
Great. You make it looks
Posted by Manda on . [Reply]
Great. You make it looks simple. Save me hours. Thanks.
Add new comment