Thumbnail

Custom views filter for an existing daterange field

Thumbnail

Si Hobbs

|

This article shows how to create a select field filter for an existing views field. This works great for date fields where you want the user to select "Past events" or "Current events" from an exposed filter.

This tutorial assumes you can create a custom module, and you have a "date range" field called field_myevent. We want an exposed filter that allows the user to select past/future/present events, which uses the event date field for the query.

picture of views filter configuration

The module

Create a custom module called myfilter.module (or use your own module of course). You need to implement hook_views_data_alter().

 

<?php

/**
 * Implements hook_views_data_alter().
 */
function myfilter_views_data_alter(array &$data) {

  // 'node__field_myevent' is magic and must match ENTITY__FIELD_NAME.
  $data['node__field_myevent']['consultation_status'] = [
    'title' => t('Event status'),
    'filter' => [
      'title' => t('Event status'),
      'group' => t('Content'),
      'help' => t('Show future/current/past events with a filter.'),
      // This matches the real field.
      'field' => 'field_myevent_value',
      // This matches the plugin machine name.
      'id' => 'myevent_filter',
    ],
  ];

}

 

The plugin

This is the the filter plugin. It lives in your module at src/Plugins/views/filter/MyEventFilter.php.

<?php

namespace Drupal\myfilter\Plugin\views\filter;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;

/**
 * Past/Present/Future event filter.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("myevent_filter")
 */
class MyEventFilter extends FilterPluginBase {

  /**
   * Operators.
   *
   * This may not be needed, as we don't have more than one operator. But it
   * is a pattern seen in other filters, 'opStateIs' would be a method that
   * a parent class calls during the query() method.
   */
  public function operators() {
    $operators = [
      'is' => [
        'title' => $this->t('The event is'),
        'method' => 'opStateIs',
        'short' => $this->t('Is'),
        'values' => 1,
      ],
    ];
  }

  /**
   * The form that is show (including the exposed form).
   */
  protected function valueForm(&$form, FormStateInterface $form_state) {
    $form['value'] = [
      '#tree' => TRUE,
      'state' => [
        '#type' => 'select',
        '#title' => $this->t('Event status'),
        '#options' => [
          'all' => $this->t('All'),
          'current' => $this->t('Current'),
          'past' => $this->t('Past'),
          'future' => $this->t('Future'),
        ],
        '#default_value' => !empty($this->value['state']) ? $this->value['state'] : 'all',
      ]
    ];
  }

  /**
   * Applying query filter. If you turn on views query debugging you should see
   * these clauses applied. If the filter is optional, and nothing is selected, this
   * code will never be called.
   */
  public function query() {
    $this->ensureMyTable();
    $start_field_name = "$this->tableAlias.$this->realField";
    $end_field_name = substr($start_field_name, 0, -6) . '_end_value';

    // Prepare sql clauses for each field.
    $date_start = $this->query->getDateFormat($this->query->getDateField($start_field_name, TRUE), 'Y-m-d H:i:s', FALSE);
    $date_end = $this->query->getDateFormat($this->query->getDateField($end_field_name, TRUE), 'Y-m-d H:i:s', FALSE);
    $date_now = $this->query->getDateFormat('FROM_UNIXTIME(***CURRENT_TIME***)', 'Y-m-d H:i:s', FALSE);

    switch ($this->value['state']) {
      case 'current':
        $this->query->addWhereExpression($this->options['group'], "$date_now BETWEEN $date_start AND $date_end");
        break;

      case 'past':
        $this->query->addWhereExpression($this->options['group'], "$date_now > $date_end");
        break;

      case 'future':
        $this->query->addWhereExpression($this->options['group'], "$date_now < $date_start");
        break;
    }
  }

  /**
   * Admin summary makes it nice for editors.
   */
  public function adminSummary() {

    if ($this->isAGroup()) {
      return $this->t('grouped');
    }
    if (!empty($this->options['exposed'])) {
      return $this->t('exposed') . ', ' . $this->t('default state') . ': ' . $this->value['state'];
    }
    else {
      return $this->t('state') . ': ' . $this->value['state'];
    }
  }

}

 

Schema config

Because we have introduced a config setting called "state", which can store a string, we need to tell Drupal about our data type.

# Lives in the module at ./config/schema/myfilter.views.schema.yml

# 'myevent_filter' should match your plugin name.
views.filter_value.myevent_filter:
  type: mapping
  label: 'Event status'
  mapping:
    state:
      type: string
      label: 'Event status'

 

 

 

 

Add new comment

The content of this field is kept private and will not be shown publicly.

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.

Comments

  • Allowed HTML tags: <em> <strong> <cite> <blockquote cite> <ul type> <ol start type> <li> <dl> <dt> <dd> <p>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
  • Use [gist:#####] where ##### is your gist number to embed the gist
    You may also include a specific file within a multi-file gist with [gist:####:my_file].

Spread the word