Add new comment

Drupal: Add new operators to views filters (such as contained in CSV) or how to override default view's handlers

Difficulty: 
Piece of Cake

Today I'm going to show you how to turbo boost Drupal's default views filters with an example on how to add  a "Conatained in CSV" operator for Numeric and String filters.

Imagine a user wants to perform a batch operation on entities with ID's X, Y and Z. Unless they all appear on the same page, or you set the page size to inifinte this is impossible because Views Bulk Operations won't let you manually pick items between searches and/or pages.

We want something like this to happen:

It is very important:

  • Not to modify core.
  • Not to introduce new handlers, so that currently configured views will have this new features available without having to reconfigure them.

The first thing we need is a handler class that extends from views_handler:

<?php

/**
 * @file
 * Definition of ViewsHandlerFilterNumeric.
 */

namespace Drupal\fdf\Views\Handlers;

/**
 * Simple filter to handle greater than/less than filters
 *
 * @ingroup views_filter_handlers
 */
class ViewsHandlerFilterNumeric extends \views_handler_filter_numeric {
  
  /**
   * {@inheritdoc}
   */
  function operators() {

    $operators = parent::operators();

    $operators += array('containedin' => array(
        'title' => t('Is contained in (csv)'),
        'short' => t('contained in'),
        'method' => 'op_contained',
        'values' => 1,
      ),);

    return $operators;
  }

  /**
   * {@inheritdoc}
   */
  function value_form(&$form, &$form_state) {
    parent::value_form($form, $form_state);
    if (isset($form['value']['value']['#type'])) {
      $form['value']['value']['#maxlength'] = NULL;
    }
    if (isset($form['value']['#type'])) {
      $form['value']['#maxlength'] = NULL;
    }
  }

  /**
   * {@inheritdoc}
   */
  function op_contained($field) {
    //dejamos solo valores numéricos
    $arr = explode(',',$this->value['value']);
    foreach($arr as $key => &$item) {
      $item  = preg_replace('#[^0-9]#', '', $item);
      if(!is_numeric($item)) {
        unset($arr[$key]);
      }
      else {
        $arr[$key] = (int) $arr[$key];
      }
    }
    $this->query->add_where($this->options['group'], $field, $arr);
  }
}

And now override the default handlers by consuming hook_views_data_alter:

/**
 * Implements hook_views_data_alter().
 */
function fdf_views_data_alter(&$data) {
  // Cambiar algunos handlers por otros customizados.
  foreach ($data as &$item) {
    foreach ($item as &$subitem) {
      if (isset($subitem['filter']['handler']) && $subitem['filter']['handler'] == \views_handler_filter_string::class) {
        $subitem['filter']['override handler'] = \Drupal\fdf\Views\Handlers\ViewsHandlerFilterString::class;
      }
      elseif (isset($subitem['filter']['handler']) && $subitem['filter']['handler'] == \views_handler_filter_numeric::class) {
        $subitem['filter']['override handler'] = \Drupal\fdf\Views\Handlers\ViewsHandlerFilterNumeric::class;
      }
    }
  }
}

Piece of cake! For this to work you will of course need the amazing XAutoload module.