6.x-3.x docs.php hook_views_data()
7.x-3.x views.api.php hook_views_data()

Describes data tables (or the equivalent) to Views.

This hook should be placed in MODULENAME.views.inc and it will be auto loaded. MODULENAME.views.inc must be in the directory specified by the 'path' key returned by MODULENAME_views_api(), or the same directory as the .module file, if 'path' is unspecified.

Return value

array An associative array describing the data structure. Primary key is the name used internally by Views for the table(s) – usually the actual table name. The values for the key entries are described in detail below.

Related topics

22 functions implement hook_views_data()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

aggregator_views_data in modules/aggregator.views.inc
Implements hook_views_data().
book_views_data in modules/book.views.inc
Implements hook_views_data().
comment_views_data in modules/comment.views.inc
Implements hook_views_data().
field_views_data in modules/field.views.inc
Implements hook_views_data().
field_views_field_default_views_data in modules/field.views.inc
Default views data implementation for a field.

... See full list

1 invocation of hook_views_data()
_views_fetch_data_build in includes/cache.inc
Build and set the views data cache if empty.


./views.api.php, line 307
Describe hooks provided by the Views module.


function hook_views_data() {

  // This example describes how to write hook_views_data() for the following
  // table:
  // CREATE TABLE example_table (
  //   nid INT(11) NOT NULL COMMENT 'Primary key; refers to {node}.nid.',
  //   plain_text_field VARCHAR(32) COMMENT 'Just a plain text field.',
  //   numeric_field INT(11) COMMENT 'Just a numeric field.',
  //   boolean_field INT(1) COMMENT 'Just an on/off field.',
  //   timestamp_field INT(8) COMMENT 'Just a timestamp field.',
  //   PRIMARY KEY(nid)
  // );
  // First, the entry $data['example_table']['table'] describes properties of
  // the actual table – not its content.
  // The 'group' index will be used as a prefix in the UI for any of this
  // table's fields, sort criteria, etc. so it's easy to tell where they came
  // from.
  $data['example_table']['table']['group'] = t('Example table');

  // Define this as a base table – a table that can be described in itself by
  // views (and not just being brought in as a relationship). In reality this
  // is not very useful for this table, as it isn't really a distinct object of
  // its own, but it makes a good example.
  $data['example_table']['table']['base'] = array(
    // This is the identifier field for the view.
    'field' => 'nid',
    'title' => t('Example table'),
    'help' => t('Example table contains example content and can be related to nodes.'),
    'weight' => -10,

  // This table references the {node} table. The declaration below creates an
  // 'implicit' relationship to the node table, so that when 'node' is the base
  // table, the fields are automatically available.
  $data['example_table']['table']['join'] = array(
    // Index this array by the table name to which this table refers.
    'node' => array(
      // The primary key in the referenced table.
      'left_field' => 'nid',
      // The foreign key in this table.
      'field' => 'nid',

  // Next, describe each of the individual fields in this table to Views. This
  // is done by describing $data['example_table']['FIELD_NAME']. This part of
  // the array may then have further entries:
  //   - title: The label for the table field, as presented in Views.
  //   - help: The description text for the table field.
  //   - relationship: A description of any relationship handler for the table
  //     field.
  //   - field: A description of any field handler for the table field.
  //   - sort: A description of any sort handler for the table field.
  //   - filter: A description of any filter handler for the table field.
  //   - argument: A description of any argument handler for the table field.
  //   - area: A description of any handler for adding content to header,
  //     footer or as no result behaviour.
  // The handler descriptions are described with examples below.
  // Node ID table field.
  $data['example_table']['nid'] = array(
    'title' => t('Example content'),
    'help' => t('Some example content that references a node.'),
    // Define a relationship to the {node} table, so example_table views can
    // add a relationship to nodes. If you want to define a relationship the
    // other direction, use hook_views_data_alter(), or use the 'implicit' join
    // method described above.
    'relationship' => array(
      // The name of the table to join with.
      'base' => 'node',
      // The name of the field on the joined table.
      'base field' => 'nid',
      // 'field' => 'nid' -- see hook_views_data_alter(); not needed here.
      'handler' => 'views_handler_relationship',
      'label' => t('Default label for the relationship'),
      'title' => t('Title shown when adding the relationship'),
      'help' => t('More information on this relationship'),

  // Example plain text field.
  $data['example_table']['plain_text_field'] = array(
    'title' => t('Plain text field'),
    'help' => t('Just a plain text field.'),
    'field' => array(
      'handler' => 'views_handler_field',
      // This is use by the table display plugin.
      'click sortable' => TRUE,
    'sort' => array(
      'handler' => 'views_handler_sort',
    'filter' => array(
      'handler' => 'views_handler_filter_string',
    'argument' => array(
      'handler' => 'views_handler_argument_string',

  // Example numeric text field.
  $data['example_table']['numeric_field'] = array(
    'title' => t('Numeric field'),
    'help' => t('Just a numeric field.'),
    'field' => array(
      'handler' => 'views_handler_field_numeric',
      'click sortable' => TRUE,
    'filter' => array(
      'handler' => 'views_handler_filter_numeric',
    'sort' => array(
      'handler' => 'views_handler_sort',

  // Example boolean field.
  $data['example_table']['boolean_field'] = array(
    'title' => t('Boolean field'),
    'help' => t('Just an on/off field.'),
    'field' => array(
      'handler' => 'views_handler_field_boolean',
      'click sortable' => TRUE,
    'filter' => array(
      'handler' => 'views_handler_filter_boolean_operator',
      // Note that you can override the field-wide label.
      'label' => t('Published'),
      // This setting is used by the boolean filter handler, as possible option.
      'type' => 'yes-no',
      // use boolean_field = 1 instead of boolean_field <> 0 in WHERE statement.
      'use equal' => TRUE,
    'sort' => array(
      'handler' => 'views_handler_sort',

  // Example timestamp field.
  $data['example_table']['timestamp_field'] = array(
    'title' => t('Timestamp field'),
    'help' => t('Just a timestamp field.'),
    'field' => array(
      'handler' => 'views_handler_field_date',
      'click sortable' => TRUE,
    'sort' => array(
      'handler' => 'views_handler_sort_date',
    'filter' => array(
      'handler' => 'views_handler_filter_date',
  return $data;


chintamani’s picture

Firstly, well documented. Very useful. Thank you very much.
Secondly, how does it work if the custom table is in a different database than the one where Drupal tables reside?


pmcdougl’s picture


If you have the advanced_help module installed that info can be found at http://example.com/help/views/api-tables where example.com is your drupal install.


hash6’s picture

I am trying to join a table but the field by which I am joining is not a foreign key

$data['example_table']['table']['join'] = array(

// Index this array by the table name to which this table refers.
// 'left_field' is the primary key in the referenced table.
// 'field' is the foreign key in this table.
'node' => array(
'left_field' => 'nid',
'field' => 'nid',

mmatsoo’s picture

Use this for fields with html markup, otherwise Views will escape your html.

  // Example markup field.
  $data['example_table']['markup_field'] = array(
    'title' => t('Markup field'),
    'help' => t('A field that allows html.'),
    'field' => array(
      'handler' => 'views_handler_field_markup',
      'format' => 'full_html', // filtered_html, plain_text etc.
    // Add your other handlers (ex. sort, filter) as necessary...
CacheCache’s picture

After hours of searching, this saved my bacon. Thank you for posting

drumm’s picture


jmolinas’s picture

how can I implement Max field select using hook_views_data?

Chetna_Negi’s picture

Very nicely documented. Thanks a lot.

ArialBlack’s picture

in schema
'ecn_fee' => array(
'description' => 'ecn_fee',
'type' => 'numeric',
'size' => 'normal',
'not null' => TRUE,
'default' => 0,
'precision' => 10,
'scale' => 4
in mymodule_views_data()
$data['tradesrecords']['ecn_fee'] = array(
'title' => t('Ecn_fee'),
'help' => t('ecn_fee'),
'field' => array(
'handler' => 'views_handler_field_numeric',
'click sortable' => TRUE,
'filter' => array(
'handler' => 'views_handler_filter_numeric',
'sort' => array(
'handler' => 'views_handler_sort_numeric',
in database -0.4000 in this entity field, but in views output table displays as 0.4 (positive!)

ressa’s picture

I thought I had to add a relationship, under 'Advanced' > 'Relationships' in the Views UI, to use the field. But the field is available right away, under 'Fields' > 'Add'.
This is a great piece of documentation, thanks!

nmillin’s picture

typo "statment" --> "statement".

http://pareview.sh/pareview caught it for me. Hopefully this helps someone else out.

mastermindg’s picture

Would stored procedures be accessed the same way by Views?

mcmacerson’s picture

There are countless bad examples of how to create a Views relationship out there. Most of the examples leave out a VERY important line:

'base field' => 'nid', // The name of the field on the joined table.

THANK YOU for showing how a proper Views relationship is supposed to be constructed.

ann b’s picture

I'm trying to run the above example in a test module I created, so I can see how it works. I used hook_enable() to create the table for the example (example_table) in an .install file. The I created a mymodule.views.inc file and copied and pasted the above hook_views_data() function into the file. I then created a simple test view and clicked Fields/Add and searched through the fields. But I don't see anything different. Am I supposed to see 'plain_text_field' and 'numeric_field' in this field list?

ann b’s picture

Sorry, there was a typo in my hook name. I see now that when I click Fields/Add, the Filter dropdown now has an 'example table' value. When I choose this value, I can use the fields from my table (e.g. Boolean field, Numeric field,...).

Neograph734’s picture

By default, Views assumes that the provided field name (snippet below) is an existing database column. If it is not, Views will throw an SQL error such as this: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'example_table.pseudo_field' in 'field list'".

 $data['example_table']['provided_field_name'] = array(...); 

However when you want to create a non-database field or a calculated field, you can point Views to another field that acts as the base field of this value. This 'real field' property, which is unfortunately missing from these examples will then provide you with the value of the base field and allows you to modify it during the render function of your custom field handler.

  // Example pseudo field.
  $data ['example_table']['pseudo_field'] = array(
    'title' => t('Pseudo field'),
    'help' => t('A non database field with a value based on another one.'),
    // This field value is based on a numeric field value.
    'real field' => 'some_numeric_field',
    'field' => array(
      'handler' => 'my_custom_field_handler',
      'click sortable' => TRUE,

  // field handler
  // Disclaimer: this has not been tested and might not exactly work as described.
  class my_custom_field_handler extends views_handler_field {
     * Render a computed field value (multiplied by 5).
    function render($values) {
      $value = $this->get_value($values) * 5;
      return $this->sanitize_value($value);

If you would like to display for instance text or links, you can always set the entity id field (node.nid, etc.) as the 'real field' and not use the get_value in the render function, but simply return a string instead.

escalinnancy’s picture

This is my first custom table try. Base table id and join with node id. What is that node id. If I include that in my custom table, what input should be given for that. Am new to this. and now totally confused. Please anyone can clarify my doubt here. My custom table has got, id, firstname and lastname. I have no idea about his join.. Please help me out.
Thanks in advance

JamesRobertson’s picture

I am trying to join 2 recognised entities: Commerce_order and Message.

I have created a customised field for the Message entity: 'field_order_id' which is represented in `field_data_field_order_id` table by the column `field_order_id_value`

How is it possible to join Message using hook_views_data() (or in this case hook_views_data_alter())

This is the sql:

SELECT `o`.`order_id` FROM `commerce_order` AS `o`
LEFT JOIN `field_data_field_order_id` AS `mo` ON `o`.`order_id` = `mo`.`field_order_id_value`
INNER JOIN `message` AS `m` ON `mo`.`entity_id` = `m`.`mid`