views.api.php

Describes hooks and plugins provided by the Views module.

File

drupal/core/modules/views/views.api.php
View source
<?php

/**
 * @file
 * Describes hooks and plugins provided by the Views module.
 */

/**
 * @defgroup views_plugins Views plugins
 *
 * Views plugins are objects that are used to build and render the view.
 * Plugins are registered by extending one of the Views base plugin classes
 * and defining settings in the plugin annotation.
 *
 * Views has the following types of plugins:
 * - Access: Access plugins are responsible for controlling access to the
 *   view. Views includes plugins for checking user roles and individual
 *   permissions. Access plugins extend
 *   \Drupal\views\Plugin\views\access\AccessPluginBase.
 * - Argument default: Argument default plugins allow pluggable ways of
 *   providing default values for contextual filters. This is useful for
 *   blocks and other display types lacking a natural argument input.
 *   Examples are plugins to extract node and user IDs from the URL. Argument
 *   default plugins extend
 *   \Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase.
 * - Argument validator: Validator plugins can ensure arguments are valid,
 *   and even do transformations on the arguments. They can also provide
 *   replacement patterns for the view title. For example, the 'content'
 *   validator verifies verifies that the argument value corresponds to a
 *   node, loads that node and provides the node title as a replacement
 *   pattern. Argument validator plugins extend
 *   \Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase.
 * - Cache: Cache plugins control the storage and loading of caches.
 *   Currently they can do both result and render caching. It might also be
 *   possible to cache the generated query. Cache plugins extend
 *   \Drupal\views\Plugin\views\cache\CachePluginBase.
 * - Display: Display plugins are responsible for controlling where a View is
 *   rendered; that is, how it is exposed to other parts of Drupal. 'Page'
 *   and 'block' are the most commonly used display plugins. Each View also
 *   has a 'master' (or 'default') display that includes information shared
 *   between all its displays. (See
 *   \Drupal\views\Plugin\views\display\DefaultDisplay.) Display plugins extend
 *   \Drupal\views\Plugin\views\display\DisplayPluginBase.
 * - Display extender: Display extender plugins allow additional options or
 *   configurations to added to views across all display types. For example,
 *   if you wanted to allow site users to add certain metadata to the rendered
 *   output of every view display regardless of display type, you could provide
 *   this option as a display extender. Display extender plugins extend
 *   \Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase.
 * - Exposed form: Exposed form plugins are responsible for building,
 *   rendering, and controlling exposed forms. Exposed form plugins extend
 *   \Drupal\views\Plugin\views\display\DisplayPluginBase.
 * - Handlers: Handler plugins help build the view query object that the query
 *   plugin then executes to retrieve the data from the storage backend (see
 *   below). There are several types of handlers:
 *   - Area handlers: Extend \Drupal\views\Plugin\views\area\AreaHandlerBase
 *   - Argument handlers: Extend
 *     \Drupal\views\Plugin\views\argument\ArgumentHandlerBase
 *   - Field handlers: Extend \Drupal\views\Plugin\views\field\FieldHandlerBase
 *   - Filter handlers: Extend
 *     \Drupal\views\Plugin\views\filter\FilterHandlerBase
 *   - Relationship handlers:
 *     Extend \Drupal\views\Plugin\views\relationship\RelationshipHandlerBase
 *   - Sort handlers: Extend \Drupal\views\Plugin\views\sort:SortHandlerBase
 * - Pager: Pager plugins take care of everything regarding pagers, including
 *   getting setting the total number of items to render the pager and
 *   setting the global pager arrays. Pager plugins extend
 *   \Drupal\views\Plugin\views\pager\PagerPluginBase.
 * - Query: Query plugins generate and execute a built query object against a
 *   particular storage backend, converting the Views query object into an
 *   actual query. The only default implementation is SQL. (Note that most
 *   handler plugins currently rely on the SQL query plugin.) Query plugins
 *   extend \Drupal\views\Plugin\views\query\QueryPluginBase.
 * - Row style: Row styles handle rendering each individual record from the
 *   main view table. The two default implementations render the entire entity
 *   (nodes only), or selected fields. Row style plugins extend
 *   \Drupal\views\Plugin\views\row\RowPluginBase).
 * - Style: Style plugins control how a view is displayed. For the most part
 *   they are object wrappers around theme templates. Examples of styles
 *   include HTML lists, tables, etc. Style plugins extend
 *   \Drupal\views\Plugin\views\style\StylePluginBase.
 *
 * @todo Add an explanation for each type of handler.
 * @todo Document how to use annotations and what goes in them.
 * @todo Add @ingroup to all the base plugins for this group.
 * @todo Add a separate @ingroup for all plugins?
 * @todo Document specific options on the appropriate plugin base classes.
 * @todo Add examples.
 *
 * @see \Drupal\views\Plugin\views\PluginBase
 * @see \Drupal\views\Plugin\views\HandlerBase
 */

/**
 * @defgroup views_hooks Views hooks
 * @{
 * Hooks that allow other modules to implement the Views API.
 */

/**
 * Describe data tables (or the equivalent) to Views.
 *
 * The data described with this hook is fetched and retrieved by
 * views_fetch_data().
 *
 * @return 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.
 */
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(
    'field' => 'nid',
    // This is the identifier field for the view.
    '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.
    // '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',
    ),
  );

  // 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(
      'base' => 'node',
      // The name of the table to join with
      'field' => 'nid',
      // The name of the field to join with
      'id' => 'standard',
      'label' => t('Example node'),
    ),
  );

  // 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(
      'id' => 'standard',
      'click sortable' => TRUE,
    ),
    'sort' => array(
      'id' => 'standard',
    ),
    'filter' => array(
      'id' => 'string',
    ),
    'argument' => array(
      'id' => 'string',
    ),
  );

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

  // Example boolean field.
  $data['example_table']['boolean_field'] = array(
    'title' => t('Boolean field'),
    'help' => t('Just an on/off field.'),
    'field' => array(
      'id' => 'boolean',
      'click sortable' => TRUE,
    ),
    'filter' => array(
      'id' => 'boolean',
      // 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 statment.
      'use_equal' => TRUE,
    ),
    'sort' => array(
      'id' => 'standard',
    ),
  );

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

/**
 * Alter the table structure defined by hook_views_data().
 *
 * @param array $data
 *   An array of all Views data, passed by reference. See hook_views_data() for
 *   structure.
 *
 * @see hook_views_data()
 */
function hook_views_data_alter(array &$data) {

  // This example alters the title of the node:nid field in the Views UI.
  $data['node']['nid']['title'] = t('Node-Nid');

  // This example adds an example field to the users table.
  $data['users']['example_field'] = array(
    'title' => t('Example field'),
    'help' => t('Some example content that references a user'),
    'handler' => 'hook_handlers_field_example_field',
  );

  // This example changes the handler of the node title field.
  // In this handler you could do stuff, like preview of the node when clicking
  // the node title.
  $data['node']['title']['handler'] = 'modulename_handlers_field_node_title';

  // This example adds a relationship to table {foo}, so that 'foo' views can
  // add this table using a relationship. Because we don't want to write over
  // the primary key field definition for the {foo}.fid field, we use a dummy
  // field name as the key.
  $data['foo']['dummy_name'] = array(
    'title' => t('Example relationship'),
    'help' => t('Example help'),
    'relationship' => array(
      'base' => 'example_table',
      // Table we're joining to.
      'base field' => 'eid',
      // Field on the joined table.
      'field' => 'fid',
      // Real field name on the 'foo' table.
      'id' => 'standard',
      'label' => t('Default label for relationship'),
      'title' => t('Title seen when adding relationship'),
      'help' => t('More information about relationship.'),
    ),
  );

  // Note that the $data array is not returned – it is modified by reference.
}

/**
 * Replace special strings in the query before it is executed.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The View being executed.
 * @return array
 *   An associative array where each key is a string to be replaced, and the
 *   corresponding value is its replacement. The strings to replace are often
 *   surrounded with '***', as illustrated in the example implementation.
 */
function hook_views_query_substitutions(ViewExecutable $view) {

  // Example from views_views_query_substitutions().
  return array(
    '***CURRENT_VERSION***' => VERSION,
    '***CURRENT_TIME***' => REQUEST_TIME,
    '***CURRENT_LANGUAGE***' => language(LANGUAGE_TYPE_CONTENT)->langcode,
    '***DEFAULT_LANGUAGE***' => language_default()->langcode,
  );
}

/**
 * Replace special strings when processing a view with form elements.
 *
 * @return array
 *   An associative array where each key is a string to be replaced, and the
 *   corresponding value is its replacement.
 */
function hook_views_form_substitutions() {
  return array(
    '<!--views-form-example-substitutions-->' => 'Example Substitution',
  );
}

/**
 * Alter a view at the very beginning of Views processing.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 * @param string $display_id
 *   The machine name of the active display.
 * @param array $args
 *   An array of arguments passed into the view.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_pre_view(ViewExecutable &$view, &$display_id, array &$args) {

  // Change the display if the acting user has 'administer site configuration'
  // permission, to display something radically different.
  // @todo Note that this is not necessarily the best way to solve that task.
  //   Add a better example.
  if ($view->name == 'my_special_view' && user_access('administer site configuration') && $display_id == 'public_display') {
    $display_id = 'private_display';
  }
}

/**
 * Act on the view before the query is built, but after displays are attached.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_pre_build(ViewExecutable &$view) {

  // Because of some unexplicable business logic, we should remove all
  // attachments from all views on Mondays.
  // (This alter could be done later in the execution process as well.)
  if (date('D') == 'Mon') {
    unset($view->attachment_before);
    unset($view->attachment_after);
  }
}

/**
 * Act on the view immediately after the query is built.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_post_build(ViewExecutable &$view) {

  // If the exposed field 'type' is set, hide the column containing the content
  // type. (Note that this is a solution for a particular view, and makes
  // assumptions about both exposed filter settings and the fields in the view.
  // Also note that this alter could be done at any point before the view being
  // rendered.)
  if ($view->name == 'my_view' && isset($view->exposed_raw_input['type']) && $view->exposed_raw_input['type'] != 'All') {

    // 'Type' should be interpreted as content type.
    if (isset($view->field['type'])) {
      $view->field['type']->options['exclude'] = TRUE;
    }
  }
}

/**
 * Act on the view after the query is built and just before it is executed.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_pre_execute(ViewExecutable &$view) {

  // Whenever a view queries more than two tables, show a message that notifies
  // view administrators that the query might be heavy.
  // (This action could be performed later in the execution process, but not
  // earlier.)
  if (count($view->query->tables) > 2 && user_access('administer views')) {
    drupal_set_message(t('The view %view may be heavy to execute.', array(
      '%view' => $view->name,
    )), 'warning');
  }
}

/**
 * Act on the view immediately after the query has been executed.
 *
 * At this point the query has been executed, but the pre_render() phase has
 * not yet happened for handlers.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_post_execute(ViewExecutable &$view) {

  // If there are more than 100 results, show a message that encourages the user
  // to change the filter settings.
  // (This action could be performed later in the execution process, but not
  // earlier.)
  if ($view->total_rows > 100) {
    drupal_set_message(t('You have more than 100 hits. Use the filter settings to narrow down your list.'));
  }
}

/**
 * Act on the view immediately before rendering it.
 *
 * At this point the query has been executed, and the pre_render() phase has
 * already happened for handlers, so all data should be available. This hook
 * can be utilized by themes.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_pre_render(ViewExecutable &$view) {

  // Scramble the order of the rows shown on this result page.
  // Note that this could be done earlier, but not later in the view execution
  // process.
  shuffle($view->result);
}

/**
 * Post-process any rendered data.
 *
 * This can be valuable to be able to cache a view and still have some level of
 * dynamic output. In an ideal world, the actual output will include HTML
 * comment-based tokens, and then the post process can replace those tokens.
 * This hook can be utilized by themes.
 *
 * Example usage. If it is known that the view is a node view and that the
 * primary field will be a nid, you can do something like this:
 * @code
 *   <!--post-FIELD-NID-->
 * @encode
 * And then in the post-render, create an array with the text that should
 * go there:
 * @code
 *   strtr($output, array('<!--post-FIELD-1-->' => 'output for FIELD of nid 1');
 * @encode
 * All of the cached result data will be available in $view->result, as well,
 * so all ids used in the query should be discoverable.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 * @param string $output
 *   A flat string with the rendered output of the view.
 * @param CacheBackendInterface $cache
 *   The cache settings.
 *
 * @see \Drupal\views\ViewExecutable
 */
function hook_views_post_render(ViewExecutable &$view, &$output, CacheBackendInterface &$cache) {

  // When using full pager, disable any time-based caching if there are fewer
  // than 10 results.
  if ($view->pager instanceof Drupal\views\Plugin\views\pager\Full && $cache instanceof Drupal\views\Plugin\views\cache\Time && count($view->result) < 10) {
    $cache->options['results_lifespan'] = 0;
    $cache->options['output_lifespan'] = 0;
  }
}

/**
 * Alter the query before it is executed.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view object about to be processed.
 * @param QueryPluginBase $query
 *   The query plugin object for the query.
 *
 * @see hook_views_query_substitutions()
 * @see \Drupal\views\Plugin\views\query\Sql
 */
function hook_views_query_alter(ViewExecutable &$view, QueryPluginBase &$query) {

  // (Example assuming a view with an exposed filter on node title.)
  // If the input for the title filter is a positive integer, filter against
  // node ID instead of node title.
  if ($view->name == 'my_view' && is_numeric($view->exposed_raw_input['title']) && $view->exposed_raw_input['title'] > 0) {

    // Traverse through the 'where' part of the query.
    foreach ($query->where as &$condition_group) {
      foreach ($condition_group['conditions'] as &$condition) {

        // If this is the part of the query filtering on title, chang the
        // condition to filter on node ID.
        if ($condition['field'] == 'node.title') {
          $condition = array(
            'field' => 'node.nid',
            'value' => $view->exposed_raw_input['title'],
            'operator' => '=',
          );
        }
      }
    }
  }
}

/**
 * Alter the view preview information.
 *
 * The view preview information is optionally displayed when a view is
 * previewed in the administrative UI. It includes query and performance
 * statistics.
 *
 * @param array $rows
 *   An associative array with two keys:
 *   - query: An array of rows suitable for theme('table'), containing
 *     information about the query and the display title and path.
 *   - statistics: An array of rows suitable for theme('table'), containing
 *     performance statistics.
 * @param \Drupal\views\ViewExecutable $view
 *   The view object.
 *
 * @see \Drupal\views_ui\ViewUI
 * @see theme_table()
 */
function hook_views_preview_info_alter(array &$rows, ViewExecutable $view) {

  // Adds information about the tables being queried by the view to the query
  // part of the info box.
  $rows['query'][] = array(
    t('<strong>Table queue</strong>'),
    count($view->query->table_queue) . ': (' . implode(', ', array_keys($view->query->table_queue)) . ')',
  );
}

/**
 * Alter the links displayed at the top of the view edit form.
 *
 * @param array $links
 *   A renderable array of links which will be displayed at the top of the
 *   view edit form. Each entry will be in a form suitable for theme('link').
 * @param \Drupal\views\ViewExecutable $view
 *   The view object being edited.
 * @param string $display_id
 *   The ID of the display being edited, e.g. 'default' or 'page_1'.
 *
 * @see \Drupal\views_ui\ViewUI::renderDisplayTop()
 */
function hook_views_ui_display_top_links_alter(array &$links, ViewExecutable $view, $display_id) {

  // Put the export link first in the list.
  if (isset($links['export'])) {
    $links = array(
      'export' => $links['export'],
    ) + $links;
  }
}

/**
 * Alter the commands used on a Views AJAX request.
 *
 * @param array $commands
 *   An array of ajax commands.
 * @param \Drupal\views\ViewExecutable $view
 *   The view which is requested.
 *
 * @see views_ajax()
 */
function hook_views_ajax_data_alter(array &$commands, ViewExecutable $view) {

  // Replace Views' method for scrolling to the top of the element with your
  // custom scrolling method.
  foreach ($commands as &$command) {
    if ($command['method'] == 'viewsScrollTop') {
      $command['method'] .= 'myScrollTop';
    }
  }
}

/**
 * Allow modules to respond to the invalidation of the Views cache.
 *
 * This hook will fire whenever a view is enabled, disabled, created,
 * updated, or deleted.
 *
 * @see views_invalidate_cache()
 */
function hook_views_invalidate_cache() {
  cache('mymodule')
    ->invalidateTags(array(
    'views' => TRUE,
  ));
}

/**
 * @}
 */

/**
 * @defgroup views_module_handlers Views module handlers
 * @{
 * Handlers exposed by various modules to Views.
 * @}
 */

Functions

Namesort descending Description
hook_views_ajax_data_alter Alter the commands used on a Views AJAX request.
hook_views_data Describe data tables (or the equivalent) to Views.
hook_views_data_alter Alter the table structure defined by hook_views_data().
hook_views_form_substitutions Replace special strings when processing a view with form elements.
hook_views_invalidate_cache Allow modules to respond to the invalidation of the Views cache.
hook_views_post_build Act on the view immediately after the query is built.
hook_views_post_execute Act on the view immediately after the query has been executed.
hook_views_post_render Post-process any rendered data.
hook_views_preview_info_alter Alter the view preview information.
hook_views_pre_build Act on the view before the query is built, but after displays are attached.
hook_views_pre_execute Act on the view after the query is built and just before it is executed.
hook_views_pre_render Act on the view immediately before rendering it.
hook_views_pre_view Alter a view at the very beginning of Views processing.
hook_views_query_alter Alter the query before it is executed.
hook_views_query_substitutions Replace special strings in the query before it is executed.
hook_views_ui_display_top_links_alter Alter the links displayed at the top of the view edit form.