entity.api.php 32.1 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

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

17 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
/**
 * 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;
}

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

117 118 119 120 121 122 123 124 125
/**
 * 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
126
 * filling-in default values. Use hook_entity_type_build() instead.
127
 *
128
 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
129 130 131 132 133 134
 *   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
 */
135 136
function hook_entity_type_alter(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
137
  // Set the controller class for nodes to an alternate implementation of the
138 139
  // Drupal\Core\Entity\EntityStorageInterface interface.
  $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
140 141
}

142 143 144 145 146 147
/**
 * 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.
 *
148 149
 * @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
 * @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
150 151 152
 * @see hook_entity_view_mode_info()
 */
function hook_entity_view_mode_info_alter(&$view_modes) {
153
  $view_modes['user']['full']['status'] = TRUE;
154 155 156 157 158 159 160 161 162 163 164 165 166
}

/**
 * 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.
167 168
 *   - translatable: (optional) A boolean value specifying whether this bundle
 *     has translation support enabled. Defaults to FALSE.
169 170 171 172 173
 *
 * @see entity_get_bundles()
 * @see hook_entity_bundle_info_alter()
 */
function hook_entity_bundle_info() {
174
  $bundles['user']['user']['label'] = t('User');
175 176 177 178 179 180 181 182 183 184 185 186 187 188
  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');
189 190
}

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

/**
 * Act on entity_bundle_rename().
 *
 * This hook is invoked after the operation has been performed.
 *
212
 * @param string $entity_type_id
213 214 215 216 217 218
 *   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.
 */
219
function hook_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) {
220
  // Update the settings associated with the bundle in my_module.settings.
221
  $config = \Drupal::config('my_module.settings');
222
  $bundle_settings = $config->get('bundle_settings');
223 224 225
  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]);
226 227 228 229 230 231 232 233 234
    $config->set('bundle_settings', $bundle_settings);
  }
}

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

250 251 252 253 254 255 256 257 258 259
/**
 * 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) {
260
  if ($entity instanceof ContentEntityInterface && !$entity->foo->value) {
261 262 263 264
    $entity->foo->value = 'some_initial_value';
  }
}

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

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

/**
293 294 295 296
 * 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.
297
 *
298
 * @param \Drupal\Core\Entity\EntityInterface $entity
299 300
 *   The entity object.
 */
301
function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
302 303 304
  // Insert the new entity into a fictional table of all entities.
  db_insert('example_entity')
    ->fields(array(
305
      'type' => $entity->getEntityTypeId(),
306
      'id' => $entity->id(),
307 308 309 310 311 312 313
      'created' => REQUEST_TIME,
      'updated' => REQUEST_TIME,
    ))
    ->execute();
}

/**
314 315 316 317
 * 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.
318
 *
319
 * @param \Drupal\Core\Entity\EntityInterface $entity
320 321
 *   The entity object.
 */
322
function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
323 324 325 326 327
  // Update the entity's entry in a fictional table of all entities.
  db_update('example_entity')
    ->fields(array(
      'updated' => REQUEST_TIME,
    ))
328
    ->condition('type', $entity->getEntityTypeId())
329
    ->condition('id', $entity->id())
330 331 332
    ->execute();
}

333
/**
334 335 336 337
 * 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.
338 339 340 341 342 343 344 345 346 347 348 349 350
 *
 * @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(),
  );
  watchdog('example', 'The @language translation of @label has just been stored.', $variables);
}

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

367
/**
368 369
 * Act before entity deletion.
 *
370
 * @param \Drupal\Core\Entity\EntityInterface $entity
371 372
 *   The entity object for the entity that is about to be deleted.
 */
373
function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
374 375
  // Count references to this entity in a custom table before they are removed
  // upon entity deletion.
376
  $id = $entity->id();
377
  $type = $entity->getEntityTypeId();
378 379 380 381 382 383 384 385
  $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.
386 387 388 389
  db_merge('example_deleted_entity_statistics')
    ->key(array('type' => $type, 'id' => $id))
    ->fields(array('count' => $count))
    ->execute();
390 391 392 393 394
}

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

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

420
/**
421
 * Alter or execute an Drupal\Core\Entity\Query\EntityQueryInterface.
422
 *
423
 * @param \Drupal\Core\Entity\Query\QueryInterface $query
424 425 426 427 428 429 430
 *   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.
 */
431 432
function hook_entity_query_alter(\Drupal\Core\Entity\Query\QueryInterface $query) {
  // @todo: code example.
433 434 435 436 437
}

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

/**
 * 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
478
 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
479
 * the particular entity type template, if there is one (e.g., node.html.twig).
480
 * See drupal_render() and _theme() for details.
481 482 483
 *
 * @param $build
 *   A renderable array representing the entity content.
484
 * @param \Drupal\Core\Entity\EntityInterface $entity
485
 *   The entity object being rendered.
486
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
487
 *   The entity view display holding the display options configured for the
488
 *   entity components.
489 490 491 492 493 494 495
 *
 * @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()
 */
496
function hook_entity_view_alter(&$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
497 498 499 500 501 502 503 504 505 506 507 508 509
  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
510
 * view. Only use this if attaching the data during the entity loading phase
511 512
 * is not appropriate, for example when attaching other 'entity' style objects.
 *
513
 * @param string $entity_type_id
514
 *   The type of entities being viewed (i.e. node, user, comment).
515 516
 * @param array $entities
 *   The entities keyed by entity ID.
517
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
518
 *   The array of entity view displays holding the display options configured
519 520 521
 *   for the entity components, keyed by bundle name.
 * @param string $view_mode
 *   The view mode.
522
 */
523
function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
524
  // Load a specific node into the user object for later theming.
525
  if (!empty($entities) && $entity_type_id == 'user') {
526 527
    // Only do the extra work if the component is configured to be
    // displayed. This assumes a 'mymodule_addition' extra field has been
528
    // defined for the entity bundle in hook_entity_extra_field_info().
529 530 531 532 533 534 535 536 537 538 539
    $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];
      }
540 541 542
    }
  }
}
543 544 545 546 547 548

/**
 * 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.
549
 * @param \Drupal\Core\Entity\EntityInterface $entity
550 551 552 553 554
 *   The entity that is being viewed.
 * @param array $context
 *   Array with additional context information, currently only contains the
 *   langcode the entity is viewed in.
 */
555
function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
556
  // For nodes, change the view mode when it is teaser.
557
  if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
558 559 560
    $view_mode = 'my_custom_view_mode';
  }
}
561

562 563 564
/**
 * Alters the settings used for displaying an entity.
 *
565
 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
566
 *   The entity view display that will be used to display the entity
567 568 569 570 571 572 573
 *   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'...
 */
574
function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
575 576
  // Leave field labels out of the search index.
  if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
577 578 579 580
    foreach ($display->getComponents() as $name => $options) {
      if (isset($options['label'])) {
        $options['label'] = 'hidden';
        $display->setComponent($name, $options);
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 613 614
/**
 * 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.
  foreach (element_children($build) as $field_name) {
    $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'];
        }
      }
    }
  }
}

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
/**
 * 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.
 *
 * @see \Drupal\Core\Entity\EntityFormController::prepareEntity()
 */
631
function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, array &$form_state) {
632 633 634 635 636 637
  if ($operation == 'edit') {
    $entity->label->value = 'Altered label';
    $form_state['mymodule']['label_altered'] = TRUE;
  }
}

638 639 640
/**
 * Alters the settings used for displaying an entity form.
 *
641
 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
642 643 644 645 646 647 648 649
 *   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'...
 */
650
function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
651 652 653 654 655 656 657 658
  // 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',
    ));
  }
}

659
/**
660
 * Provides custom base field definitions for a content entity type.
661
 *
662 663
 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
 *   The entity type definition.
664
 *
665 666 667 668 669 670
 * @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()
671
 * @see \Drupal\Core\Field\FieldDefinitionInterface
672
 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
673
 */
674 675 676 677
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')
678 679 680 681 682
      ->setLabel(t('The text'))
      ->setDescription(t('A text property added by mymodule.'))
      ->setComputed(TRUE)
      ->setClass('\Drupal\mymodule\EntityComputedText');

683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
    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.
 *
 * @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()
 * @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')
730 731 732
        ->setLabel(t('More text'))
        ->setComputed(TRUE)
        ->setClass('\Drupal\mymodule\EntityComputedMoreText');
733
    return $fields;
734 735 736 737
  }
}

/**
738
 * Alters bundle field definitions.
739
 *
740 741 742 743 744 745
 * @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.
746
 *
747 748 749
 * @see hook_entity_base_field_info()
 * @see hook_entity_base_field_info_alter()
 * @see hook_entity_bundle_field_info()
750
 */
751 752
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'])) {
753
    // Alter the mymodule_text field to use a custom class.
754
    $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
755 756
  }
}
757

758 759 760 761 762
/**
 * Alter entity operations.
 *
 * @param array $operations
 *   Operations array as returned by
763
 *   \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
764 765 766 767
 * @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) {
768 769 770 771
  $operations['translate'] = array(
    'title' => t('Translate'),
    'weight' => 50,
  ) + $entity->urlInfo('my-custom-link-template');
772 773
}

774 775 776
/**
 * Control access to fields.
 *
777
 * This hook is invoked from
778
 * \Drupal\Core\Entity\EntityAccessController::fieldAccess() to let modules
779
 * grant or deny operations on fields.
780 781 782
 *
 * @param string $operation
 *   The operation to be performed. See
783
 *   \Drupal\Core\Access\AccessibleInterface::access() for possible values.
784
 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
785
 *   The field definition.
786
 * @param \Drupal\Core\Session\AccountInterface $account
787
 *   The user account to check.
788
 * @param \Drupal\Core\Field\FieldItemListInterface $items
789 790
 *   (optional) The entity field object on which the operation is to be
 *   performed.
791
 *
792
 * @return bool|null
793 794 795
 *   TRUE if access should be allowed, FALSE if access should be denied and NULL
 *   if the implementation has no opinion.
 */
796
function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
797
  if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
798 799 800 801 802
    return user_access('update field of interest', $account);
  }
}

/**
803
 * Alters the default access behavior for a given field.
804 805 806 807 808 809 810 811 812 813 814
 *
 * 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).
815
 *   - field_definition: The field definition object
816
 *     (\Drupal\Core\Field\FieldDefinitionInterface)
817
 *   - account: The user account to check access for
818
 *     (Drupal\user\Entity\User).
819
 *   - items: (optional) The entity field items
820
 *     (\Drupal\Core\Field\FieldItemListInterface).
821 822
 */
function hook_entity_field_access_alter(array &$grants, array $context) {
823
  /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
824
  $field_definition = $context['field_definition'];
825
  if ($field_definition->getName() == 'field_of_interest' && $grants['node'] === FALSE) {
826 827 828 829 830 831 832 833
    // 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;
  }
}
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

/**
 * 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) {
    if ($bundle->has_title) {
      $extra['node'][$bundle->type]['form']['title'] = array(
        'label' => String::checkPlain($bundle->title_label),
        'description' => $description,
        'weight' => -5,
      );
    }

    // 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;
    }
  }
}