Hello,

I created a custom module to allow a dropdown of choices for an Exposed Is-Between Filter for 'price'.

Operator: Is between
Filter identifier: field_price_value
Optional: checked
Min: <= using module to show dropdown of values
Max: <= using module to show dropdown of values
Exposed: checked

View Display: Page
AJAX: Yes and No results in same issue
Exposed form in block: Yes

The module works just fine and changes are reflected in the filter on the exposed form block. However, on the exposed filter block I would like it to work so that users may select only a Min or only a Max. At the moment I am forced to select both a Min and a Max value, or else no results show. When both values are selected the results work fine.

Is this as per design or with 'Optional' checked should I be able to get results from selecting only Min or only Max value?

Please let me know if more information is required.
Thanks very much in advance!

/**
* Alter the form to display dropdowns for price range
*/
function price_range_dropdown_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'views_exposed_form' :
      $price_from_options = array(
        '' => t('No Min'),
        '1000' => t('1000'),
        '1500' => t('1500'),
        '2000' => t('2000'),
        '2500' => t('2500'),
        '3000' => t('3000'),
        '3500' => t('3500'),
      );
      $price_to_options = array(
        '' => t('No Max'),
        '1500' => t('1500'),
        '2000' => t('2000'),
        '2500' => t('2500'),
        '3000' => t('3000'),
        '3500' => t('3500'),
        '4000' => t('4000'),
      );
      $field_price_value_from = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_from_options,
        '#default_value' => 'No Min',
      );      
      $field_price_value_to = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_to_options,
        '#default_value' => 'No Max',
      );    
      $form['field_price_value']['min'] = $field_price_value_from;
      $form['field_price_value']['max'] = $field_price_value_to;
      break;
  }
}
Members fund testing for the Drupal project. Drupal Association Learn more

Comments

dagmar’s picture

This task cannot be solved using only a hook_form_alter(). When you use a numeric filter in a view, views::handler_filter_numeric perform different querys depending on the operator selected.

The function that do this query is called op_between:

  function op_between($field) {
    if ($this->operator == 'between') {
      $this->query->add_where($this->options['group'], "$field >= %d", $this->value['min']);
      $this->query->add_where($this->options['group'], "$field <= %d", $this->value['max']);
    }
    else {
      $this->query->add_where($this->options['group'], "$field <= %d OR $field >= %d", $this->value['min'], $this->value['max']);
    }
  }

As you can see, when 'between' is setted, two WHERE statements are added to the query. So, if 'max' is set to "No Max", '' will be converted to 0 and finally you get something like: Price between 'my_min' and 0.

There is two possible approaches to solve this issue.

First, you can set No Max to a very high value:

      $price_to_options = array(
        10000000 => t('No Max'),

Ok, this is very ugly, but should work. You can also get the maximum value using a $max = db_result(db_query("SELECT MAX(your_field_price) FROM {content_type_XYZ}")); to be sure that max value is the highest.

A better approach is write your own filter handler. You have to create a new class overriding some functions:


class views_handler_my_between_filter extends views_handler_filter_numeric { 

  function op_between($field) {
    if ($this->operator == 'between') {
      // only apply this filter if a numeric value was setted
      if (!empty($this->value['min'])) {
        $this->query->add_where($this->options['group'], "$field >= %d", $this->value['min']);
      }
      // only apply this filter if a numeric value was setted
      if (!empty($this->value['max'])) {
        $this->query->add_where($this->options['group'], "$field <= %d", $this->value['max']);
      }
    }
    else {
      $this->query->add_where($this->options['group'], "$field <= %d OR $field >= %d", $this->value['min'], $this->value['max']);
    }
  }

  //TODO: you can alter the textfield for max and min values overriding value_form()
}

The second approach requires a custom module with a mymodule.views.inc file to register the new handler. And if you don't know very much how views iternally works, probably the best option is define a very high value for max and 0 as min value.

Bilmar’s picture

hi dagmar, thanks for the awesome response!

Your second approach is something I really hope to be able to challenge myself to in the next several months as I learn more about programming, Drupal and Views. I tried the first approach you mentioned by setting the 'No Max' value to a very high number. I got a "An illegal choice has been detected. Please contact the site administrator." error that breaks the exposed filter and highlights the maximum price box in red. I tried changing this value to the max value that users can choose from but still the same error. I believe the custom module is ok bc I have similar dropdowns for other filters without any problems, but just having problems with this Is Between filter for price.

I also tried typing 10000 without the ' ' as you had above but same error.
$price_to_options = array(
'10000' => t('No Max'),
);

I have attached two screengrabs for your reference.
I really appreciate your help. Thanks!

function price_range_dropdown_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'views_exposed_form' :
      $price_from_options = array(
        '' => t('No Min'),
        '1000' => t('1000'),
        '1500' => t('1500'),
        '2000' => t('2000'),
        '2500' => t('2500'),
        '3000' => t('3000'),
        '3500' => t('3500'),
      );
      $price_to_options = array(
        '10000' => t('No Max'),
      );
      $field_price_value_from = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_from_options,
        '#default_value' => 'No Min',
      );      
      $field_price_value_to = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_to_options,
        '#default_value' => '10000',
      );    
      $form['field_price_value']['min'] = $field_price_value_from;
      $form['field_price_value']['max'] = $field_price_value_to;
      break;
  }
}
dagmar’s picture

Make sure that you are not using an old parameter ir your url:

I.e: www.example.com/your_view?price[max]='No Max'&price[min]='No Min' delete all the string after ? and see if this problem is still present when you apply again the filter.

Bilmar’s picture

When I go to the search page and the page loads at www.example.com/search I get the error mentioned above. On the page I click 'Apply' on the broken exposed block filter and the url changes to "http://www.example.com/search?field_price_value[min]=&field_price_value[..." and now the exposed block filter 'price' no longer has an error. When AJAX is set to YES, the url doesn't change and the error remains.

Does this mean that I must use AJAX: NO and set the search page url to the long one above?
I would prefer www.example.com/search as the url for the start and have all the info that follows come once the filter is used.

Thanks

Bilmar’s picture

I'm continuing to try to get this filter to work =)

Found out that adding the line '' => t('') below fixed the error (so no error on www.example.com/search).
The filters start by showing 'No Min' and 'No Max' as I want but now I have a blank choice in the dropdown list because of adding '' => t('')

      $price_to_options = array(
        '10000' => t('No Max'),
        '' => t(''),
      );
      $field_price_value_from = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_from_options,
        '#default_value' => 'No Min',
      );      
      $field_price_value_to = array (
        '#type' => 'select',
        '#multiple' => false,
        '#required' => false,
        '#options' => $price_to_options,
        '#default_value' => '10000',
      ); 
Bilmar’s picture

The closest I am getting to making this exposed Is-Between filter to work is by including a blank choice in the dropdown list of the maximum value with '' => t('') [shown above]. Is a '0' value required in the Maximum value of the Is-Between filter? It just seems weird that a zero is required for the maximum value droplist to work properly (from what it seems like from my testing).

Gonna keep trying. I really want this Is-Between filter working for price =)

Bilmar’s picture

Status: Active » Fixed

hey dagmar, I figured it out!

In the views filter setting for 'price' I had to make Min: -blank- and And max: 10000 (to match what is in the custom module) which fixed all the errors and allows me to not have to manually enter in a blank value for Max into the custom module with line '' => t(''). My guess is that the -blank- in the And max: in the filter setting required a -blank- to be entered in the custom module. I first thought I didn't have to touch the views filter setting as I was adding the values into the custom module but there's a new thing to learn everyday =)

thanks for your help on this!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

jackocnr’s picture

Version: 6.x-3.x-dev » 7.x-3.3
Category: support » feature
Status: Closed (fixed) » Active

I've just come across this same problem in Views 7.x-3.3. In my eCommerce website, I want users to be able to filter products between a min and max price, but if they dont select a max price it breaks (shows no products at all)! I don't understand why if the user only enters a min value, and leaves the max value blank, the default behaviour would be to set the max to 0 - when would this ever be useful? I propose we alter the default behaviour to only include the max part of the db query if the user has entered a value. Or if anyone can think of a use case where this wouldnt work, then at least provide an option in the exposed filter settings.

jackocnr’s picture

Status: Active » Needs review
FileSize
1.23 KB
None View

Here is a patch that fixes the problem for me by changing the default behaviour s.t. we only set the max part of the query if the user has entered a value for it. I would be very interested if anyone has any feedback.

EDIT: oops, the patch includes git diff color information, which makes it all weird! I will add another one!

jackocnr’s picture

yultyyev’s picture

I think, same operation should be applied for blank MIN value of BETWEEN-filters, because epmty MIN-value shouldn't be equal to 0, but "-inf" (for fields with values less than zero)
...
elseif ($this->value['min'] !== "") {
$this->query->add_where($this->options['group'], $field, $this->value['max'], '<=');
}
...

What do you think, this patch will be commited in core? There are some other issues with same problem.

korolt’s picture

Thanks