views.api.php 32.3 KB
Newer Older
merlinofchaos's avatar
merlinofchaos committed
1 2 3 4
<?php

/**
 * @file
5
 * Describes hooks and plugins provided by the Views module.
merlinofchaos's avatar
merlinofchaos committed
6 7
 */

8 9
use Drupal\Core\Language\LanguageInterface;

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/**
 * @defgroup views_overview Views overview
 * @{
 * Overview of the Views module API
 *
 * The Views module is a generalized query and display engine, which can be used
 * to make views (formatted lists, grids, feeds, and other output) of items
 * (often entities, but can be other types of data). Developers can interact
 * with Views in several ways:
 * - Provide plugins: Views plugins govern nearly every aspect of views,
 *   including querying (sorting, filtering, etc.) and display (at several
 *   levels of granularity, ranging from the entire view to the details of a
 *   field). See the @link views_plugins Views plugins topic @endlink for
 *   more information.
 * - Provide data: Data types can be provided to Views by implementing
 *   hook_views_data(), and data types provided by other modules can be
 *   altered by implementing hook_views_data_alter().
 * - Implement hooks: A few operations in Views can be influenced by hooks.
 *   See the @link Views hooks topic @endlink for a list.
 * - Theming: See the @link views_templates Views templates topic @endlink
 *   for more information.
 *
 * @see \Drupal\views\ViewExecutable
 * @}
 */

merlinofchaos's avatar
merlinofchaos committed
36
/**
37
 * @defgroup views_plugins Views plugins
merlinofchaos's avatar
merlinofchaos committed
38
 *
39
 * Views plugins are objects that are used to build and render the view.
40 41 42 43
 * See individual views plugin topics for more information about the
 * specifics of each plugin type, and the
 * @link plugin_api Plugin API topic @endlink for more information about
 * plugins in general.
merlinofchaos's avatar
merlinofchaos committed
44
 *
45 46 47
 * Some Views plugins are known as handlers. Handler plugins help build the
 * view query object: filtering, contextual filtering, sorting, relationships,
 * etc.
48
 *
49 50
 * @todo Document specific options on the appropriate plugin base classes.
 * @todo Add examples.
merlinofchaos's avatar
merlinofchaos committed
51
 *
52 53
 * @see \Drupal\views\Plugin\views\PluginBase
 * @see \Drupal\views\Plugin\views\HandlerBase
54 55
 * @see plugin_api
 * @see annotation
merlinofchaos's avatar
merlinofchaos committed
56 57 58 59 60
 */

/**
 * @defgroup views_hooks Views hooks
 * @{
61
 * Hooks that allow other modules to implement the Views API.
merlinofchaos's avatar
merlinofchaos committed
62 63
 */

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/**
 * Analyze a view to provide warnings about its configuration.
 *
 * @param \Drupal\views\ViewExecutable $view
 *   The view being executed.
 *
 * @return array
 *   Array of warning messages built by Analyzer::formatMessage to be displayed
 *   to the user following analysis of the view.
 */
function hook_views_analyze(Drupal\views\ViewExecutable $view) {
  $messages = array();

  if ($view->display_handler->options['pager']['type'] == 'none') {
    $messages[] = Drupal\views\Analyzer::formatMessage(t('This view has no pager. This could cause performance issues when the view contains many items.'), 'warning');
  }

  return $messages;
}

merlinofchaos's avatar
merlinofchaos committed
84
/**
85
 * Describe data tables (or the equivalent) to Views.
86
 *
87
 * The data described with this hook is fetched and retrieved by
88
 * \Drupal\views\Views::viewsData()->get().
89
 *
90
 * @return array
merlinofchaos's avatar
merlinofchaos committed
91
 *   An associative array describing the data structure. Primary key is the
92
 *   name used internally by Views for the table(s) – usually the actual table
merlinofchaos's avatar
merlinofchaos committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
 *   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
109
  // the actual table – not its content.
merlinofchaos's avatar
merlinofchaos committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

  // 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.
145 146
  //   - relationship: A description of any relationship handler for the table
  //     field.
merlinofchaos's avatar
merlinofchaos committed
147 148 149 150 151
  //   - 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,
152
  //     footer or as no result behavior.
merlinofchaos's avatar
merlinofchaos committed
153 154 155 156 157 158 159
  //
  // 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.'),
160 161 162 163
    // 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.
merlinofchaos's avatar
merlinofchaos committed
164
    'relationship' => array(
dawehner's avatar
dawehner committed
165 166
      'base' => 'node', // The name of the table to join with
      'field' => 'nid', // The name of the field to join with
aspilicious's avatar
aspilicious committed
167
      'id' => 'standard',
dawehner's avatar
dawehner committed
168
      'label' => t('Example node'),
merlinofchaos's avatar
merlinofchaos committed
169 170 171 172 173 174 175 176
    ),
  );

  // 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(
aspilicious's avatar
aspilicious committed
177
      'id' => 'standard',
merlinofchaos's avatar
merlinofchaos committed
178 179
    ),
    'sort' => array(
aspilicious's avatar
aspilicious committed
180
      'id' => 'standard',
merlinofchaos's avatar
merlinofchaos committed
181 182
    ),
    'filter' => array(
aspilicious's avatar
aspilicious committed
183
      'id' => 'string',
merlinofchaos's avatar
merlinofchaos committed
184 185
    ),
    'argument' => array(
aspilicious's avatar
aspilicious committed
186
      'id' => 'string',
merlinofchaos's avatar
merlinofchaos committed
187 188 189 190 191 192 193 194
    ),
  );

  // Example numeric text field.
  $data['example_table']['numeric_field'] = array(
    'title' => t('Numeric field'),
    'help' => t('Just a numeric field.'),
    'field' => array(
aspilicious's avatar
aspilicious committed
195
      'id' => 'numeric',
196
    ),
merlinofchaos's avatar
merlinofchaos committed
197
    'filter' => array(
aspilicious's avatar
aspilicious committed
198
      'id' => 'numeric',
merlinofchaos's avatar
merlinofchaos committed
199 200
    ),
    'sort' => array(
aspilicious's avatar
aspilicious committed
201
      'id' => 'standard',
merlinofchaos's avatar
merlinofchaos committed
202 203 204 205 206 207 208 209
    ),
  );

  // Example boolean field.
  $data['example_table']['boolean_field'] = array(
    'title' => t('Boolean field'),
    'help' => t('Just an on/off field.'),
    'field' => array(
aspilicious's avatar
aspilicious committed
210
      'id' => 'boolean',
merlinofchaos's avatar
merlinofchaos committed
211 212
    ),
    'filter' => array(
aspilicious's avatar
aspilicious committed
213
      'id' => 'boolean',
merlinofchaos's avatar
merlinofchaos committed
214 215 216 217
      // 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',
218
      // use boolean_field = 1 instead of boolean_field <> 0 in WHERE statement.
219
      'use_equal' => TRUE,
merlinofchaos's avatar
merlinofchaos committed
220 221
    ),
    'sort' => array(
aspilicious's avatar
aspilicious committed
222
      'id' => 'standard',
merlinofchaos's avatar
merlinofchaos committed
223 224 225 226 227 228 229 230
    ),
  );

  // Example timestamp field.
  $data['example_table']['timestamp_field'] = array(
    'title' => t('Timestamp field'),
    'help' => t('Just a timestamp field.'),
    'field' => array(
aspilicious's avatar
aspilicious committed
231
      'id' => 'date',
merlinofchaos's avatar
merlinofchaos committed
232 233
    ),
    'sort' => array(
aspilicious's avatar
aspilicious committed
234
      'id' => 'date',
merlinofchaos's avatar
merlinofchaos committed
235 236
    ),
    'filter' => array(
aspilicious's avatar
aspilicious committed
237
      'id' => 'date',
merlinofchaos's avatar
merlinofchaos committed
238 239 240 241 242 243 244
    ),
  );

  return $data;
}

/**
245
 * Alter the table structure defined by hook_views_data().
merlinofchaos's avatar
merlinofchaos committed
246
 *
247
 * @param array $data
248 249
 *   An array of all Views data, passed by reference. See hook_views_data() for
 *   structure.
merlinofchaos's avatar
merlinofchaos committed
250 251 252
 *
 * @see hook_views_data()
 */
253
function hook_views_data_alter(array &$data) {
254
  // This example alters the title of the node:nid field in the Views UI.
merlinofchaos's avatar
merlinofchaos committed
255 256
  $data['node']['nid']['title'] = t('Node-Nid');

257
  // This example adds an example field to the users table.
merlinofchaos's avatar
merlinofchaos committed
258 259 260 261
  $data['users']['example_field'] = array(
    'title' => t('Example field'),
    'help' => t('Some example content that references a user'),
    'handler' => 'hook_handlers_field_example_field',
262 263 264
    'field' => array(
      'id' => 'example_field',
    ),
merlinofchaos's avatar
merlinofchaos committed
265 266 267
  );

  // This example changes the handler of the node title field.
268
  // In this handler you could do stuff, like preview of the node when clicking
merlinofchaos's avatar
merlinofchaos committed
269
  // the node title.
270
  $data['node']['title']['field']['id'] = 'node_title';
merlinofchaos's avatar
merlinofchaos committed
271

272 273 274 275 276 277 278 279 280 281 282
  // 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.
aspilicious's avatar
aspilicious committed
283
      'id' => 'standard',
284 285 286 287 288 289
      'label' => t('Default label for relationship'),
      'title' => t('Title seen when adding relationship'),
      'help' => t('More information about relationship.'),
    ),
  );

merlinofchaos's avatar
merlinofchaos committed
290 291 292
  // Note that the $data array is not returned – it is modified by reference.
}

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
/**
 * Override the default Views data for a Field API field.
 *
 * The field module's implementation of hook_views_data() invokes this for each
 * field, in the module that defines the field type (as declared in the field
 * array). It is not invoked in other modules.
 *
 * If no hook implementation exists, hook_views_data() falls back to
 * field_views_field_default_views_data().
 *
 * @param \Drupal\field\FieldConfigInterface $field
 *   The field config entity.
 *
 * @return array
 *   An array of views data, in the same format as the return value of
 *   hook_views_data().
 *
 * @see field_views_data()
 * @see hook_field_views_data_alter()
 * @see hook_field_views_data_views_data_alter()
 */
function hook_field_views_data(\Drupal\field\FieldConfigInterface $field) {
  $data = field_views_field_default_views_data($field);
  foreach ($data as $table_name => $table_data) {
    // Add the relationship only on the target_id field.
    $data[$table_name][$field->getName() . '_target_id']['relationship'] = array(
      'id' => 'standard',
      'base' => 'file_managed',
      'base field' => 'target_id',
      'label' => t('image from !field_name', array('!field_name' => $field->getName())),
    );
  }

  return $data;
}

/**
 * Alter the Views data for a single Field API field.
 *
 * This is called on all modules even if there is no hook_field_views_data()
 * implementation for the field, and therefore may be used to alter the
 * default data that field_views_field_default_views_data() supplies for the
 * field.
 *
 *  @param array $data
 *    The views data for the field. This has the same format as the return
 *    value of hook_views_data().
 *  @param \Drupal\field\FieldConfigInterface $field
 *    The field config entity.
 *
 * @see field_views_data()
 * @see hook_field_views_data()
 * @see hook_field_views_data_views_data_alter()
 */
function hook_field_views_data_alter(array &$data, \Drupal\field\FieldConfigInterface $field) {
  $entity_type_id = $field->entity_type;
  $field_name = $field->getName();
  $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
  $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;

  list($label) = field_views_field_label($entity_type_id, $field_name);

  $data['file_managed'][$pseudo_field_name]['relationship'] = array(
    'title' => t('@entity using @field', array('@entity' => $entity_type->getLabel(), '@field' => $label)),
    'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity_type->getLabel(), '@field' => $label)),
    'id' => 'entity_reverse',
    'field_name' => $field_name,
    'entity_type' => $entity_type_id,
    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field),
    'field field' => $field_name . '_target_id',
    'base' => $entity_type->getBaseTable(),
    'base field' => $entity_type->getKey('id'),
    'label' => t('!field_name', array('!field_name' => $field_name)),
    'join_extra' => array(
      0 => array(
        'field' => 'deleted',
        'value' => 0,
        'numeric' => TRUE,
      ),
    ),
  );
}

/**
 * Alter the Views data on a per field basis.
 *
 * The field module's implementation of hook_views_data_alter() invokes this for
 * each field, in the module that defines the field type (as declared in the
 * field array). It is not invoked in other modules.
 *
 * Unlike hook_field_views_data_alter(), this operates on the whole of the views
 * data. This allows a field module to add data that concerns its fields in
 * other tables, which would not yet be defined at the point when
 * hook_field_views_data() and hook_field_views_data_alter() are invoked. For
 * example, entityreference adds reverse relationships on the tables for the
 * entities which are referenced by entityreference fields.
 *
 * (Note: this is weirdly named so as not to conflict with
 * hook_field_views_data_alter().)
 *
 * @param array $data
 *   The views data.
 * @param \Drupal\field\FieldConfigInterface $field
 *   The field config entity.
 *
 * @see hook_field_views_data()
 * @see hook_field_views_data_alter()
 * @see field_views_data_alter()
 */
function hook_field_views_data_views_data_alter(array &$data, \Drupal\field\FieldConfigInterface $field) {
  $field_name = $field->getName();
  $data_key = 'field_data_' . $field_name;
  $entity_type_id = $field->entity_type;
  $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id);
  $pseudo_field_name = 'reverse_' . $field_name . '_' . $entity_type_id;
  list($label) = field_views_field_label($entity_type_id, $field_name);

  // Views data for this field is in $data[$data_key].
  $data[$data_key][$pseudo_field_name]['relationship'] = array(
    'title' => t('@entity using @field', array('@entity' => $entity_type->getLabel(), '@field' => $label)),
    'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity_type->getLabel(), '@field' => $label)),
    'id' => 'entity_reverse',
    'field_name' => $field_name,
    'entity_type' => $entity_type_id,
    'field table' => ContentEntityDatabaseStorage::_fieldTableName($field),
    'field field' => $field_name . '_target_id',
    'base' => $entity_type->getBaseTable(),
    'base field' => $entity_type->getKey('id'),
    'label' => t('!field_name', array('!field_name' => $field_name)),
    'join_extra' => array(
      0 => array(
        'field' => 'deleted',
        'value' => 0,
        'numeric' => TRUE,
      ),
    ),
  );
}

merlinofchaos's avatar
merlinofchaos committed
432
/**
433
 * Replace special strings in the query before it is executed.
merlinofchaos's avatar
merlinofchaos committed
434
 *
435
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
436
 *   The View being executed.
437 438 439 440
 * @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.
merlinofchaos's avatar
merlinofchaos committed
441
 */
442
function hook_views_query_substitutions(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
443 444
  // Example from views_views_query_substitutions().
  return array(
445
    '***CURRENT_VERSION***' => \Drupal::VERSION,
merlinofchaos's avatar
merlinofchaos committed
446
    '***CURRENT_TIME***' => REQUEST_TIME,
447
    '***CURRENT_LANGUAGE***' => \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->id,
448
    '***DEFAULT_LANGUAGE***' => \Drupal::languageManager()->getDefaultLanguage()->id,
merlinofchaos's avatar
merlinofchaos committed
449 450 451 452
  );
}

/**
453
 * Replace special strings when processing a view with form elements.
merlinofchaos's avatar
merlinofchaos committed
454
 *
455 456 457
 * @return array
 *   An associative array where each key is a string to be replaced, and the
 *   corresponding value is its replacement.
merlinofchaos's avatar
merlinofchaos committed
458 459 460 461 462 463 464 465
 */
function hook_views_form_substitutions() {
  return array(
    '<!--views-form-example-substitutions-->' => 'Example Substitution',
  );
}

/**
466
 * Alter a view at the very beginning of Views processing.
merlinofchaos's avatar
merlinofchaos committed
467
 *
468 469 470
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
471
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
472
 *   The view object about to be processed.
473
 * @param string $display_id
merlinofchaos's avatar
merlinofchaos committed
474
 *   The machine name of the active display.
475
 * @param array $args
merlinofchaos's avatar
merlinofchaos committed
476
 *   An array of arguments passed into the view.
477
 *
478
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
479
 */
480 481 482 483 484
function hook_views_pre_view(ViewExecutable $view, $display_id, array &$args) {

  // Modify contextual filters for my_special_view if user has 'my special permission'.
  if ($view->name == 'my_special_view' && user_access('my special permission')) {
    $args[0] = 'custom value';
merlinofchaos's avatar
merlinofchaos committed
485 486 487 488
  }
}

/**
489
 * Act on the view before the query is built, but after displays are attached.
merlinofchaos's avatar
merlinofchaos committed
490
 *
491 492 493
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
494
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
495
 *   The view object about to be processed.
496
 *
497
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
498
 */
499
function hook_views_pre_build(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
500 501 502 503 504 505 506 507 508 509
  // 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);
  }
}

/**
510
 * Act on the view immediately after the query is built.
merlinofchaos's avatar
merlinofchaos committed
511
 *
512 513 514
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
515
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
516
 *   The view object about to be processed.
517
 *
518
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
519
 */
520
function hook_views_post_build(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
521 522 523 524 525 526 527 528 529 530 531 532 533 534
  // 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;
    }
  }
}

/**
535
 * Act on the view after the query is built and just before it is executed.
merlinofchaos's avatar
merlinofchaos committed
536
 *
537 538 539
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
540
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
541
 *   The view object about to be processed.
542
 *
543
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
544
 */
545
function hook_views_pre_execute(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
546 547 548 549 550 551 552 553 554 555
  // 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');
  }
}

/**
556
 * Act on the view immediately after the query has been executed.
merlinofchaos's avatar
merlinofchaos committed
557
 *
558
 * At this point the query has been executed, but the preRender() phase has
559 560 561 562 563
 * not yet happened for handlers.
 *
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
564
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
565
 *   The view object about to be processed.
566
 *
567
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
568
 */
569
function hook_views_post_execute(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
570 571 572 573 574 575 576 577 578 579
  // 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.'));
  }
}

/**
580
 * Act on the view immediately before rendering it.
merlinofchaos's avatar
merlinofchaos committed
581
 *
582
 * At this point the query has been executed, and the preRender() phase has
583 584
 * already happened for handlers, so all data should be available. This hook
 * can be utilized by themes.
merlinofchaos's avatar
merlinofchaos committed
585
 *
586 587 588
 * Output can be added to the view by setting $view->attachment_before
 * and $view->attachment_after.
 *
589
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
590
 *   The view object about to be processed.
591
 *
592
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
593
 */
594
function hook_views_pre_render(ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
595 596 597 598 599 600 601
  // 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);
}

/**
602
 * Post-process any rendered data.
merlinofchaos's avatar
merlinofchaos committed
603 604 605
 *
 * 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
606 607
 * comment-based tokens, and then the post process can replace those tokens.
 * This hook can be utilized by themes.
merlinofchaos's avatar
merlinofchaos committed
608 609 610
 *
 * 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:
611 612 613 614
 * @code
 *   <!--post-FIELD-NID-->
 * @encode
 * And then in the post-render, create an array with the text that should
merlinofchaos's avatar
merlinofchaos committed
615
 * go there:
616 617 618
 * @code
 *   strtr($output, array('<!--post-FIELD-1-->' => 'output for FIELD of nid 1');
 * @encode
merlinofchaos's avatar
merlinofchaos committed
619 620 621
 * All of the cached result data will be available in $view->result, as well,
 * so all ids used in the query should be discoverable.
 *
622
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
623
 *   The view object about to be processed.
624
 * @param string $output
merlinofchaos's avatar
merlinofchaos committed
625
 *   A flat string with the rendered output of the view.
626
 * @param CacheBackendInterface $cache
merlinofchaos's avatar
merlinofchaos committed
627
 *   The cache settings.
628
 *
629
 * @see \Drupal\views\ViewExecutable
merlinofchaos's avatar
merlinofchaos committed
630
 */
631
function hook_views_post_render(ViewExecutable $view, &$output, CacheBackendInterface $cache) {
632 633 634 635 636
  // 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;
merlinofchaos's avatar
merlinofchaos committed
637 638 639 640
  }
}

/**
641
 * Alter the query before it is executed.
merlinofchaos's avatar
merlinofchaos committed
642
 *
643
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
644
 *   The view object about to be processed.
645 646 647
 * @param QueryPluginBase $query
 *   The query plugin object for the query.
 *
merlinofchaos's avatar
merlinofchaos committed
648
 * @see hook_views_query_substitutions()
649
 * @see \Drupal\views\Plugin\views\query\Sql
merlinofchaos's avatar
merlinofchaos committed
650
 */
651
function hook_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
merlinofchaos's avatar
merlinofchaos committed
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
  // (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' => '=',
          );
        }
      }
    }
  }
}

/**
674 675 676 677 678
 * 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.
merlinofchaos's avatar
merlinofchaos committed
679
 *
680
 * @param array $rows
merlinofchaos's avatar
merlinofchaos committed
681
 *   An associative array with two keys:
682
 *   - query: An array of rows suitable for '#type' => 'table', containing
merlinofchaos's avatar
merlinofchaos committed
683
 *     information about the query and the display title and path.
684
 *   - statistics: An array of rows suitable for '#type' => 'table',
685
 *     containing performance statistics.
686
 * @param \Drupal\views\ViewExecutable $view
merlinofchaos's avatar
merlinofchaos committed
687
 *   The view object.
688
 *
689
 * @see \Drupal\views_ui\ViewUI
merlinofchaos's avatar
merlinofchaos committed
690 691
 * @see theme_table()
 */
692
function hook_views_preview_info_alter(array &$rows, ViewExecutable $view) {
merlinofchaos's avatar
merlinofchaos committed
693 694 695 696 697 698 699 700 701
  // 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)) . ')',
  );
}

/**
702 703 704 705
 * 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
706
 *   view edit form. Each entry will be in a form suitable for
707
 *   '#theme' => 'links'.
708 709 710 711 712 713
 * @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()
merlinofchaos's avatar
merlinofchaos committed
714
 */
715
function hook_views_ui_display_top_links_alter(array &$links, ViewExecutable $view, $display_id) {
merlinofchaos's avatar
merlinofchaos committed
716 717 718 719 720 721
  // Put the export link first in the list.
  if (isset($links['export'])) {
    $links = array('export' => $links['export']) + $links;
  }
}

722
// @todo Describe how to alter a view ajax response with event listeners.
merlinofchaos's avatar
merlinofchaos committed
723

724
/**
725
 * Allow modules to respond to the invalidation of the Views cache.
726
 *
727
 * This hook will fire whenever a view is enabled, disabled, created,
728 729 730 731 732
 * updated, or deleted.
 *
 * @see views_invalidate_cache()
 */
function hook_views_invalidate_cache() {
733
  \Drupal\Core\Cache\Cache::invalidateTags(array('views' => TRUE));
734 735
}

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
/**
 * Modify the list of available views access plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_access_alter(array &$plugins) {
  // Remove the available plugin because the users should not have access to it.
  unset($plugins['role']);
}

/**
 * Modify the list of available views default argument plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_argument_default_alter(array &$plugins) {
  // Remove the available plugin because the users should not have access to it.
  unset($plugins['php']);
}

/**
 * Modify the list of available views argument validation plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_argument_validator_alter(array &$plugins) {
  // Remove the available plugin because the users should not have access to it.
  unset($plugins['php']);
}

/**
 * Modify the list of available views cache plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_cache_alter(array &$plugins) {
  // Change the title.
  $plugins['time']['title'] = t('Custom title');
}

/**
 * Modify the list of available views display extender plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_display_extenders_alter(array &$plugins) {
  // Alter the title of an existing plugin.
  $plugins['time']['title'] = t('Custom title');
}

/**
 * Modify the list of available views display plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_display_alter(array &$plugins) {
  // Alter the title of an existing plugin.
  $plugins['rest_export']['title'] = t('Export');
}

/**
 * Modify the list of available views exposed form plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_exposed_form_alter(array &$plugins) {
  // Remove the available plugin because the users should not have access to it.
  unset($plugins['input_required']);
}

/**
 * Modify the list of available views join plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_join_alter(array &$plugins) {
  // Print out all join plugin names for debugging purposes.
  debug($plugins);
}

/**
 * Modify the list of available views join plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_pager_alter(array &$plugins) {
  // Remove the sql based plugin to force good performance.
  unset($plugins['full']);
}

/**
 * Modify the list of available views query plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_query_alter(array &$plugins) {
  // Print out all query plugin names for debugging purposes.
  debug($plugins);
}

/**
 * Modify the list of available views row plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_row_alter(array &$plugins) {
  // Change the used class of a plugin.
  $plugins['entity:node']['class'] = 'Drupal\node\Plugin\views\row\NodeRow';
  $plugins['entity:node']['module'] = 'node';
}

/**
 * Modify the list of available views style plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_style_alter(array &$plugins) {
  // Change the theme hook of a plugin.
  $plugins['html_list']['theme'] = 'custom_views_view_list';
}

/**
 * Modify the list of available views wizard plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param array $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see \Drupal\views\Plugin\ViewsPluginManager
 */
function hook_views_plugins_wizard_alter(array &$plugins) {
  // Change the title of a plugin.
  $plugins['node_revision']['title'] = t('Node revision wizard');
}

merlinofchaos's avatar
merlinofchaos committed
945 946 947
/**
 * @}
 */