Starting Views 2 Documentation

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
You are viewing a wiki page. You are welcome to join the group and then edit it. Be bold!

Creating this wiki page as a dumping ground for bits of documentation on Views2. It's totally a work in progress that can serve as a basis for future handbook pages.

Typical of drupal "documentation", this page completely omits critical context. A desperate user arriving at this page is given no clue where these various snippets actually go; just some of the details of how to write them.
Please, please add a couple of links to the overall topic, so someone who wants to use this stuff can put it together.

Links

Hooks

Merlin keeps changing the signatures so go check the code to see how they're really supposed to be used, this should just give you an idea what's out there.

hook_views_data
Tells views about the tables, field, filters, arguments, etc.
hook_views_data_alter
Allows modify tables, field, filters, arguments, etc.
hook_views_default_views
Lets modules provide default views.
hook_views_convert
Converting views1 views to views2.
hook_views_query_substitutions
Query substitutions

Terminology

Pseudo Table
A pseudo table is not much more than defining the name of the table as a table that doesn't exist. For example, $data['term_data_' . $vid] = array(...); Then, in that data, you need to tell it that 'table' => 'term_data' so that it knows it's the real table. And in the 'extra' part of the join, you generally do something like 'vid' => $vid

For example, a Pseudo Table for term_data would be declared as this

$data['term_data_'. $vid] = array(
   'term_data' => array(
       // This is where we link to the actual table  
       'table' => 'term_node',
       'left_field' => 'fid',
       'field' => 'fid',
   ),
);

If you've got a SQL background you may know this as an aliased table that's added to the query as part of a complex join e.g.:
LEFT JOIN {term_data} term_data_1 WHERE term_data_1.tid = term_node_1.tid AND term_data_1.vid = 1

Easy embed

Note that this function passes any additional parameters to the View as Arguments.

/**
* Embed a view using a PHP snippet.
*
* This function is meant to be called from PHP snippets, should one wish to
* embed a view in a node or something. It's meant to provide the simplest
* solution and doesn't really offer a lot of options, but breaking the function
* apart is pretty easy, and this provides a worthwhile guide to doing so.
*
* @param $name
*   The name of the view to embed.
* @param $display_id
*   The display id to embed. If unsure, use 'default', as it will always be
*   valid. But things like 'page' or 'block' should work here.
* @param ...
*   Any additional parameters will be passed as arguments.
*/
function views_embed_view($name, $display_id = 'default') {

Use page_1, page_2, block_1, block_2, ... as $display_id. You'll see the display IDs when hovering over the display names in Vews UI or in the URL when editing the displays.

API

Earl's been using Doxygen to generate API documentation: http://views2.logrus.com/doc/html/index.html
(“$view” is mostly undocumented. The Views 2 module developer API is much more clueful.)

  • Load a view:

    <?php
    $view
    = views_get_view('view_name');
    ?>
  • Initalize it and add arguments. Fiddle with options as needed:

    <?php
    $view
    ->set_display('default');
    $view->set_arguments(array('first', 'second'));
    $view->is_cacheable = FALSE;
    $view->display['default']->handler->options['items_per_page'] = 3;
    $view->display['default']->handler->options['title'] = $title;
    $view->display['default']->handler->options['display_options']['use_pager'] = FALSE;
    ?>
  • Or add a filter. Works for fields, sorts, and arguments as well.

    <?php
    $display_id
    = 'default';
    $view->set_display($display_id);
    $id = $view->add_item($display_id, 'filter', 'og_uid', 'is_admin');
    //some more example http://drupal.org/node/550886
    $item = $view->get_item($display_id, 'filter', $id);
    $item['value'] = array(0, 1);
    $view->set_item($display_id, 'filter', $id, $item);
    $view->is_cacheable = FALSE;
    ?>
  • You can set a value to be used by an existing exposed filter.

    <?php
    $view
    ->set_exposed_input(array('filter_identifier' => 'filter_value'));
    ?>
  • Get an array of the view result objects:

    <?php
    $view
    ->execute();
    foreach (
    $view->result as $result) {
      foreach (
    $view->field as $id => $field) {
          if (!empty(
    $view->field[$id]['handler'])) {
           
    $view->field[$id]['handler']->pre_render($view->result);
           
    // Do something with this unrendered result object
         
    }
        }
    }
    ?>
  • Getting to a field value taken from http://drupal.org/node/389428#comment-1312648

    <?php
    $view
    = views_get_view('my_raw_view');
    $view->set_arguments(array($node->nid));
    $view->execute();

    $field_output = $view->render_field('field_myfield', $view->row_index);
    ?>
  • Fire a module function just before view rendering

    <?php
    /**
    * Implementation of hook_views_pre_render().
    */
    function mymodule_views_pre_render(&$view) {
      if (
    $view->name == 'my_test_view') {
       
    $view->execute();
        foreach (
    $view->result as $result) {
         
    $node = node_load($result->nid);
         
    // Do something with this unrendered result object,
          // like update a field using node_submit() and node_save()
       
    }
      }
    }
    ?>
  • Another way to get array of results:

    <?php
    $view
    ->render();
    foreach (
    $view->result as $result) {
     
    // Do something with this rendered result object
    }
    ?>
  • Use a custom display plugin

    <?php
    $display_id
    = $view->add_display('my plugin');
    $result = $view->render($display_id);
    ?>
  • Another method

    <?php
    $view
    = views_get_view('my_view');
    $view->execute_display($display_id, $args);
    ?>

Theme Views 2 from template files

  • Get any field value in individual field template file
    <?php
    // current row field value
    $row_value = $row->{$view->field['FIELD_ID']->field_alias};

    // current rendered field value (with prefix, suffix et.)
    $rendered_value = $view->field['FIELD_ID']->render($row);
    ?>

Writing Views 2 style and row plugins

The Views 2 API has changed significantly from Views 1: it has been reorganized and is now much more object-oriented, but the basic concepts remain the same. You use the Views UI as a dynamic query builder to construct lists of nodes based on fields, arguments, filters, and sort criteria, which can then be styled and displayed in quite a number of ways. You use style plugins to display nodes in any type of format from simple HTML tables to more exotic formats based on XML and JSON. In this tutorial I'll go over the steps I took to create a new Views 2 style plugin for rendering nodes in the JSON data format and a Views 2 row plugin used by my style plugin .

Only local images are allowed.
New Views styles for displaying nodes in the JSON data format

A complete style plugin is made up of these components:

  1. An implementation of hook_views_api so Views will load your include files;
  2. An implementation of hook_views_plugins to declare your style plugin;
  3. An implementation of the views_plugin_style class;
  4. A theme preprocess function for your style theme;
  5. A theme .tpl.php page.

NOTE: If you are adding views code to an existing, enabled module. Make sure to clear cache_views to see your changes.

1)Declaring Views support
Views2 requires use of hook_views_api() to indicate that your module provides Views support and the version of the Views API that your module uses. As is typical, the hook_views_api() must be in the module_name.module file.

<?php
/**
* Implementation of hook_views_api().
* @return Array with Views API version.
*/
function views_json_views_api() {
  return array(
'api' => 2.0);
}
?>

2)Declaring the plugins
Views2 looks for plugins supplied by modules in files named module_name.views.inc. They can be in the module directory or an 'includes' sub-directory. My module will be called views_json so in my views_json folder I have a views_json.views.inc code file. Whereas in Views 1.x you declared your style plugins by implementing hook_views_style_plugins, all Views 2 plugins are declared by implementing hook_views_plugins and indicating the plugin type as the name of the top-level array containing your plugin definition:

<?php
/**
* Implementation of hook_views_plugins
*
*
*/
function views_json_views_plugins() {
  return array(
   
'style' => array( //declare the views_json style plugin
     
'views_json' => array(
       
'title' => t('JSON data document'),
       
'theme' => 'views_view_json',
       
'help' => t('Displays nodes in the JSON data format.'),
       
'handler' => 'views_plugin_style_json',
       
'uses row plugin' => TRUE,
       
'uses fields' => TRUE,
       
'uses options' => TRUE,
       
'type' => 'normal',      
      ),
    ),
   
'row' => array( //declare the unformatted row plugin
     
'unformatted' => array(
       
'title' => t('Unformatted'),
       
'help' => t('(Displays the unformatted data for each row from the views query with each row on a new line. Set as | for views_json.'),
       
'handler' => 'views_plugin_row_unformatted',
       
'theme' => 'views_view_row_unformatted',
       
'uses fields' => TRUE,
       
'uses options' => TRUE,
       
'type' => 'normal',
      )
     ) 
  );
}
?>

In this case I'm declaring a style plugin called views_json and a row plugin called unformatted. I need the row plugin because I want to get at the unformatted text for each record returned from the views query, and the default row plugin adds a lot of html formatting that I don't want.

3)Implementing views_plugin_style and views_plugin_row. One cool thing about Views 2 is that every default display type, style, and row are implemented as plugins derived from base classes from the root superclass views_plugin So I could just look at the code written for the default plugins to have a really good guide on how to implement my own.

I created a class views_plugin_style_json (inside views_plugin_style_json.inc) deriving from views_plugin_style:

<?php
/<strong>
*
Implementation of views_plugin_style
*
*/

class
views_plugin_style_json extends views_plugin_style {
 
  /</
strong>
   *
Set default options
  
*/
  function
option_definition() {
   
$options = parent::option_definition();
   
$options['format'] = array('default' => 'Exhibit');

    return
$options;
  }
 
 
/**
   * Provide a form for setting options.
   *
   * @param array $form
   * @param array $form_state
   */ 
 
function options_form(&$form, &$form_state) {
   
$form['format'] = array(
     
'#type' => 'radios',
     
'#title' => t('JSON data format'),
     
'#options' => array('Exhibit' => t('MIT Simile/Exhibit'), 'Canonical' => t('Canonical'), 'JSONP' => t('JSONP')),
     
'#default_value' => $this->options['format'],
    );
  }

}
?>

If you're curious about the base views_plugin_style class and all the members our class inherits, you can view the source here. Most of the boilerplate code for setting up a views style plugin is done already in the base class, I just need to implement my custom bits (+1 for OO). My implementation does only one custom thing - provide a form for users to select the JSON format they want to render. This is done by implementing options_form.
Form building for options form is similar to building forms through the Form API. An options form can be very simple, like ours which is just presenting a single set of radio buttons, to a very complex one like the one the table style uses:
Only local images are allowed.
Table style options form

In the next iteration of our JSON plugin we'll use a more advanced options form to allow the user to select what fields to render and maybe use different labels for field.

For our row plugin we follow the same pattern: we extend views_plugin_row inside views_plugin_row_unformatted.inc:

<?php
/<strong>
*
Implementation of views_row_plugin
*
*/
class
views_plugin_row_unformatted extends views_plugin_row {
   
   /</
strong>
     *
Set default options.
     */
  function
option_definition() {
   
$options = parent::option_definition();
   
$options['separator'] = array('default' => '|');

    return
$options;
  }

 
/**
   * Provide a form for setting options.
   */
 
function options_form(&$form, &$form_state) {
   
$fields = $this->display->handler->get_option('fields');
   
$options = array();
    foreach (
$fields as $field => $info) {
     
$handler = views_get_handler($info['table'], $info['field'], 'field');
      if (
$handler) {
       
$options[$field] = $handler->ui_name();
      }
    }
 
   
$form['separator'] = array(
     
'#title' => t('Separator'),
     
'#type' => 'textfield',
     
'#size' => 10,
     
'#default_value' => isset($this->options['separator']) ? $this->options['separator'] : ',',
     
'#description' => t('The separator is placed between fields.'),
    );
  }
 
}
?>

In this case we also have an options form which is displayed to the user for selecting a field delimiter.

You'll notice that we have reference to 'handlers' in each bit of code Each view has handlers designed to respond to certain actions. @TODO: explain handlers (not needed for the rest of the tutorial)

Once we have our options form setup we can process to the heart of the style plugin - the theme template preprocess function and most importantly, the theme template.

4) Register class files in .info file
Register the files containing your plugin-classes in the .info file. For example:

files[] = views/views_plugin_style_json.inc
files[] = views/views_plugin_row_unformatted.inc

5) (optional) Implement template_preprocess_
Inside template_preprocess_ you setup variables to be accessed by the plugin theme template .tpl.php file.You can also modify variables before they are passed to the theme template. You can read more about template_preprocess functions here:http://drupal.org/node/223430 as they are part if the standard Drupal theme system. In the class we extended - views_plugin_style - there is this method defined:

<?php
function render() {
     if (empty(
$this->row_plugin)) {
      
vpr('views_plugin_style_default: Missing row plugin');
       return;
     }
    
$rows = array();
     foreach (
$this->view->result as $row) {

      
$rows[] = $this->row_plugin->render($row);
     }
     return
theme($this->theme_functions(), $this->view, $this->options, $rows);
   }
?>

theme_functions() returns a full list of possible theme templates used by this style. render() is what is called by the view's display for the style plugin to do it's thing. In addition to the variables we define in our preprocess function, the most important of the variables is provide automatically to the theme: $rows. Style templates boil down to iterating through the $rows variable and spitting out some markup. We'll get to the style template in a minute. If you don't need any other variables other than $rows and $options and you don't need to modify these variables, can omit the preprocess function which will be fine for our simple style plugin.

Our row plugin template_preprocess looks like this (inside views_json.module):

<?php
/**
* Theme preprocess function for views-view-row-unformatted.tpl.php
*/
function template_preprocess_views_view_row_unformatted(&$vars) {
 
$view = $vars['view'];
 
//print('preprocess');
  // Loop through the fields for this view.
 
foreach ($view->field as $id => $field) {
    if (!empty(
$field->handler) && is_object($field->handler)) {
     
$object = new stdClass();
     
$object->content = $field->handler->theme($vars['row']);
      if (isset(
$field->handler->field_alias) && isset($vars['row']->{$field->handler->field_alias})) {
       
$object->raw = $vars['row']->{$field->handler->field_alias};
      }
      else {
       
$object->raw = NULL; // make sure it exists to reduce NOTICE
     
}
      if (!empty(
$vars['options']['separator']) && $object->content) {
       
$object->separator = filter_xss_admin($vars['options']['separator']);
      }
     
$object->handler = $field->handler;
     
$object->class = views_css_safe($id);
     
$object->label = check_plain($field->handler->label());
     
$vars['fields'][$id] = $object;
    }
  }
}
?>

It looks more complex but it follows the same pattern as for a style plugin (remember Views 2 is very OO.) Row plugins automatically provide an array variable call $fields to the theme, which contains each field value and field label for the row to be rendered. They also have analogous render() and theme_functions() methods implemented in the base views_plugin_row class Before the plugin themes the row we can modify the $fields by modifying the $vars['fields'] array.

6) Create a .tpl.php file for each theme.
Your module must have a .tpl.php file for each theme declared in hook_views_plugins. In this case, a file named views-view-json.tpl.php with the style template and a file named views-view-row-unformatted.tpl.php with the row template, are required. A good starting point is the .tpl.php files in /sites/all/modules/views/theme/.

Upgrading from Views 1.x to Views 2: A user's perspective

Views 2 is the newest major release of Views specifically coded for Drupal 6. Views 2 retains all of the core functionality of Views 1, together with a completely revamped user interface, and a large set of new features which enhance the original feature-set of Views 1. This document is a side-by-side comparison of Views 1 vs. Views 2 from a user's perspective, detailing the UI changes, some new ways to perform old tasks, the cool new feature of Views 2 and how these features can be used to address some of the shortcomings of Views 1.

Admin interface

The first thing that pops out after you install Views 2 is the radically different admin interface:
Only local images are allowed.
Views 2 admin interface

compared to the old comfy Views 1 interface:
Only local images are allowed.
Views 1 admin interface

The new admin interface performs the same functions as the old - listing all the views in the system, providing links to add or import views and a link to Views Tools - but has been compacted, with each view displayed as a paragraph style-row compared to the table of Views 1 and set of filters on top to ease locating views among a large list.

Context-help is available by clicking the small blue question-mark icon. Context-help in Views 2 is provided by the Advanced Help module so make sure to install that together with installing Views 2. Context-sensitive help is available in many areas of the Views 2 module.

A couple new attributes of each view are visible in the filter header:

  1. Tag - This is just another label for organizing and sorting views. Tags can be any text.
  2. Display - In Views 1 each view query was tied to its display; in other words your fields, sorts, filters, and arguments could only be displayed in the single page or block display provided in the view definition. In Views 2, view displays have been decoupled from view queries - it is now possible to have multiple page, block, and feed displays from a single view. More on view displays later.
  3. Type - Views 2 view types are radically different from Views 1 types. Views 1 types basically defined how the node list displays were styled - you had Full Nodes, Teaser List, Table View, and so on. In Views 2 view display styles have been broken out into the separate Style attribute. View types now refer to the primary table on which information for the query will be retrieved which controls what arguments,fields,sort criteria and filters are available. Views 2 view types are discussed later.

Adding a view

So let's jump in and add a view
Only local images are allowed.
Adding a view

The first step in adding a view is simply entering a name (only alphanumeric characters, no spaces,) a description, tag, and the view type. In this case we're going to create a view with user data.
Only local images are allowed.
Configuring the new view

This might be the 2nd whoa moment as the interface here is also completely revamped from Views 1.x. The best way to summarize is to say all the pieces from the Views 1.x interface are still there...just in different places. Fields, Arguments,Sort critera,and Filters are all there, just in new AJAXY-flavours
We can start by adding fields:
Only local images are allowed.
Adding fields

Clicking on one of the of Fields +widgets unfurls a section at the bottom of the page with all the available fields grouped by Comment, File, Node, Node revision, Taxonomy and User. This is a general paradigm for the Views 2 interface - clicking on a widget or link unfurls a section at the bottom of the page with the relevant interface. In this case you can use the Groups drop-down box to show only a subset of the fields available according to the above groups, or select All to see all fields available. In the shot below, clicking on the Add button for our fields brings up the familiar jQuery "wait" animation as the fields we selected are added to the interface
Only local images are allowed.
Adding fields

Once we add our fields they show up in the Fields section of the interface where they can be rearranged by clicking the up/down widget. We can also remove a field using the same interface:
Only local images are allowed.
Rearranging fields

Making a change to usually triggers a refresh of the view preview conveniently located right below the main interface:
Only local images are allowed.
Views preview

Now that we have some fields setup we can turn our attention to Basic Settings for the view.
It's important to note that all the interface elements pertain to the current Display selected for the view. As mentioned before a view can have multiple displays. The first time you create a view you'll be manipulating the Default display. You can add displays using the Add Display button; this lets you have as many displays of a view as you would like all sharing the same Fields, Sort Criteria, Filters and Arguments but using different types of displays like Page, Feed, Block and Attachment.
Only local images are allowed.
Adding a Page display

Let's stick with the Default display and twiddle some settings. We can set the Title to "User View 1" and the Style to Table. As mentioned earlier, view styles in Views 2 correspond more to view types in Views 1.
Only local images are allowed.
Selecting a Views 1 View Type

In Views 2, view styles control how a view display looks.
Only local images are allowed.
Selecting a Views 2 Display Style

We're given the style options of Grid, List, Table and Unformatted. Additional display styles can be added by Views style plugins. Choosing a style reveals a "settings" button which you can click to configure the style you've chosen. In the shot below we've selected and are configuring the Table style:
Only local images are allowed.
Selecting and configuring the table style

We won't set any options other than the defaults. Now we can click the preview button to get a live preview of what our view looks like:
Only local images are allowed.
Live preview

Notice that in addition to the view, we can also see the SQL query that the view is using. This is really useful in debugging complex view queries.
We can add a filter by clicking on the Filters+ widget which unfurls the filters interface:
Only local images are allowed.
Adding filters

You can select the field you want to filter on, as well as the filter operator and the filter string:

Only local images are allowed.
Configuring filter

Sort criteria are added by clicking on the sort criteria + widget:
Only local images are allowed.
Configuring sort criteria

@TODO relationships and arguments

Anytime we update sort criteria or filters or any element of the view, it triggers a refresh of the live preview. We can get a look at the view we've defined at anytime by clicking on the Preview button. Here we have defined an ascending sort, and we filter on e-mail addresses starting with c:
Only local images are allowed.
View preview

Now that we have defined our view we would like to be able to access it from somewhere other than the Views interface. In Views 1 you would use "Add Page View" to set a URL for the view:
Only local images are allowed.
Views 1 add page view

In Views 2 you specify how and where you want to display you view by adding Views displays
to your view. If we look at the add display drop-down:
Only local images are allowed.
Views 2 display types

The page display the view as a Drupal page with its own URL and menu entry.
Only local images are allowed.
Page display options

Upgrading from Views 1.x to Views 2.x: A developer's perspective

hook_views_tables() is now hook_views_data()

The new name for hook_views_tables() is hook_views_data(). That's about where their similarity ends.

Views 1.x:

<?php
function example_views_tables() {
 
// ...
 
return $tables;
}
?>

Views 2.x:

<?php
function example_views_data() {
 
// ...
 
return $data;
}
?>

Base (new in 2.x)

While Views 1.x could only display lists of nodes and node-related properties, Views 2.x allows modules to define their own top-level type that may be listed.

<?php
function example_views_data() {
 
// ...
 
$data[$table_name]['table']['base'] = array(
   
'field' => $primary_key,
   
'title' => t($human_readable_name),
   
'help' => t($human_readable_description),
  );
 
// ...
 
return $data;
}
?>

Here's an example from comment.views.inc:

<?php
function comment_views_data() {
 
// ...
 
$data['comments']['table']['base'] = array(
   
'field' => 'cid',
   
'title' => t('Comment'),
   
'help' => t("Comments are responses to node content."),
  );
 
// ...
 
return $data;
}
?>

This results in "Comments" being one of the "View types" available when creating a view.

Table definition

In Views 1.x, you needed to specify the 'name' and 'provider' of a table. In Views 2.x, only the name is specified, along with a 'group' that determines how its properties are grouped in the UI.

Views 1.x:

<?php
  $tables
['book'] = array(
   
'name' => 'book',
   
'provider' => 'internal',
   
// ...
 
);
}
?>

Views 2.x:

<?php
  $data
['book']['table']['group']  = t('Book');

 
// ...
?>

Joins

The syntax for specifying table joins has changed.

In Views 1.x, you would specify both the left and right side of a join in a full array syntax. Views 2.x uses a shorter-hand version.

Views 1.x:

<?php
   
// ...
   
'join' => array(
     
'left' => array(
       
'table' => 'node',
       
'field' => 'vid'
     
),
     
'right' => array(
       
'field' => 'vid'
     
)
    ),
   
// ...
?>

Views 2.x:

<?php
  $data
['book']['table']['join'] = array(
   
'node' => array(
     
'left_field' => 'nid',
     
'field' => 'nid',
    ),
  );
?>

This link can be useful for for creating view fields/arguments/etc in modules
http://views-help.doc.logrus.com/help/views/api-tables

Views Developers

Group organizers

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: