20 APIs in 20 Days: View on Views API

Error message

The spam filter installed on this site is currently unavailable. Per site policy, we are unable to accept new submissions until that problem is resolved. Please try resubmitting the form in a couple of minutes.

This post is an entry in our 20 APIs in 20 Days series. Learn more about how best practices lead to sustainable development at www.trellon.com.

Views is the most widely used of all the contributed modules for Drupal. It offers the ability to easily create pages and blocks of dynamic content, which can be authored through a web based interface. It also allows site admins to attach filters to views, providing useful ways to drill down on data without too much effort.

The Views module also possesses a well defined API for working with views in code. Developers can call views within their modules, modify the way views are displayed, and even create new views programmatically. There is extensive documentation and community discussion about the different ways you can work with views in your code. This edition of the 20 APIs in 20 Days series seeks to provide an overview of some of the less well-known features of the API, that our team often finds useful when developing sites for clients.

Data Hooks for the Views Module

For developers building custom modules, there are times you might want to have some views that already exist when your module is installed. You may also want to allow data generated by your module to display within Views created by users. The Views API provides data hooks to allow you to do this.

Data hooks are for developers looking to integrate their modules with the Views module. At a real basic level, data hooks allow developers to tell the Views module how to include data generated by the module into a view. They also give the Views module some information about that data, and how it relates to other content in your site. At a more advanced level, data hooks allow developers to control the way information will appear in Views, and how to handle more advanced operations like filtering and sorting. Overall, this set of hooks provides developers with a comprehensive way to tie the operations of their module into the Views module.

The data hooks available to developers in Views 6 are:

  • hook_views_api – This is a very simple hook that registers a module with the Views API. It provides some data about the version of the Views API that is supported.
  • hook_views_default_views – This hook allows module developers to create original views when the module is installed. This saves users from having to create the views themselves, and ensures that modules can provide reliable interfaces.
  • hook_views_data – This hook provides the Views module with information about the tables containing data that Views can pull from. It defines relationships between these tables and other ones in Drupal, so that Views can create the appropriate queries.
  • hook_views_handlers – Basically, this hook does 2 things: first, it allows developers to control how the UI for a certain field will appear within the Views query builder. Second, it controls how a field will display when it presented within a view. There are more advanced options available as well.
  • hook_views_plugins – Views can be extended with new plugins. These plugins can provide new ways of displaying view data (displays, styles, rows), access control of views and argument validators.

Together, these functions that compose the data hooks combine to create a powerful API for hooking into views. When exmployed, your custom module can provide data to views, control the way it will appear, and permit advanced filtering. Here is an example of data hooks in action, taken from the nodequeue module.

<?php
/**
 * hook_views_api - registers the module with the Views API
 * This specific example tells Drupal to use version 2 of the API
 */
function nodequeue_views_api() {
  return array(
   
'api' => 2,
   
'path' => drupal_get_path('module', 'nodequeue') .'/includes/views',
  );
}

/**
 * Implementation of hook_views_data()
 *
 * hook_views_data tells the views module where to look for data. There is an entry 
 * for each field supported by the view. This example is truncated to save space.
 *
 */
function nodequeue_views_data() {

 
$data = array();

 
// ----------------------------------------------------------------
  // nodequeue_nodes table
 
$data['nodequeue_nodes']['table']['group'] = t('Nodequeue');

 
// this example shows how a field is connected to your views. A filter and sorting
  // function is tied to the field, which enables views to carry out these functions without
  // needing to write more code.
 
$data['nodequeue_nodes']['position'] = array(
   
'title' => t('Position'),
   
'help' => t('The position of the node within a queue.'),
   
'field' => array(
     
'handler' => 'views_handler_field_numeric',
     
'click sortable' => TRUE,
     ),
   
'sort' => array(
     
'handler' => 'views_handler_sort',
    ),
   
'filter' => array(
     
'handler' => 'views_handler_filter_numeric',
    ),
   
'argument' => array(
     
'handler' => 'views_handler_argument_numeric',
    ),
  );

 
// notice how a different sorting handler and filter is applied to this field. Views
  // has native tools for handling dates, numbers, text and other types of data.
 
$data['nodequeue_nodes']['timestamp'] = array(
   
'title' => t('Added date'),
   
'help' => t('The date the node was added to a queue.'),
   
'field' => array(
     
'handler' => 'views_handler_field_date',
     
'click sortable' => TRUE,
    ),
   
'sort' => array(
     
'handler' => 'views_handler_sort_date',
    ),
   
'filter' => array(
     
'handler' => 'views_handler_filter_date',
    ),
  );

 
// there are a bunch of other fields contained in this function, but these two are good
  // examples of how fields are tied into views.
 
...

  return
$data;
}


/**
 * Implementation of hook_views_handlers()
 *
 * hook_views_handlers tells the views module how to present fields within the UI and also how to
 * display fields within a view itself. You will find a file corresponding to each parent item
 * in the module directory.
 *
 */

function nodequeue_views_handlers() {
  return array(
 
'info' => array(
   
'path' => drupal_get_path('module', 'nodequeue') .'/includes/views',
    ),
   
'handlers' => array(
     
'nodequeue_handler_argument_subqueue_qid' => array(
        
'parent' => 'views_handler_filter_numeric',
       ),
     
'nodequeue_handler_field_all_queues' => array(
       
'parent' => 'views_handler_field_prerender_list',
      ),
     
'nodequeue_handler_field_all_subqueues' => array(
       
'parent' => 'nodequeue_handler_field_all_queues',
      ),
     
'nodequeue_handler_field_links' => array(
       
'parent' => 'views_handler_field_node_link',
      ),
     
'nodequeue_handler_field_queue_tab' => array(
       
'parent' => 'views_handler_field_node_link',
      ),
     
'nodequeue_handler_filter_in_queue' => array(
       
'parent' => 'views_handler_filter_boolean_operator',
      ),
     
'nodequeue_handler_relationship_nodequeue' => array(
       
'parent' => 'views_handler_relationship',
      ),
    ),
  );
}
?>

This is a fairly simple example of how to work with data hooks in custom modules to tie them into the Views API. By no means should this be considered exhaustive - there is detailed documentation available about the Views API at http://views.doc.logrus.com/.

Change the Views as They are Built

Another group of Views APIs are alter hooks. Using these hooks you can change the view behavior in different phases of building a view. A change can be in any aspect of the view which can be changed in Views UI. Each view is a PHP Object, which consists of many different properties. Changing these properties can change the output of the view. Let's look at available hooks ordered by how they are called in code:

  • hook_views_pre_view - This hook is called at the very beginning of views processing, before anything is done.
  • hook_views_pre_execute - This hook is called right before the execute process. The query is now fully built, but it has not yet been run through db_rewrite_sql.
  • hook_views_pre_render - This hook is called right before the render process. The query has been executed, and the pre_render() phase has already happened for handlers, so all data should be available.
  • hook_views_post_render - Post process any rendered data.

Alter hooks have a lot of applications in development, and they can make Views more usable and useful without having to write much code. This is an example of how to attach some text before a view. Using hook_views_pre_render allows us to keep PHP code out of the views header and control the display of certain views in a more permanent form:

<?php
/**
  * Attach term description to view by argument
  */
function MYMODULE_views_pre_render(&$view) {
  if (
$view->name == 'view-i-want-to-change') {
   
// we can access view arguments
   
$term = taxonomy_get_term($view->arg[0]);
    if (
$term) {
     
$view->attachment_before = check_markup($term->description, FILTER_FORMAT_DEFAULT, FALSE);
    }
  }
}
?>

Beyond just controlling the display of views, filters and sorts can be modified as well. This example code demonstrates how to modify the order in which a view presents data.

<?php
function MYMODULE_views_pre_render(&$view) {
  if (
$view->name == 'view-i-want-to-change') {
   
// we defined 3 sort in Views UI for our view
   
$available_sorts = array('node_title' => t('Title'), 'node_created' => t('Post Date'), 'user_name' => t('Author'));
   
// determine which sort is available
   
$current_sort = isset($_GET['sort']) && isset($available_sorts[$_GET['sort']) ? $_GET['sort'] : 'node_title';

   
// now we will remove all sort definitions except one its used
   
$sort = $view->display_handler->get_option('sort');
    foreach (
array_keys($sort) as $sort_name) {
      if (
$sort_name != $current_sort) {
       
// here we are removing unused sorts
       
unset($sort[$sort_name]);
      }
    }
   
$view->display_handler->sort_option('sort', $sort);

   
// we build sort links
   
$links = array();
    foreach (
$available_sorts as $type => $title) {
     
$links[] = array(
       
'title' => $title,
       
'path' => $_GET['q'],
       
'query' => array('sort' => $type)
      )
    }
   
// we will attach links to view
   
$view->attachment_before = theme('links', $links);
  }
}
?>

It should be noted that modifying the sort of a view can be a painful process and require a lot of troubleshooting to get right. Don't forget to use the Devel module when exploring view objects and it's nice dpm function, which can save you days of head scratching. Here is an example:

<?php
function MYMODULE_views_pre_render(&$view) {
 
dpm($view);
}
?>

Organize your Code

A lot of hooks have been mentioned in this post, and it is generally unnecessary to store all of your views API code in a single file. In fact, it is usually a bad idea to keep everything all in one place - loading all of this code each time a view loads would lead to performance problems. With the Views API, code can be divided to many files without much effort, and only the necessary files will be loaded to get a view to run.

So which hook belongs where? Some thought has gone into common conventions for strucutring code within custom modules, to make them easier to understand and work with. The outline below is intended to demonstrate a typical implementation pattern for views code.

  • MYMODULE.module – main module file
    • hook_views_api

    • All alter hooks (hook_views_pre_view, hook_views_pre_execute, hook_views_pre_render, hook_views_post_render)
  • MYMODULE.views.inc – this file contains hook, that provides data and plugins to views
    • hook_views_data

    • hook_views_handlers
    • hook_views_plugins
  • MYMODULE.views_default.inc – contains only one hook with long views definitions
    • hook_views_default_views

Other Useful Functions Every Developer Ought to Know

Finally, the Views API doesn't ends with hooks, but it provides also small functions that make the lives of developers easier. If you are planning on doing intensive Views hacking, it is worth your time to scour the documentation and get a better sense of all the Views module has to offer. Here are some of the more useful functions every developers should be aware of:

  • views_embed_view – Use this function to display a view on a web page. Fairly straightforward.
  • views_get_view – returns a full View object as a gigantic array. Use this when you want to modify the basic parameters associated with a view, without needing to go through the alter hooks. There are some things alter hooks cannot modify easily, and sometimes this function offers a more expedient route.

More Resources

This overview only describes a few approaches to working with views, and these are just the ones we tend to use most often here at Trellon. There are some additional resources listed below for digging into the details of the Views API and coming up with your own solutions to common challenges. Be sure to catch all of Trellon’s API blog posts as we explore the fundamental tools for extending Drupal’s functionality.