3 namespace Drupal\views\Plugin\views\filter;
5 use Drupal\Core\Form\FormStateInterface;
8 * Simple filter to handle greater than/less than filters
10 * @ingroup views_filter_handlers
12 * @ViewsFilter("numeric")
14 class NumericFilter extends FilterPluginBase {
16 protected $alwaysMultiple = TRUE;
18 protected function defineOptions() {
19 $options = parent::defineOptions();
23 'min' => ['default' => ''],
24 'max' => ['default' => ''],
25 'value' => ['default' => ''],
29 $options['expose']['contains']['placeholder'] = ['default' => ''];
30 $options['expose']['contains']['min_placeholder'] = ['default' => ''];
31 $options['expose']['contains']['max_placeholder'] = ['default' => ''];
39 public function defaultExposeOptions() {
40 parent::defaultExposeOptions();
41 $this->options['expose']['min_placeholder'] = NULL;
42 $this->options['expose']['max_placeholder'] = NULL;
43 $this->options['expose']['placeholder'] = NULL;
49 public function buildExposeForm(&$form, FormStateInterface $form_state) {
50 parent::buildExposeForm($form, $form_state);
52 $form['expose']['min_placeholder'] = [
53 '#type' => 'textfield',
54 '#default_value' => $this->options['expose']['min_placeholder'],
55 '#title' => $this->t('Min placeholder'),
57 '#description' => $this->t('Hint text that appears inside the Min field when empty.'),
59 $form['expose']['max_placeholder'] = [
60 '#type' => 'textfield',
61 '#default_value' => $this->options['expose']['max_placeholder'],
62 '#title' => $this->t('Max placeholder'),
64 '#description' => $this->t('Hint text that appears inside the Max field when empty.'),
66 // Setup #states for all operators with two value.
67 $states = [[':input[name="options[expose][use_operator]"]' => ['checked' => TRUE]]];
68 foreach ($this->operatorValues(2) as $operator) {
70 ':input[name="options[operator]"]' => ['value' => $operator],
73 $form['expose']['min_placeholder']['#states']['visible'] = $states;
74 $form['expose']['max_placeholder']['#states']['visible'] = $states;
76 $form['expose']['placeholder'] = [
77 '#type' => 'textfield',
78 '#default_value' => $this->options['expose']['placeholder'],
79 '#title' => $this->t('Placeholder'),
81 '#description' => $this->t('Hint text that appears inside the field when empty.'),
83 // Setup #states for all operators with one value.
84 $form['expose']['placeholder']['#states']['visible'] = [[':input[name="options[expose][use_operator]"]' => ['checked' => TRUE]]];
85 foreach ($this->operatorValues(1) as $operator) {
86 $form['expose']['placeholder']['#states']['visible'][] = [
87 ':input[name="options[operator]"]' => ['value' => $operator],
92 public function operators() {
95 'title' => $this->t('Is less than'),
96 'method' => 'opSimple',
97 'short' => $this->t('<'),
101 'title' => $this->t('Is less than or equal to'),
102 'method' => 'opSimple',
103 'short' => $this->t('<='),
107 'title' => $this->t('Is equal to'),
108 'method' => 'opSimple',
109 'short' => $this->t('='),
113 'title' => $this->t('Is not equal to'),
114 'method' => 'opSimple',
115 'short' => $this->t('!='),
119 'title' => $this->t('Is greater than or equal to'),
120 'method' => 'opSimple',
121 'short' => $this->t('>='),
125 'title' => $this->t('Is greater than'),
126 'method' => 'opSimple',
127 'short' => $this->t('>'),
131 'title' => $this->t('Is between'),
132 'method' => 'opBetween',
133 'short' => $this->t('between'),
137 'title' => $this->t('Is not between'),
138 'method' => 'opBetween',
139 'short' => $this->t('not between'),
142 'regular_expression' => [
143 'title' => $this->t('Regular expression'),
144 'short' => $this->t('regex'),
145 'method' => 'opRegex',
150 // if the definition allows for the empty operator, add it.
151 if (!empty($this->definition['allow empty'])) {
154 'title' => $this->t('Is empty (NULL)'),
155 'method' => 'opEmpty',
156 'short' => $this->t('empty'),
160 'title' => $this->t('Is not empty (NOT NULL)'),
161 'method' => 'opEmpty',
162 'short' => $this->t('not empty'),
172 * Provide a list of all the numeric operators
174 public function operatorOptions($which = 'title') {
176 foreach ($this->operators() as $id => $info) {
177 $options[$id] = $info[$which];
183 protected function operatorValues($values = 1) {
185 foreach ($this->operators() as $id => $info) {
186 if ($info['values'] == $values) {
195 * Provide a simple textfield for equality
197 protected function valueForm(&$form, FormStateInterface $form_state) {
198 $form['value']['#tree'] = TRUE;
200 // We have to make some choices when creating this as an exposed
201 // filter form. For example, if the operator is locked and thus
202 // not rendered, we can't render dependencies; instead we only
203 // render the form items we need.
205 if (!empty($form['operator'])) {
206 $source = ':input[name="options[operator]"]';
209 if ($exposed = $form_state->get('exposed')) {
210 $identifier = $this->options['expose']['identifier'];
212 if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
213 // exposed and locked.
214 $which = in_array($this->operator, $this->operatorValues(2)) ? 'minmax' : 'value';
217 $source = ':input[name="' . $this->options['expose']['operator_id'] . '"]';
221 $user_input = $form_state->getUserInput();
222 if ($which == 'all') {
223 $form['value']['value'] = [
224 '#type' => 'textfield',
225 '#title' => !$exposed ? $this->t('Value') : '',
227 '#default_value' => $this->value['value'],
229 if (!empty($this->options['expose']['placeholder'])) {
230 $form['value']['value']['#attributes']['placeholder'] = $this->options['expose']['placeholder'];
232 // Setup #states for all operators with one value.
233 foreach ($this->operatorValues(1) as $operator) {
234 $form['value']['value']['#states']['visible'][] = [
235 $source => ['value' => $operator],
238 if ($exposed && !isset($user_input[$identifier]['value'])) {
239 $user_input[$identifier]['value'] = $this->value['value'];
240 $form_state->setUserInput($user_input);
243 elseif ($which == 'value') {
244 // When exposed we drop the value-value and just do value if
245 // the operator is locked.
247 '#type' => 'textfield',
248 '#title' => !$exposed ? $this->t('Value') : '',
250 '#default_value' => $this->value['value'],
252 if (!empty($this->options['expose']['placeholder'])) {
253 $form['value']['#attributes']['placeholder'] = $this->options['expose']['placeholder'];
255 if ($exposed && !isset($user_input[$identifier])) {
256 $user_input[$identifier] = $this->value['value'];
257 $form_state->setUserInput($user_input);
261 if ($which == 'all' || $which == 'minmax') {
262 $form['value']['min'] = [
263 '#type' => 'textfield',
264 '#title' => !$exposed ? $this->t('Min') : $this->exposedInfo()['label'],
266 '#default_value' => $this->value['min'],
267 '#description' => !$exposed ? '' : $this->exposedInfo()['description'],
269 if (!empty($this->options['expose']['min_placeholder'])) {
270 $form['value']['min']['#attributes']['placeholder'] = $this->options['expose']['min_placeholder'];
272 $form['value']['max'] = [
273 '#type' => 'textfield',
274 '#title' => !$exposed ? $this->t('And max') : $this->t('And'),
276 '#default_value' => $this->value['max'],
278 if (!empty($this->options['expose']['max_placeholder'])) {
279 $form['value']['max']['#attributes']['placeholder'] = $this->options['expose']['max_placeholder'];
281 if ($which == 'all') {
283 // Setup #states for all operators with two values.
284 foreach ($this->operatorValues(2) as $operator) {
285 $states['#states']['visible'][] = [
286 $source => ['value' => $operator],
289 $form['value']['min'] += $states;
290 $form['value']['max'] += $states;
292 if ($exposed && !isset($user_input[$identifier]['min'])) {
293 $user_input[$identifier]['min'] = $this->value['min'];
295 if ($exposed && !isset($user_input[$identifier]['max'])) {
296 $user_input[$identifier]['max'] = $this->value['max'];
299 if (!isset($form['value'])) {
300 // Ensure there is something in the 'value'.
309 public function query() {
310 $this->ensureMyTable();
311 $field = "$this->tableAlias.$this->realField";
313 $info = $this->operators();
314 if (!empty($info[$this->operator]['method'])) {
315 $this->{$info[$this->operator]['method']}($field);
319 protected function opBetween($field) {
320 if ($this->operator == 'between') {
321 $this->query->addWhere($this->options['group'], $field, [$this->value['min'], $this->value['max']], 'BETWEEN');
324 $this->query->addWhere($this->options['group'], $field, [$this->value['min'], $this->value['max']], 'NOT BETWEEN');
328 protected function opSimple($field) {
329 $this->query->addWhere($this->options['group'], $field, $this->value['value'], $this->operator);
332 protected function opEmpty($field) {
333 if ($this->operator == 'empty') {
334 $operator = "IS NULL";
337 $operator = "IS NOT NULL";
340 $this->query->addWhere($this->options['group'], $field, NULL, $operator);
344 * Filters by a regular expression.
346 * @param string $field
347 * The expression pointing to the queries field, for example "foo.bar".
349 protected function opRegex($field) {
350 $this->query->addWhere($this->options['group'], $field, $this->value['value'], 'REGEXP');
353 public function adminSummary() {
354 if ($this->isAGroup()) {
355 return $this->t('grouped');
357 if (!empty($this->options['exposed'])) {
358 return $this->t('exposed');
361 $options = $this->operatorOptions('short');
362 $output = $options[$this->operator];
363 if (in_array($this->operator, $this->operatorValues(2))) {
364 $output .= ' ' . $this->t('@min and @max', ['@min' => $this->value['min'], '@max' => $this->value['max']]);
366 elseif (in_array($this->operator, $this->operatorValues(1))) {
367 $output .= ' ' . $this->value['value'];
373 * Do some minor translation of the exposed input
375 public function acceptExposedInput($input) {
376 if (empty($this->options['exposed'])) {
380 // rewrite the input value so that it's in the correct format so that
381 // the parent gets the right data.
382 if (!empty($this->options['expose']['identifier'])) {
383 $value = &$input[$this->options['expose']['identifier']];
384 if (!is_array($value)) {
391 $rc = parent::acceptExposedInput($input);
393 if (empty($this->options['expose']['required'])) {
394 // We have to do some of our own checking for non-required filters.
395 $info = $this->operators();
396 if (!empty($info[$this->operator]['values'])) {
397 switch ($info[$this->operator]['values']) {
399 if ($value['value'] === '') {
404 if ($value['min'] === '' && $value['max'] === '') {