How to create a custom Views handler

tanc's picture

Ever wanted to alter the output of a date field in Views beyond what a custom date format can do? I needed to be able to show a node’s created date formatted in two different ways depending on whether the date is today or before today: All nodes in my view with a created date of today should only show the time posted, while nodes older than today should show the full date and time posted.

This is impossible to do with a custom format for dates so altering the rendering function of the date handler for Views was required. The method for doing this is not immediately obvious so I thought I’d blog the method I used here.

In the following examples you need to substitute the name of your custom module for the word ‘example’. So if your module is called ‘mymodule’ you need to change example_views_api to mymodule_views_api.

Ok, so in your custom module you need the following hook_views_api implementation:

<?php

/**
 * Implementation of hook_views_api().
 */
function example_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'example'),
  );
}

This just tells views that we are using the views 2 api and the path to our example.views.inc file which in turn will define our custom handler. Next we need to create example.views.inc in our module directory with the following code in it:

<?php

/**
 * Implementation of hook_views_data_alter().
 */
function example_views_data_alter(&$data) {
  foreach ($data as $table => $config) {
    foreach ($config as $item => $item_config) {
      if (isset($item_config['field']['handler']) && $item_config['field']['handler'] == 'views_handler_field_date') {
        $data[$table][$item]['field']['handler'] = 'views_handler_field_example_date';
      }
    }
  }

  return $data;
}

/**
 * Implementation of hook_views_handlers().
 */
function example_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'example') . '/handlers',
    ),
    'handlers' => array(
      'views_handler_field_example_date' => array(
        'parent' => 'views_handler_field_date',
      ),
    ),
  );
}

hook_views_data_alter allow us to replace the usual date handler function with our own handler: views_handler_field_example_date. This handler is defined with hook_views_handlers and we tell it in which directory to look for the code. We also tell Views that our custom handler has a parent, the original views_handler_field_date. This is important because we are going to extend the parent in the next bit of code.

Finally create the ‘handlers’ directory in your custom module and create a new file in the handlers directory called views_handler_field_example_date.inc

Inside this you want to add something like the following:

<?php

/**
 * A handler to display dates as just the time if today, otherwise as time and date.
 *
 * @ingroup views_field_handlers
 */
class views_handler_field_example_date extends views_handler_field_date {

  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $form['date_format']['#options']['example_custom'] = t('My custom format');
  }  
    
  function render($values) {
    $format = $this->options['date_format'];
    $value = $values->{$this->field_alias};
    // check for our custom format
    if ($format == 'example_custom') {
      // check to see whether value is whithin today range
      $timezone = new DateTimeZone("Australia/Melbourne"); 
      $date = new DateTime();
      $date->setTimezone($timezone);
      $today = strtotime($date->format('Y-m-d 00:00:00'));
      if ($value >= $today) {
        // this value is still within today
        return format_date($value, 'custom', 'g:i A');
      }
      else {
        // this value is older than today
        return format_date($value, 'custom', 'j M Y g:i A');      
      }
    }
    // otherwise render the date using the normal date handler
    else {
      return parent::render($values);
    }
  }
  
}

This new class we are defining extends the views_handler_field_date class. This allows us to add our new option to the select widget and make use of the standard rendering function of the parent class.

In the options_form function we are first loading the existing options with parent::options_form($form, $form_state) and then we’re adding our own option.

In the render function we are first checking to see whether our custom option has been selecting and if not we pass the values through the standard parent render function. If we are using our new custom option then this is where we execute our custom logic to determine how to render the date and finally we return the custom rendered date.

For many other fields rendered by Views you can simply create a custom formatter, either using deciphered’s custom formatters module (easy) or by creating it in the usual way in code with theme functions (advanced). Unfortunately a node’s created date needs the Views handler approach to alter its output. Of course if you just want a simple custom date format use Date API’s custom format as its much easier to do in the GUI.