Skip to content

Views data integration

28. Views Data Integration (hook_views_data)

When to Use

Exposing custom database tables, altering entity views integration, defining custom handlers for existing fields, adding computed/pseudo-fields.

Pattern: Basic Table Integration

/**
 * Implements hook_views_data().
 */
function mymodule_views_data() {
  $data = [];

  $data['custom_table']['table']['group'] = t('Custom Data');
  $data['custom_table']['table']['base'] = [
    'field' => 'id',
    'title' => t('Custom Table'),
    'help' => t('Custom data table'),
  ];

  // Field definitions
  $data['custom_table']['id'] = [
    'title' => t('ID'),
    'field' => ['id' => 'numeric'],
    'sort' => ['id' => 'standard'],
    'filter' => ['id' => 'numeric'],
    'argument' => ['id' => 'numeric'],
  ];

  return $data;
}

Handler Definitions by Type

Field Handler

$data['custom_table']['name'] = [
  'title' => t('Name'),
  'help' => t('The name field'),
  'field' => [
    'id' => 'standard',  // Or custom handler ID
    'click sortable' => TRUE,
  ],
];

Filter Handler

$data['custom_table']['status'] = [
  'title' => t('Status'),
  'filter' => [
    'id' => 'boolean',
    'label' => t('Active'),
    'type' => 'yes-no',
    'use_equal' => TRUE,
  ],
];

Sort Handler

$data['custom_table']['created'] = [
  'title' => t('Created date'),
  'sort' => [
    'id' => 'date',
  ],
];

Argument (Contextual Filter)

$data['custom_table']['id'] = [
  'argument' => [
    'id' => 'numeric',
    'name field' => 'name',  // Field to use for title
    'numeric' => TRUE,
  ],
];

Relationship

$data['custom_table']['node_id'] = [
  'title' => t('Content'),
  'relationship' => [
    'id' => 'standard',
    'base' => 'node_field_data',
    'base field' => 'nid',
    'label' => t('Related content'),
  ],
];

Pattern: Join to Existing Table

function mymodule_views_data() {
  $data = [];

  // Define join FROM node_field_data TO custom_table
  $data['custom_table']['table']['join']['node_field_data'] = [
    'left_field' => 'nid',
    'field' => 'node_id',
  ];

  return $data;
}

Pattern: Altering Entity Integration

/**
 * Implements hook_views_data_alter().
 */
function mymodule_views_data_alter(&$data) {
  // Replace default handler with custom
  $data['node_field_data']['title']['field']['id'] = 'my_custom_field';

  // Add pseudo-field (computed, not in DB)
  $data['node_field_data']['custom_calculated'] = [
    'title' => t('Custom Calculation'),
    'field' => [
      'id' => 'my_custom_field',
      'real field' => 'nid',  // Actual DB field to add to query
    ],
  ];
}

Common Definition Keys

Key Purpose Example Values
id Plugin ID 'standard', 'numeric', 'boolean', 'my_custom_handler'
title Admin UI label t('Node ID')
help Admin UI description t('The unique node identifier')
real field Actual DB column 'nid' (when key name differs)
click sortable Field supports click-sort TRUE, FALSE
use_equal Use = instead of <> TRUE (performance optimization)
allow empty Support NULL operators TRUE (adds IS NULL, IS NOT NULL)

Common Mistakes

  • Missing 'table']['base' for base tables → Views won't list table as queryable base
  • Wrong handler ID → Use standard handlers ('numeric', 'string', 'boolean') unless custom needed
  • Forgetting 'real field' for computed fields → Views needs to know which DB field to actually query
  • Not defining all handler types → Fields without sort/filter definitions can't be sorted/filtered
  • Missing join definitions → Related tables need join paths defined

See Also

  • Section 19-27: Plugin types that hook_views_data() references
  • Reference: core/modules/views/src/EntityViewsData.php — entity integration base class
  • Reference: core/modules/node/src/NodeViewsData.php — entity-specific customization example