Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
This is a cheat sheet for information about extending the capabilities of Views by extending and building your own handlers. It will cover the basic handlers and attempt to cover some of the hooks that you might use when doing so. Including examples, basic documentation and some caveats where necessary.THIS IS NOT A COMPLETE LIST!! Consider the …

#Views Handlers - A Shakedown

This is a cheat sheet for information about extending the capabilities of Views by extending and building your own handlers. It will cover the basic handlers and attempt to cover some of the hooks that you might use when doing so. Including examples, basic documentation and some caveats where necessary. THIS IS NOT A COMPLETE LIST!! Consider the code of the handler your looking to extend to be the ultimate source of documentation. This is merely a convenience and a starting point.

_Ignore the tags in the included snippets, they are a convenience for formatting.

Extending / Including Your Handler

  • Determine which handler you're going to extend. (eg views_handler_field_numeric).
  • Create a PHP .inc file using the class name of your handler: views_handler_field_my_numeric_field.inc.
  • Add the following code as a starting point for your handler:
<?php
class views_handler_field_my_numeric_field extends views_handler_field_numeric {
  // Functions go here.
}
?>

Depending on your Drupal version...

Drupal 7

  1. Add the following code to your .info file to ensure that Drupal acknowledges and loads your class. Remember that it must be one file per class. files[] = /views_handler_field_my_numeric_field.inc

Drupal 6

  1. Implement the hook_views_handlers() in the same location as your hook_views_data() to advise Views where to find your class file and what its parent class is:
<?php
function YOURMODULE_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'YOURMODULE') . 'path/to/handlers',
    ),
    'handlers' => array(
      'views_handler_field_my_numeric_field' => array(
        'parent' => 'views_handler_field_numeric',
      ),
    ),
  );
}
?>
  1. Add your code in the appropriate function to achieve the desired functionality.

All of the different handlers share a certain amount of the core functions so overriding certain aspects will occur in the same place for all of them, the query function is a good example of this. We'll cover all the generic ones in the Field Handler section for simplicity. Whilst the coding standards may complain at you regarding the naming convention of your classes, the standard for these handlers is to not use camel casing, and to where possible, avoid the use of public,private, or protected declarations on types and functions.

Adding Options

To add additional configuration options to any handler there are a minimum of two functions that must be included in your handler. They are option_definition and options_form. Depending on your needs there is also options_form_validate and options_form_submit. This process is the same for any handler type, field, argument, filter, or sort.

Adding Options to the Handler

<?php
function option_definition() {
  // It's vital you call the parent function so that the rest
  // of the handler default settings are initiated correctly.
  $options = parent::option_definition();
  // Now you can add your option with a default value.
  $options['my_new_option_key'] = array('default' => 'MY_DEFAULT');
  // Return the $options so the handler setup can continue.
  return $options;
}
?>

Then to expose a form to update your settings, you include a call to options_form(&$form, &$form_state). Both parameters must be taken by reference otherwise the form will not be updated as you expect, and will in all likelihood be quite broken.

<?php
function options_form(&$form, &$form_state) {
  // Remember to call the parent::options_form if you just want to
  // add your options to the whole form instead of trying to replace
  // it entirely.
  parent::($form, $form_state);
  // Now we can add our new option using normal FAPI elements.
  $form['my_new_option_key'] = array(
    '#type' => 'select',
    '#title' => t('Fancy Pants Option #1'),
    '#options' => array(
      'MY_DEFAULT' => t('Door #1'),
      'DOOR_TWO' => t('Door #2'),
      'DOOR_THREE' => t('Door #3'),
    ),
    // All properly defined options are saved to the handler and are accessible
    // from the 'options' array on that object. To access this use the following.
    '#default_value' => $this->options['my_new_option_key']
  );
  // Note that we do not return the form. We've modified it by reference so we
  // don't have to. You can also alter any component of the form here if it has
  // been added when your handler runs.
}
?>

Altering the Output

To change how the field is rendered you need to implement the render($values) function.

<?php
// The $values object contains the result of the current row.
function render($values) {
  // Best practice is to ensure you have something to work with
  // before attempting to render anything. As Views assigns an alias
  // to every field and uses that alias to key the results you must
  // use that value to locate your data on the $values obj. Thus...
  if (!empty($values->{$this->field_alias})) {
    // You can then either just return the desired output..
    return "My Value:" . $values->{$this->field_alias}
    // You can return the result of a theme function
    return theme('my_module_field_theme', array('result_var' => $values->{$this->field_alias}));
    // Additional Fields should also be available at this point
    return "My Foo:" . $this->additional_fields['foo_prefix_var'] . $values->{$this->field_alias};
  }
}
?>

There is also a pre_render(&$values) function that allows you to adjust values and run some setup prior to any value being passed to its respective render function. For the majority of cases, the render function is sufficient.

Exposed Form / Filter Forms

When creating a filter handler, there might be a need to create custom form to be used for when the filter is set to be exposed. There is also a function for altering the form that is used to configure the filter itself that runs in addition to the functions explained above regarding the option_definition and options_form.

First is the value_form function that adjusts the presentation of the filter settings form in the View.

<?php
function value_form(&$form, &$form_state) {
  // You may create a new form layout for your custom
  // field here using the normal FAPI elements. The
  // current value of the filter is stored in
  // $this->value.
  // This form element must be 'value' unless you're
  // handling the submit and validation steps yourself.

  // Our field title is configurable on the default form
  // so we must take that into account if we're overriding
  // the 'value' form element.
  $title = ($this->options['exposed'])
    ? $this->options['expose']['label']
    : $this->definition['title'];

  $form['value'] = array(
    '#type' => 'textfield',
    '#title' => t('My Filter Value'),
    '#default_value' => $this->value,
  );
}
?>

The other form is the exposed form, this is the form that is created when you opt to display this filter to users and let them change the value. It's implementation is very similar to the value form however its purpose is to be used by end users and thus can be treated differently to a filter that is only for admins and View creators. The implementation and requirements depend entirely on what you're trying to achieve with your custom filter.

<?php
function exposed_form(&$form, &$form_state) {
  // This allows for a place holder function, which
  // simply calls the parent handler to create the form.
  parent::exposed_form($form, $form_state);
}
?>

Adjusting the Query (Advanced)

To alter how the query is performed for this handler then you must implement your own version of the query function. This is the same function for all the handler implementations.

<?php
// This is the basic views_handler_field query function.
function query() {
  $this->ensure_my_table();
  // Add the field.
  $params = $this->options['group_type'] != 'group' ? array('function' => $this->options['group_type']) : array();
  $this->field_alias = $this->query->add_field($this->table_alias, $this->real_field, NULL, $params);

  $this->add_additional_fields();
}
?>

Primarily the query function is used to ensure that the table you're about to try to pull data from actually exists in the query. This is the ensure_my_table() function. If there is any grouping information to be added to the field that information is extracted.

Your field is then added to the query by calling the add_field function on the query handler. Note that this uses the real_field value, this uses either the ID of the field itself, or the real field value that you set on your Views Data entry for this field.

The call to add_additional_fields will go through the motions of adding any fields specified in your field definition under additional fields to the query. The $params array contains additional options.

This function is generally overridden if there is some special requirement for selecting your field, or you want to leverage functionality that is built into your choice of persistent storage. Such as a CONCAT function in SQL.

Always ensure that the field_alias value is set during this function otherwise the rest of the View processes may not work smoothly.

Different operations of the Query function

The different types of handlers have different goals inside the query function.

  • The field handler will generally always want to run add_field function to ensure the information is selected for use later.
  • The argument handler will usually want to run add_where to limit the query based on the provided arguments. The value for the passed in variable is stored on $this->argument.
  • The sort handler runs add_orderby to add its requirements to the ORDER BY or sort array. Depending on the storage type.
  • Finally the filter handler will also want to run a add_where as you are generally trying to filter the results of a View.

The function definitions can be found either in the views_plugin_query_default.inc in the Views module, or online:

All Query functions are here.

Two other features that are useful for more advanced functionality are views_join and add_relationship. When combined these two features let you create joins and relationships to other tables and data to allow for more powerful automated query creation. I highly recommend examining some of the included Views handlers to see how these two interact.

Accounting For Solr

Extending handlers for creating more advanced functionality for the Apache Solr views integration matches the above functionality with the following exceptions:

  • There are no joins or relationships in Solr. So don't even try to add them.
  • add_orderby is replaced by add_sort.
  • There is no need for ensure_my_table or ensure_table as they are redundant when querying a Solr index.
  • The list of base handlers that will most commonly be used for creating new functionality are located at: apachesolr_views/handlers. These handlers extend the core Views handlers but provide the underlying functional changes for use with Solr so they are a good starting point.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment