entity.api.php 36.5 KB
Newer Older
1 2 3 4 5 6 7
<?php

/**
 * @file
 * Hooks provided the Entity module.
 */

8
use Drupal\Component\Utility\String;
9
use Drupal\Core\Entity\ContentEntityInterface;
10
use Drupal\Core\Field\FieldDefinition;
11
use Drupal\Core\Render\Element;
12

13 14 15 16 17
/**
 * @addtogroup hooks
 * @{
 */

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/**
 * Control entity operation access.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity to check access to.
 * @param string $operation
 *   The operation that is to be performed on $entity.
 * @param \Drupal\Core\Session\AccountInterface $account
 *    The account trying to access the entity.
 * @param string $langcode
 *    The code of the language $entity is accessed in.
 *
 * @return bool|null
 *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
 *   deny access.
 *
 * @see \Drupal\Core\Entity\EntityAccessController
 */
function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) {
  return NULL;
}

/**
 * Control entity operation access for a specific entity type.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity to check access to.
 * @param string $operation
 *   The operation that is to be performed on $entity.
 * @param \Drupal\Core\Session\AccountInterface $account
 *    The account trying to access the entity.
 * @param string $langcode
 *    The code of the language $entity is accessed in.
 *
 * @return bool|null
 *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
 *   deny access.
 *
 * @see \Drupal\Core\Entity\EntityAccessController
 */
function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account, $langcode) {
  return NULL;
}

/**
 * Control entity create access.
 *
 * @param \Drupal\Core\Session\AccountInterface $account
 *    The account trying to access the entity.
 * @param string $langcode
 *    The code of the language $entity is accessed in.
 *
 * @return bool|null
 *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
 *   deny access.
 *
 * @see \Drupal\Core\Entity\EntityAccessController
 */
function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) {
  return NULL;
}

/**
 * Control entity create access for a specific entity type.
 *
 * @param \Drupal\Core\Session\AccountInterface $account
 *    The account trying to access the entity.
 * @param string $langcode
 *    The code of the language $entity is accessed in.
 *
 * @return bool|null
 *   A boolean to explicitly allow or deny access, or NULL to neither allow nor
 *   deny access.
 *
 * @see \Drupal\Core\Entity\EntityAccessController
 */
function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) {
  return NULL;
}

98
/**
99 100
 * Add to entity type definitions.
 *
101 102
 * Modules may implement this hook to add information to defined entity types,
 * as defined in \Drupal\Core\Entity\EntityTypeInterface.
103
 *
104
 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
105 106 107 108
 *   An associative array of all entity type definitions, keyed by the entity
 *   type name. Passed by reference.
 *
 * @see \Drupal\Core\Entity\Entity
109
 * @see \Drupal\Core\Entity\EntityTypeInterface
110
 */
111 112
function hook_entity_type_build(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
113
  // Add a form for a custom node form without overriding the default
114
  // node form. To override the default node form, use hook_entity_type_alter().
115
  $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
116 117
}

118 119 120 121 122 123 124 125 126
/**
 * Alter the entity type definitions.
 *
 * Modules may implement this hook to alter the information that defines an
 * entity type. All properties that are available in
 * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally
 * provided by modules can be altered here.
 *
 * Do not use this hook to add information to entity types, unless you are just
127
 * filling-in default values. Use hook_entity_type_build() instead.
128
 *
129
 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
130 131 132 133 134 135
 *   An associative array of all entity type definitions, keyed by the entity
 *   type name. Passed by reference.
 *
 * @see \Drupal\Core\Entity\Entity
 * @see \Drupal\Core\Entity\EntityTypeInterface
 */
136 137
function hook_entity_type_alter(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
138
  // Set the controller class for nodes to an alternate implementation of the
139 140
  // Drupal\Core\Entity\EntityStorageInterface interface.
  $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
141 142
}

143 144 145 146 147 148
/**
 * Alter the view modes for entity types.
 *
 * @param array $view_modes
 *   An array of view modes, keyed first by entity type, then by view mode name.
 *
149 150
 * @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
 * @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
151 152 153
 * @see hook_entity_view_mode_info()
 */
function hook_entity_view_mode_info_alter(&$view_modes) {
154
  $view_modes['user']['full']['status'] = TRUE;
155 156 157 158 159 160 161 162 163 164 165 166 167
}

/**
 * Describe the bundles for entity types.
 *
 * @return array
 *   An associative array of all entity bundles, keyed by the entity
 *   type name, and then the bundle name, with the following keys:
 *   - label: The human-readable name of the bundle.
 *   - uri_callback: The same as the 'uri_callback' key defined for the entity
 *     type in the EntityManager, but for the bundle only. When determining
 *     the URI of an entity, if a 'uri_callback' is defined for both the
 *     entity type and the bundle, the one for the bundle is used.
168 169
 *   - translatable: (optional) A boolean value specifying whether this bundle
 *     has translation support enabled. Defaults to FALSE.
170 171 172 173 174
 *
 * @see entity_get_bundles()
 * @see hook_entity_bundle_info_alter()
 */
function hook_entity_bundle_info() {
175
  $bundles['user']['user']['label'] = t('User');
176 177 178 179 180 181 182 183 184 185 186 187 188 189
  return $bundles;
}

/**
 * Alter the bundles for entity types.
 *
 * @param array $bundles
 *   An array of bundles, keyed first by entity type, then by bundle name.
 *
 * @see entity_get_bundles()
 * @see hook_entity_bundle_info()
 */
function hook_entity_bundle_info_alter(&$bundles) {
  $bundles['user']['user']['label'] = t('Full account');
190 191
}

192 193 194 195 196
/**
 * Act on entity_bundle_create().
 *
 * This hook is invoked after the operation has been performed.
 *
197
 * @param string $entity_type_id
198 199 200 201
 *   The type of $entity; e.g. 'node' or 'user'.
 * @param string $bundle
 *   The name of the bundle.
 */
202
function hook_entity_bundle_create($entity_type_id, $bundle) {
203 204
  // When a new bundle is created, the menu needs to be rebuilt to add the
  // Field UI menu item tabs.
205
  \Drupal::service('router.builder')->setRebuildNeeded();
206 207 208 209 210 211 212
}

/**
 * Act on entity_bundle_rename().
 *
 * This hook is invoked after the operation has been performed.
 *
213
 * @param string $entity_type_id
214 215 216 217 218 219
 *   The entity type to which the bundle is bound.
 * @param string $bundle_old
 *   The previous name of the bundle.
 * @param string $bundle_new
 *   The new name of the bundle.
 */
220
function hook_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) {
221
  // Update the settings associated with the bundle in my_module.settings.
222
  $config = \Drupal::config('my_module.settings');
223
  $bundle_settings = $config->get('bundle_settings');
224 225 226
  if (isset($bundle_settings[$entity_type_id][$bundle_old])) {
    $bundle_settings[$entity_type_id][$bundle_new] = $bundle_settings[$entity_type_id][$bundle_old];
    unset($bundle_settings[$entity_type_id][$bundle_old]);
227 228 229 230 231 232 233 234 235
    $config->set('bundle_settings', $bundle_settings);
  }
}

/**
 * Act on entity_bundle_delete().
 *
 * This hook is invoked after the operation has been performed.
 *
236
 * @param string $entity_type_id
237 238 239 240
 *   The type of entity; for example, 'node' or 'user'.
 * @param string $bundle
 *   The bundle that was just deleted.
 */
241
function hook_entity_bundle_delete($entity_type_id, $bundle) {
242
  // Remove the settings associated with the bundle in my_module.settings.
243
  $config = \Drupal::config('my_module.settings');
244
  $bundle_settings = $config->get('bundle_settings');
245 246
  if (isset($bundle_settings[$entity_type_id][$bundle])) {
    unset($bundle_settings[$entity_type_id][$bundle]);
247 248 249 250
    $config->set('bundle_settings', $bundle_settings);
  }
}

251 252 253 254 255 256 257 258 259 260
/**
 * Act on a newly created entity.
 *
 * This hook runs after a new entity object has just been instantiated. It can
 * be used to set initial values, e.g. to provide defaults.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity object.
 */
function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
261
  if ($entity instanceof ContentEntityInterface && !$entity->foo->value) {
262 263 264 265
    $entity->foo->value = 'some_initial_value';
  }
}

266 267 268 269 270 271
/**
 * Act on entities when loaded.
 *
 * This is a generic load hook called for all entity types loaded via the
 * entity API.
 *
272
 * @param array $entities
273
 *   The entities keyed by entity ID.
274
 * @param string $entity_type_id
275 276
 *   The type of entities being loaded (i.e. node, user, comment).
 */
277
function hook_entity_load($entities, $entity_type_id) {
278
  foreach ($entities as $entity) {
279
    $entity->foo = mymodule_add_something($entity);
280 281 282 283 284 285
  }
}

/**
 * Act on an entity before it is about to be created or updated.
 *
286
 * @param \Drupal\Core\Entity\EntityInterface $entity
287 288
 *   The entity object.
 */
289
function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
290 291 292 293
  $entity->changed = REQUEST_TIME;
}

/**
294 295 296 297
 * Respond to creation of a new entity.
 *
 * This hook runs once the entity has been stored. Note that hook
 * implementations may not alter the stored entity data.
298
 *
299
 * @param \Drupal\Core\Entity\EntityInterface $entity
300 301
 *   The entity object.
 */
302
function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
303 304 305
  // Insert the new entity into a fictional table of all entities.
  db_insert('example_entity')
    ->fields(array(
306
      'type' => $entity->getEntityTypeId(),
307
      'id' => $entity->id(),
308 309 310 311 312 313 314
      'created' => REQUEST_TIME,
      'updated' => REQUEST_TIME,
    ))
    ->execute();
}

/**
315 316 317 318
 * Respond to updates to an entity.
 *
 * This hook runs once the entity storage has been updated. Note that hook
 * implementations may not alter the stored entity data.
319
 *
320
 * @param \Drupal\Core\Entity\EntityInterface $entity
321 322
 *   The entity object.
 */
323
function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
324 325 326 327 328
  // Update the entity's entry in a fictional table of all entities.
  db_update('example_entity')
    ->fields(array(
      'updated' => REQUEST_TIME,
    ))
329
    ->condition('type', $entity->getEntityTypeId())
330
    ->condition('id', $entity->id())
331 332 333
    ->execute();
}

334
/**
335 336 337 338
 * Respond to creation of a new entity translation.
 *
 * This hook runs once the entity translation has been stored. Note that hook
 * implementations may not alter the stored entity translation data.
339 340 341 342 343 344 345 346 347
 *
 * @param \Drupal\Core\Entity\EntityInterface $translation
 *   The entity object of the translation just stored.
 */
function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
  $variables = array(
    '@language' => $translation->language()->name,
    '@label' => $translation->getUntranslated()->label(),
  );
348
  \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
349 350 351
}

/**
352 353 354
 * Respond to entity translation deletion.
 *
 * This hook runs once the entity translation has been deleted from storage.
355 356 357 358 359
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The original entity object.
 */
function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
360
  $languages = \Drupal::languageManager()->getLanguages();
361 362 363 364
  $variables = array(
    '@language' => $languages[$langcode]->name,
    '@label' => $entity->label(),
  );
365
  \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
366 367
}

368
/**
369 370
 * Act before entity deletion.
 *
371
 * @param \Drupal\Core\Entity\EntityInterface $entity
372 373
 *   The entity object for the entity that is about to be deleted.
 */
374
function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
375 376
  // Count references to this entity in a custom table before they are removed
  // upon entity deletion.
377
  $id = $entity->id();
378
  $type = $entity->getEntityTypeId();
379 380 381 382 383 384 385 386
  $count = db_select('example_entity_data')
    ->condition('type', $type)
    ->condition('id', $id)
    ->countQuery()
    ->execute()
    ->fetchField();

  // Log the count in a table that records this statistic for deleted entities.
387 388 389 390
  db_merge('example_deleted_entity_statistics')
    ->key(array('type' => $type, 'id' => $id))
    ->fields(array('count' => $count))
    ->execute();
391 392 393 394 395
}

/**
 * Respond to entity deletion.
 *
396
 * This hook runs once the entity has been deleted from the storage.
397
 *
398
 * @param \Drupal\Core\Entity\EntityInterface $entity
399
 *   The entity object for the entity that has been deleted.
400
 */
401
function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
402 403
  // Delete the entity's entry from a fictional table of all entities.
  db_delete('example_entity')
404
    ->condition('type', $entity->getEntityTypeId())
405
    ->condition('id', $entity->id())
406 407 408
    ->execute();
}

409 410 411
/**
 * Respond to entity revision deletion.
 *
412
 * This hook runs once the entity revision has been deleted from the storage.
413
 *
414
 * @param \Drupal\Core\Entity\EntityInterface $entity
415 416 417 418 419 420
 *   The entity object for the entity revision that has been deleted.
 */
function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
  // @todo: code example
}

421
/**
422
 * Alter or execute an Drupal\Core\Entity\Query\EntityQueryInterface.
423
 *
424
 * @param \Drupal\Core\Entity\Query\QueryInterface $query
425 426 427 428 429 430 431
 *   Note the $query->altered attribute which is TRUE in case the query has
 *   already been altered once. This happens with cloned queries.
 *   If there is a pager, then such a cloned query will be executed to count
 *   all elements. This query can be detected by checking for
 *   ($query->pager && $query->count), allowing the driver to return 0 from
 *   the count query and disable the pager.
 */
432 433
function hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query) {
  // @todo: code example.
434 435 436 437 438
}

/**
 * Act on entities being assembled before rendering.
 *
439 440
 * @param &$build
 *   A renderable array representing the entity content.
441
 * @param \Drupal\Core\Entity\EntityInterface $entity
442
 *   The entity object.
443
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
444
 *   The entity view display holding the display options configured for the
445
 *   entity components.
446 447 448 449 450
 * @param $view_mode
 *   The view mode the entity is rendered in.
 * @param $langcode
 *   The language code used for rendering.
 *
451 452
 * The module may add elements to $build prior to rendering. The
 * structure of $build is a renderable array as expected by
453 454 455 456 457 458 459
 * drupal_render().
 *
 * @see hook_entity_view_alter()
 * @see hook_comment_view()
 * @see hook_node_view()
 * @see hook_user_view()
 */
460
function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) {
461 462
  // Only do the extra work if the component is configured to be displayed.
  // This assumes a 'mymodule_addition' extra field has been defined for the
463
  // entity bundle in hook_entity_extra_field_info().
464
  if ($display->getComponent('mymodule_addition')) {
465
    $build['mymodule_addition'] = array(
466 467 468 469
      '#markup' => mymodule_addition($entity),
      '#theme' => 'mymodule_my_additional_field',
    );
  }
470 471 472 473 474 475 476 477 478 479 480
}

/**
 * Alter the results of ENTITY_view().
 *
 * This hook is called after the content has been assembled in a structured
 * array and may be used for doing processing which requires that the complete
 * entity content structure has been built.
 *
 * If a module wishes to act on the rendered HTML of the entity rather than the
 * structured content array, it may use this hook to add a #post_render
481
 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
482
 * the particular entity type template, if there is one (e.g., node.html.twig).
483
 * See drupal_render() and _theme() for details.
484
 *
485
 * @param array &$build
486
 *   A renderable array representing the entity content.
487
 * @param \Drupal\Core\Entity\EntityInterface $entity
488
 *   The entity object being rendered.
489
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
490
 *   The entity view display holding the display options configured for the
491
 *   entity components.
492 493 494 495 496 497 498
 *
 * @see hook_entity_view()
 * @see hook_comment_view_alter()
 * @see hook_node_view_alter()
 * @see hook_taxonomy_term_view_alter()
 * @see hook_user_view_alter()
 */
499
function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
500 501 502 503 504 505 506 507 508 509 510 511 512
  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
    // Change its weight.
    $build['an_additional_field']['#weight'] = -10;

    // Add a #post_render callback to act on the rendered HTML of the entity.
    $build['#post_render'][] = 'my_module_node_post_render';
  }
}

/**
 * Act on entities as they are being prepared for view.
 *
 * Allows you to operate on multiple entities as they are being prepared for
513
 * view. Only use this if attaching the data during the entity loading phase
514 515
 * is not appropriate, for example when attaching other 'entity' style objects.
 *
516
 * @param string $entity_type_id
517
 *   The type of entities being viewed (i.e. node, user, comment).
518 519
 * @param array $entities
 *   The entities keyed by entity ID.
520
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
521
 *   The array of entity view displays holding the display options configured
522 523 524
 *   for the entity components, keyed by bundle name.
 * @param string $view_mode
 *   The view mode.
525
 */
526
function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
527
  // Load a specific node into the user object for later theming.
528
  if (!empty($entities) && $entity_type_id == 'user') {
529 530
    // Only do the extra work if the component is configured to be
    // displayed. This assumes a 'mymodule_addition' extra field has been
531
    // defined for the entity bundle in hook_entity_extra_field_info().
532 533 534 535 536 537 538 539 540 541 542
    $ids = array();
    foreach ($entities as $id => $entity) {
      if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) {
        $ids[] = $id;
      }
    }
    if ($ids) {
      $nodes = mymodule_get_user_nodes($ids);
      foreach ($ids as $id) {
        $entities[$id]->user_node = $nodes[$id];
      }
543 544 545
    }
  }
}
546 547 548 549 550 551

/**
 * Change the view mode of an entity that is being displayed.
 *
 * @param string $view_mode
 *   The view_mode that is to be used to display the entity.
552
 * @param \Drupal\Core\Entity\EntityInterface $entity
553 554 555 556 557
 *   The entity that is being viewed.
 * @param array $context
 *   Array with additional context information, currently only contains the
 *   langcode the entity is viewed in.
 */
558
function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
559
  // For nodes, change the view mode when it is teaser.
560
  if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
561 562 563
    $view_mode = 'my_custom_view_mode';
  }
}
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
/**
 * Alters entity renderable values before cache checking in drupal_render().
 *
 * Invoked for a specific entity type.
 *
 * The values in the #cache key of the renderable array are used to determine if
 * a cache entry exists for the entity's rendered output. Ideally only values
 * that pertain to caching should be altered in this hook.
 *
 * @param array &$build
 *   A renderable array containing the entity's caching and view mode values.
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity that is being viewed.
 * @param string $view_mode
 *   The view_mode that is to be used to display the entity.
 * @param string $langcode
 *   The code of the language $entity is accessed in.
 *
 * @see drupal_render()
 * @see \Drupal\Core\Entity\EntityViewBuilder
 */
function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode, $langcode) {

}

/**
 * Alters entity renderable values before cache checking in drupal_render().
 *
 * The values in the #cache key of the renderable array are used to determine if
 * a cache entry exists for the entity's rendered output. Ideally only values
 * that pertain to caching should be altered in this hook.
 *
 * @param array &$build
 *   A renderable array containing the entity's caching and view mode values.
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity that is being viewed.
 * @param string $view_mode
 *   The view_mode that is to be used to display the entity.
 * @param string $langcode
 *   The code of the language $entity is accessed in.
 *
 * @see drupal_render()
 * @see \Drupal\Core\Entity\EntityViewBuilder
 */
function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode, $langcode) {

}

613 614 615
/**
 * Alters the settings used for displaying an entity.
 *
616
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
617
 *   The entity view display that will be used to display the entity
618 619 620 621 622 623 624
 *   components.
 * @param array $context
 *   An associative array containing:
 *   - entity_type: The entity type, e.g., 'node' or 'user'.
 *   - bundle: The bundle, e.g., 'page' or 'article'.
 *   - view_mode: The view mode, e.g. 'full', 'teaser'...
 */
625
function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
626 627
  // Leave field labels out of the search index.
  if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
628 629 630 631
    foreach ($display->getComponents() as $name => $options) {
      if (isset($options['label'])) {
        $options['label'] = 'hidden';
        $display->setComponent($name, $options);
632 633 634 635 636
      }
    }
  }
}

637 638 639 640 641 642 643 644 645 646 647 648 649
/**
 * Alter the render array generated by an EntityDisplay for an entity.
 *
 * @param array $build
 *   The renderable array generated by the EntityDisplay.
 * @param array $context
 *   An associative array containing:
 *   - entity: The entity being rendered.
 *   - view_mode: The view mode; for example, 'full' or 'teaser'.
 *   - display: The EntityDisplay holding the display options.
 */
function hook_entity_display_build_alter(&$build, $context) {
  // Append RDF term mappings on displayed taxonomy links.
650
  foreach (Element::children($build) as $field_name) {
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
    $element = &$build[$field_name];
    if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') {
      foreach ($element['#items'] as $delta => $item) {
        $term = $item->entity;
        if (!empty($term->rdf_mapping['rdftype'])) {
          $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
        }
        if (!empty($term->rdf_mapping['name']['predicates'])) {
          $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
        }
      }
    }
  }
}

666 667 668 669 670 671 672 673 674 675 676 677 678 679
/**
 * Acts on an entity object about to be shown on an entity form.
 *
 * This can be typically used to pre-fill entity values or change the form state
 * before the entity form is built. It is invoked just once when first building
 * the entity form. Rebuilds will not trigger a new invocation.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity that is about to be shown on the form.
 * @param $operation
 *   The current operation.
 * @param array $form_state
 *   An associative array containing the current state of the form.
 *
680
 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
681
 */
682
function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, array &$form_state) {
683 684 685 686 687 688
  if ($operation == 'edit') {
    $entity->label->value = 'Altered label';
    $form_state['mymodule']['label_altered'] = TRUE;
  }
}

689 690 691
/**
 * Alters the settings used for displaying an entity form.
 *
692
 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
693 694 695 696 697 698 699 700
 *   The entity_form_display object that will be used to display the entity form
 *   components.
 * @param array $context
 *   An associative array containing:
 *   - entity_type: The entity type, e.g., 'node' or 'user'.
 *   - bundle: The bundle, e.g., 'page' or 'article'.
 *   - form_mode: The form mode, e.g. 'default', 'profile', 'register'...
 */
701
function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
702 703 704 705 706 707 708 709
  // Hide the 'user_picture' field from the register form.
  if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
    $form_display->setComponent('user_picture', array(
      'type' => 'hidden',
    ));
  }
}

710
/**
711
 * Provides custom base field definitions for a content entity type.
712
 *
713 714
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
715
 *
716 717 718 719 720 721
 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
 *   An array of field definitions, keyed by field name.
 *
 * @see hook_entity_base_field_info_alter()
 * @see hook_entity_bundle_field_info()
 * @see hook_entity_bundle_field_info_alter()
722
 * @see \Drupal\Core\Field\FieldDefinitionInterface
723
 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
724
 */
725 726 727 728
function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'node') {
    $fields = array();
    $fields['mymodule_text'] = FieldDefinition::create('string')
729 730 731 732 733
      ->setLabel(t('The text'))
      ->setDescription(t('A text property added by mymodule.'))
      ->setComputed(TRUE)
      ->setClass('\Drupal\mymodule\EntityComputedText');

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
    return $fields;
  }
}

/**
 * Alters base field definitions for a content entity type.
 *
 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
 *   The array of base field definitions for the entity type.
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
 *
 * @see hook_entity_base_field_info()
 * @see hook_entity_bundle_field_info()
 * @see hook_entity_bundle_field_info_alter()
 */
function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  // Alter the mymodule_text field to use a custom class.
  if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
    $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
  }
}

/**
 * Provides field definitions for a specific bundle within an entity type.
 *
760 761 762 763
 * Bundle fields either have to override an existing base field, or need to
 * provide a field storage definition via hook_entity_field_storage_info()
 * unless they are computed.
 *
764 765 766 767 768 769 770 771 772 773 774 775
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
 * @param string $bundle
 *   The bundle.
 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
 *   The list of base field definitions for the entity type.
 *
 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
 *   An array of bundle field definitions, keyed by field name.
 *
 * @see hook_entity_base_field_info()
 * @see hook_entity_base_field_info_alter()
776 777
 * @see hook_entity_field_storage_info()
 * @see hook_entity_field_storage_info_alter()
778 779 780 781 782 783 784 785 786
 * @see hook_entity_bundle_field_info_alter()
 * @see \Drupal\Core\Field\FieldDefinitionInterface
 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
 */
function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  // Add a property only to nodes of the 'article' bundle.
  if ($entity_type->id() == 'node' && $bundle == 'article') {
    $fields = array();
    $fields['mymodule_text_more'] = FieldDefinition::create('string')
787 788 789
        ->setLabel(t('More text'))
        ->setComputed(TRUE)
        ->setClass('\Drupal\mymodule\EntityComputedMoreText');
790
    return $fields;
791 792 793 794
  }
}

/**
795
 * Alters bundle field definitions.
796
 *
797 798 799 800 801 802
 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
 *   The array of bundle field definitions.
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
 * @param string $bundle
 *   The bundle.
803
 *
804 805 806
 * @see hook_entity_base_field_info()
 * @see hook_entity_base_field_info_alter()
 * @see hook_entity_bundle_field_info()
807
 */
808 809
function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
  if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
810
    // Alter the mymodule_text field to use a custom class.
811
    $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
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
/**
 * Provides field storage definitions for a content entity type.
 *
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
 *
 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
 *   An array of field storage definitions, keyed by field name.
 *
 * @see hook_entity_field_storage_info_alter()
 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface
 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldStorageDefinitions()
 */
function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  // Expose storage definitions for all exposed bundle fields.
  if ($entity_type->isFieldable()) {
    // Query by filtering on the ID as this is more efficient than filtering
    // on the entity_type property directly.
    $ids = \Drupal::entityQuery('field_config')
      ->condition('id', $entity_type->id() . '.', 'STARTS_WITH')
      ->execute();

    // Fetch all fields and key them by field name.
    $field_configs = entity_load_multiple('field_config', $ids);
    $result = array();
    foreach ($field_configs as $field_config) {
      $result[$field_config->getName()] = $field_config;
    }
    return $result;
  }
}

/**
 * Alters field storage definitions for a content entity type.
 *
 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields
 *   The array of field storage definitions for the entity type.
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
 *
 * @see hook_entity_field_storage_info()
 */
function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  // Alter the max_length setting.
  if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
    $fields['mymodule_text']->setSetting('max_length', 128);
  }
}

864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
/**
 * Declares entity operations.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity on which the linked operations will be performed.
 *
 * @return array
 *   An operations array as returned by
 *   \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
 */
function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
  $operations = array();
  $operations['translate'] = array(
    'title' => t('Translate'),
    'route_name' => 'foo_module.entity.translate',
    'weight' => 50,
  );

  return $operations;
}

885 886 887 888 889
/**
 * Alter entity operations.
 *
 * @param array $operations
 *   Operations array as returned by
890
 *   \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
891 892 893 894
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity on which the linked operations will be performed.
 */
function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) {
895 896 897 898 899
  // Alter the title and weight.
  $operations['translate']['title'] = t('Translate @entity_type', array(
    '@entity_type' => $entity->getEntityTypeId(),
  ));
  $operations['translate']['weight'] = 99;
900 901
}

902 903 904
/**
 * Control access to fields.
 *
905
 * This hook is invoked from
906
 * \Drupal\Core\Entity\EntityAccessController::fieldAccess() to let modules
907
 * grant or deny operations on fields.
908 909 910
 *
 * @param string $operation
 *   The operation to be performed. See
911
 *   \Drupal\Core\Access\AccessibleInterface::access() for possible values.
912
 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
913
 *   The field definition.
914
 * @param \Drupal\Core\Session\AccountInterface $account
915
 *   The user account to check.
916
 * @param \Drupal\Core\Field\FieldItemListInterface $items
917 918
 *   (optional) The entity field object on which the operation is to be
 *   performed.
919
 *
920
 * @return bool|null
921 922 923
 *   TRUE if access should be allowed, FALSE if access should be denied and NULL
 *   if the implementation has no opinion.
 */
924
function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
925
  if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
926 927 928 929 930
    return user_access('update field of interest', $account);
  }
}

/**
931
 * Alters the default access behavior for a given field.
932 933 934 935 936 937 938 939 940 941 942
 *
 * Use this hook to override access grants from another module. Note that the
 * original default access flag is masked under the ':default' key.
 *
 * @param array $grants
 *   An array of grants gathered by hook_entity_field_access(). The array is
 *   keyed by the module that defines the field's access control; the values are
 *   grant responses for each module (Boolean or NULL).
 * @param array $context
 *   Context array on the performed operation with the following keys:
 *   - operation: The operation to be performed (string).
943
 *   - field_definition: The field definition object
944
 *     (\Drupal\Core\Field\FieldDefinitionInterface)
945
 *   - account: The user account to check access for
946
 *     (Drupal\user\Entity\User).
947
 *   - items: (optional) The entity field items
948
 *     (\Drupal\Core\Field\FieldItemListInterface).
949 950
 */
function hook_entity_field_access_alter(array &$grants, array $context) {
951
  /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
952
  $field_definition = $context['field_definition'];
953
  if ($field_definition->getName() == 'field_of_interest' && $grants['node'] === FALSE) {
954 955 956 957 958 959 960 961
    // Override node module's restriction to no opinion. We don't want to
    // provide our own access hook, we only want to take out node module's part
    // in the access handling of this field. We also don't want to switch node
    // module's grant to TRUE, because the grants of other modules should still
    // decide on their own if this field is accessible or not.
    $grants['node'] = NULL;
  }
}
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029

/**
 * Exposes "pseudo-field" components on content entities.
 *
 * Field UI's "Manage fields" and "Manage display" pages let users re-order
 * fields, but also non-field components. For nodes, these include elements
 * exposed by modules through hook_form_alter(), for instance.
 *
 * Content entities or modules that want to have their components supported
 * should expose them using this hook. The user-defined settings (weight,
 * visible) are automatically applied when entities or entity forms are
 * rendered.
 *
 * @see hook_entity_extra_field_info_alter()
 *
 * @return array
 *   The array structure is identical to that of the return value of
 *   \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
 */
function hook_entity_extra_field_info() {
  $extra = array();
  $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
  $description = t('Node module element');

  foreach (node_type_get_types() as $bundle) {

    // Add also the 'language' select if Language module is enabled and the
    // bundle has multilingual support.
    // Visibility of the ordering of the language selector is the same as on the
    // node/add form.
    if ($module_language_enabled) {
      $configuration = language_get_default_configuration('node', $bundle->type);
      if ($configuration['language_show']) {
        $extra['node'][$bundle->type]['form']['language'] = array(
          'label' => t('Language'),
          'description' => $description,
          'weight' => 0,
        );
      }
    }
    $extra['node'][$bundle->type]['display']['language'] = array(
      'label' => t('Language'),
      'description' => $description,
      'weight' => 0,
      'visible' => FALSE,
    );
  }

  return $extra;
}

/**
 * Alter "pseudo-field" components on content entities.
 *
 * @param array $info
 *   The array structure is identical to that of the return value of
 *   \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
 *
 * @see hook_entity_extra_field_info()
 */
function hook_entity_extra_field_info_alter(&$info) {
  // Force node title to always be at the top of the list by default.
  foreach (node_type_get_types() as $bundle) {
    if (isset($info['node'][$bundle->type]['form']['title'])) {
      $info['node'][$bundle->type]['form']['title']['weight'] = -20;
    }
  }
}