Skip to main content
pseudo field in view

Drupal 7 - Exposing pseudo field in view from custom table

Introduction:

At KnackForge we often see requirement to expose custom fields to views for better integration with Drupal flow. In this article we are going to see steps to expose a custom field from a custom table. Infact its a virtual field. 

Use case:

Assume you have two custom fields total_marks and marks_obtained in a custom table. Now you need to calculate the percentage of mark scored. You don't need a field in table for this. You can create a virtual field bound to marks_obtained field.

Hooks to be used:

hook_views_data_alter() or hook_views_data()

hook_views_api()

hook_views_handlers()

I assume the custom table has already been created using .install file.

Implementation:

In your custom module write,

/**
 * Implements hook_views_api().
 */
function MY_MODULE_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'MY_MODULE') . '/views/',
  );
}
 
this tells Drupal that we have some views related custimizations. Create a views folder under your module, with a file called, MY_MODULE_.views.inc.
 
/**
 * Implementation of hook_views_data_alter().
 */
function MY_MODULE_views_data_alter(&$data) {
  $data['CUSTOM_TABLE_NAME']['percentage'] = array(
      'title' => t('Percentage obtained'),
      'help' => t('Shows percentage obtained'),
      'real field' => 'marks_obtained',
      'field' => array(
        'handler' => 'MY_MODULE_views_handler_field_percentage',
        'click sortable' => TRUE,
      ),
      'argument' => array(
        'handler' => 'views_handler_argument_numeric',
        'name_field' => 'title',
        'string' => TRUE
      ),
      'filter' => array(
        'handler' => 'views_handler_filter_numeric',
        'help' => t('Filter results to a particular result set'),
      ),
      'sort' => array('handler' => 'views_handler_sort'),
  ); 
}
 
The above defines our virtual field bound to marks_obtained field for use in views as a field.
 
/**
* Implements hook_views_handlers()
*/
function MY_MODULE_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'MY_MODULE') . '/views/handlers',
    ),
    'handlers' => array(
      'MY_MODULE_views_handler_field_percentage' => array(
        'parent' => 'views_handler_field',
      )
  );
}
 
Place a "handlers" folder inside MY_MODULE and write a file called MY_MODULE_views_handler_field_percentage.inc.
 
Inside that file write,
 
 * @file
 * Handles filtering by networks
 */
class MY_MODULE_views_handler_field_percentage extends views_handler_field {
 function render($values) {
    $total_marks = $this->options['total_marks'];
    $marks_obtained = $this->options['marks_obtained'];
    $tokens = $this->get_render_tokens('');
    if(!empty($tokens)) {
      $total_marks = $tokens[$total_marks];
      $marks_obtained = $tokens[$marks_obtained];
      return ($marks_obtained/$total_marks) * 100;
   }
   return 0;
}
  function option_definition() {
    $options = parent::option_definition();
    //define our custom settings fields
    $options['total_marks'] = array();
    $options['marks_obtained'] = array();
    return $options;
  }
  function options_form(&$form, &$form_state) {    
    $options = array();
    foreach ($this->view->display_handler->get_handlers('field') as $field => $handler) {
      $options[t('Fields')]["[$field]"] = $handler->ui_name();
    }    
    $form['total_marks'] = array(
      '#type' => 'select',
      '#title' => t('Total marks'),
      '#options' => $options,
      '#default_value' => isset($this->options['total_marks']) ? $this->options['total_marks'] : '',
      '#required' => TRUE
    );
    $form['marks_obtained'] = array(
      '#type' => 'select',
      '#title' => t('Marks obtained'),
      '#options' => $options,
      '#default_value' => isset($this->options['marks_obtained']) ? $this->options['marks_obtained'] : '',
      '#required' => TRUE
    );
    parent::options_form($form, $form_state);

}     
  function options_submit(&$form, &$form_state) {
    parent::options_submit($form, $form_state);
    //update our custom values
    $this->options['total_marks'] = $form_state['values']['options']['total_marks'];
    $this->options['marks_obtained'] = $form_state['values']['options']['marks_obtained']; 
   }
}
 
options_definition() and options_form() method let us to define custom options for our field, in which we attach the previously included fields in view i.e total _marks and marks_obatined which are necessary for our field to work. It will show a select box where we can choose the fields that are available as views field.
 
options_submit() method lets us to get the submitted values. 
 
render() method is responsible for final result of the field. We use get_render_tokens() method to get available tokens for that row and get the total_marks and marks_obtained values. Then we return the final percentage.

 

Let me know, if you have any queries :)

Comments

Hi, As stated there, have you try adding files[]=views/addressfield_views_handler_field_country.inc line? If the custom table has not been already exposed to views, you might need to use hook_views_data() to do so. For eg, function MODULE_views_data() { $data['clicktracking']['table']['group'] = t('Tracking'); $data['clicktracking']['table']['base'] = array( 'field' => 'click_id', 'title' => t('Click Tracking'), 'weight' => -10, ); $data['clicktracking']['table']['join'] = array( 'node' => array( 'left_field' => 'nid', 'field' => 'nid', ), ); ...then define the field..... Let me know if you need more details. Ref https://api.drupal.org/api/views/views.api.php/function/hook_views_data/7