Exposing custom Fields and Filters to Views using hook_views_data_alter()

Drupal Version
7

INTRODUCTION

This tutorial will show you how to add your own custom Fields and Filters to expose to Views. In this case, we had an Entityform that is being submitted from one of multiple sub-domain sites using the Domain Access module. Entityform does not keep track of the domain that the form was submitted on so we simply added an integer field to the form and hid it using Drupal Field Permissions. Then, when the form is submitted we add the domain's ID value (from where the form was submitted) to the form using hook_entity_insert()

Now that our Entityform contains the Domain ID in our newly created field, we needed to be able to display the domain’s display name instead of the ID in our View, and also to filter the Entityform submissions using some specific criteria. This may me a bit of a niche case but the information on how to achieve this seems to be scattered around. Hopefully this information will help consolidate this use case. Here’s how we did it.

MODULE SETUP

For this example we'll create a small, custom module called "entityform_reg_code" to hold our code. For an example of creating a basic Drupal module see Creating a Basic Drupal 7 Module.

The entityform_reg_code.info file should look something like the below text. The important thing to note are the two files[] entries that are going to tell Drupal about our extra, "include" files that will contain the code for our custom Views handlers.

name = Entityform Registration Code
description = Custom Code for Entityform Registration Forms
core = 7.x

files[]=views/entityform_reg_code_handler_filter_domain_admin.inc
files[]=views/entityform_reg_code_handler_field_domain_admin.inc

Next, we'll add our HOOK_views_api() hook to tell Views where our code resides. In this case it is in a sub-directory of our entityform_reg_code module. Be sure to put the name of your module in the drupal_get_path() function call.

<?php

/**
 * Implements hook_views_api().
 */
function entityform_reg_code_views_api() {
  return array(
   
'api' => 3,
   
'path' => drupal_get_path('module', 'entityform_reg_code') . '/views',
  );
}
?>

Now create the sub-directory to contain your Views code. Make a new directory called 'views' so that it resides within your root module and create a new file inside of this directory called "entityform_reg_code.views.inc".

The directory/file structure will look like this: entityform_reg_code/views/entityform_reg_code.views.inc

The entityform_reg_code.views.inc file will contain your HOOK_views_data_alter() hook that will tell Views about your custom data, field and filter.

In the example below you'll see that all of our array's begin with the the key that will match that name of the table in the database where our field exists.

The 'group' key is simply the name of the group that the field and filter will belong too. In this case we're creating a new group called 'Student Registrations'.

The 'join' key is where we'll be joining our table with the entityform data table. In the example below we're joining the 'field_data_field_domain_id' table with the 'entityform' table where the values from 'entityform_id' (in the entityform table) and the 'entity_id' (in our field table) match.

Where you see 'field_domain_id_value', this is the name of the field in the data base. We're telling Views that the data we're exposing is in the 'field_data_field_domain_id' table in the 'field_domain_id_value' field where the Entityform ID's match.

The 'handler' keys on the 'field' and 'filter' arrays are telling Views where our custom handler code is. This is the code that will perform the logic for our needs.

<?php

/**
 * Implements hook_views_data().
 * field_domain_id_value
 */
function entityform_reg_code_views_data_alter(&$data) {
  
    
$data['field_data_field_domain_id']['table']['group'] = t('Student Registrations');
    
$data['field_data_field_domain_id']['table']['join'] = array(
     
'entityform' => array(
       
'left_field' => 'entityform_id',
       
'field' => 'entity_id',     
      ),
    );
 
   
$data['field_data_field_domain_id']['field_domain_id_value'] = array(
     
'field' => array(
         
'title' => t('Submitter Domain'),
         
'help' => t('This is the domain that the student registration form was submitted on.'),
         
'handler' => 'entityform_reg_code_handler_field_domain_admin',
      ),     
     
'filter' => array(
         
'title' => t('Admin of Domain'),
         
'help' => t('Returns true if the user has access to the domain the registration was submitted on.'),
         
'handler' => 'entityform_reg_code_handler_filter_domain_admin',
      ),
  ); 

?>

Next create two more files inside of your /views directory to hold the handler code. The file names will match your handler function names above with .inc appended to the end. The file names for this example are "entityform_reg_code_handler_field_domain_admin.inc" and "entityform_reg_code_handler_filter_domain_admin.inc".

Our module file structure now looks like:

  • entityform_reg_code
  • entityform_reg_code/entityform_reg_code.module
  • entityform_reg_code/views/entityform_reg_code_handler_field_domain_admin.inc
  • entityform_reg_code/views/entityform_reg_code_handler_filter_domain_admin.inc

For the field handler we're simply going to expend the views_handler_field class. The Views classes can be found in the Views/handlers directory. We just referenced the basic views_handler_field class in the views_handler_field.inc file and extended for our needs. See comments below:

entityform_reg_code_handler_field_domain_admin.inc

<?php

class entityform_reg_code_handler_field_domain_admin extends views_handler_field {

  function
get_value($values, $field = NULL) {
   
$alias = isset($field) ? $this->aliases[$field] : $this->field_alias;
   
    if (isset(
$values->{$alias}) && is_numeric($values->{$alias}) && $values->{$alias}>0){
     
// $values->{$alias} contains the field value which is our domain ID
      // We then load the domain object from the ID value and return the domain's display name
     
$domain = domain_load($values->{$alias});
      return
$domain['sitename']; 
    }else{
      return
'not available';
    }
  }
}
?>

The last step is to create the filter handler file. We extended the VIews core filter handler called 'views_handler_filter_equality' to provide our own query. In our case we added a query to check for two circumstances. One is if the current user is assigned to a domain that matches our domain_id field and the other is if the user created the entityform submission.

The two query conditions are chained using db_or().

entityform_reg_code_handler_filter_domain_admin.inc

<?php
class student_reg_code_handler_filter_domain_admin extends views_handler_filter_equality {
      
  function
query() {
   
$this->ensure_my_table();
   
$field = "$this->table_alias.$this->real_field";   

    global
$user;
   
$domains = domain_get_user_domains($user); // Load the domains the current user is assigned to
   
   
if (empty($this->value)) {                       
     
$this->query->add_where(
       
$this->options['group'],
       
db_or()
          ->
condition($field, $domains, 'IN')
          ->
condition('entityform.uid', $user->uid, '=')
        );
    }   
  }
}
?>

That's all there is to it. With this small amount of code we exposed a custom field to translate our domain id into the domain display name and a custom filter to filter the View to show only entityforms that were submitted on a domain that the current user is assigned to or that the current user submitted. Of course, you can create View arguments and sorters as well using this method.

Custom Drupal Views Handler Field and Filter

Author Information

Written by: Shawn Ostermann

Shawn is a Drupal Specialists with over 12 years of experiencing building and developing Drupal websites including custom module development and e-commerce websites.