- 🇨🇦Canada noah
I had a need for the functionality requested here, so I took a run at making it work via
hook_views_query_alter()
—I went this route rather than trying to create a patch because I think there are some UX and other decisions that would need to be made here that are better left to the maintainers (if they decide to pursue this). In the meantime, the following query adjustment is working for me for a range filter called "price", so I'm putting it here in case it's useful to anyone else:/** * Implements hook_views_query_alter(). * * Revise the price range filter to compare range to range, rather than value * to range. */ function MODULE_views_query_alter(ViewExecutable $view, QueryPluginBase $query) { if (($exposed_input = $view?->getExposedInput()) && (isset($exposed_input['price'])) && (!empty($exposed_input['price'])) && ($filters = $view?->getDisplay()?->getOption('filters')) && (isset($filters['field_price_range']['group_info']['group_items'])) && ($option_data = $filters['field_price_range']['group_info']['group_items'])) { // The Views filter only provides the floor for each filter range; establish // ceilings by loading all the floors, sorting by floor (in case they aren't // in order), then using each floor to establish the ceiling of the previous // range (if one exists). This won't provide a ceiling for the final range, // but fortunately in this case the final range doesn't need a ceiling. uasort($option_data, fn($a, $b) => $a['value'] <=> $b['value']); $options = []; foreach ($option_data as $key => $option) { if (isset($option['value'])) { $options[$key] = ['floor' => $option['value']]; if (isset($previous_key) && ($previous_key)) { $options[$previous_key]['ceiling'] = $option['value']; } $previous_key = $key; } } if (!empty($options)) { // Use the existing range condition to determine the group delta and the // fields, then unset the condition so that it can be replaced with a // range to range comparison. foreach ($query?->where as $group_delta => $condition_group) { $fields = []; foreach ($condition_group['conditions'] as $delta => $condition) { if (isset($condition['field']) && ($condition['field'] instanceof Condition)) { // The condition is an instance of // “Drupal\Core\Database\Query\Condition”, and I can't figure out // the “right” way to access it, but casting to array makes the // elements accessible so that the “to” and “from” field names can // be extracted. $condition_field = (array) $condition['field']; foreach ($condition_field as $field_name => $field_data) { if (strpos($field_name, 'conditions') !== FALSE) { if (!isset($fields['from']) && (isset($field_data[0]['field']))) { $fields['from'] = $field_data[0]['field']; } if (!isset($fields['to']) && (isset($field_data[1]['field']))) { $fields['to'] = $field_data[1]['field']; } } } unset($query->where[$group_delta]['conditions'][$delta]); } } // If the fields are available, add new conditions to the current group. if (count($fields) == 2) { foreach ($exposed_input['price'] as $price_range_index) { if (isset($options[$price_range_index])) { $query_snippets = []; if (isset($options[$price_range_index]['floor'])) { $query_snippets[] = "{$fields['to']} >= {$options[$price_range_index]['floor']}"; } if (isset($options[$price_range_index]['ceiling'])) { $query_snippets[] = "{$fields['from']} < {$options[$price_range_index]['ceiling']}"; } if (!empty($query_snippets)) { $query->addWhereExpression($group_delta, implode(' AND ', $query_snippets)); } } } } } } } }