1<?php 2 3/** 4 * @file 5 * Hooks and documentation related to entities. 6 */ 7 8use Drupal\Core\Access\AccessResult; 9use Drupal\Core\Entity\ContentEntityInterface; 10use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface; 11use Drupal\Core\Field\BaseFieldDefinition; 12use Drupal\Core\Field\FieldDefinition; 13use Drupal\Core\Render\Element; 14use Drupal\language\Entity\ContentLanguageSettings; 15use Drupal\node\Entity\NodeType; 16 17/** 18 * @defgroup entity_crud Entity CRUD, editing, and view hooks 19 * @{ 20 * Hooks used in various entity operations. 21 * 22 * Entity create, read, update, and delete (CRUD) operations are performed by 23 * entity storage classes; see the 24 * @link entity_api Entity API topic @endlink for more information. Most 25 * entities use or extend the default classes: 26 * \Drupal\Core\Entity\Sql\SqlContentEntityStorage for content entities, and 27 * \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities. 28 * For these entities, there is a set of hooks that is invoked for each 29 * CRUD operation, which module developers can implement to affect these 30 * operations; these hooks are actually invoked from methods on 31 * \Drupal\Core\Entity\EntityStorageBase. 32 * 33 * For content entities, viewing and rendering are handled by a view builder 34 * class; see the @link entity_api Entity API topic @endlink for more 35 * information. Most view builders extend or use the default class 36 * \Drupal\Core\Entity\EntityViewBuilder. 37 * 38 * Entity editing (including adding new entities) is handled by entity form 39 * classes; see the @link entity_api Entity API topic @endlink for more 40 * information. Most entity editing forms extend base classes 41 * \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm. 42 * Note that many other operations, such as confirming deletion of entities, 43 * also use entity form classes. 44 * 45 * This topic lists all of the entity CRUD and view operations, and the hooks 46 * and other operations that are invoked (in order) for each operation. Some 47 * notes: 48 * - Whenever an entity hook is invoked, there is both a type-specific entity 49 * hook, and a generic entity hook. For instance, during a create operation on 50 * a node, first hook_node_create() and then hook_entity_create() would be 51 * invoked. 52 * - The entity-type-specific hooks are represented in the list below as 53 * hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To 54 * implement one of these hooks for an entity whose machine name is "foo", 55 * define a function called mymodule_foo_create(), for instance. Also note 56 * that the entity or array of entities that are passed into a specific-type 57 * hook are of the specific entity class, not the generic Entity class, so in 58 * your implementation, you can make the $entity argument something like $node 59 * and give it a specific type hint (which should normally be to the specific 60 * interface, such as \Drupal\node\NodeInterface for nodes). 61 * - $storage in the code examples is assumed to be an entity storage 62 * class. See the @link entity_api Entity API topic @endlink for 63 * information on how to instantiate the correct storage class for an 64 * entity type. 65 * - $view_builder in the code examples is assumed to be an entity view builder 66 * class. See the @link entity_api Entity API topic @endlink for 67 * information on how to instantiate the correct view builder class for 68 * an entity type. 69 * - During many operations, static methods are called on the entity class, 70 * which implements \Drupal\Core\Entity\EntityInterface. 71 * 72 * @section entities_revisions_translations Entities, revisions and translations 73 * A content entity can have multiple stored variants: based on its definition, 74 * it can be revisionable, translatable, or both. 75 * 76 * A revisionable entity can keep track of the changes that affect its data. In 77 * fact all previous revisions of the entity can be stored and made available as 78 * "historical" information. The "default" revision is the canonical variant of 79 * the entity, the one that is loaded when no specific revision is requested. 80 * Only changes to the default revision may be performed without triggering the 81 * creation of a new revision, in any other case revision data is not supposed 82 * to change. Aside from historical revisions, there can be "pending" revisions, 83 * that contain changes that did not make their way into the default revision. 84 * Typically these revisions contain data that is waiting for some form of 85 * approval, before being accepted as canonical. 86 * @see \Drupal\Core\Entity\RevisionableInterface 87 * @see \Drupal\Core\Entity\RevisionableStorageInterface 88 * 89 * A translatable entity can contain multiple translations of the same content. 90 * Content entity data is stored via fields, and each field can have one version 91 * for each enabled language. Some fields may be defined as untranslatable, 92 * which means that their values are shared among all translations. The 93 * "default" translation is the canonical variant of the entity, the one whose 94 * content will be accessible in the entity field data. Other translations 95 * can be instantiated from the default one. Every translation has an "active 96 * language" that is used to determine which field translation values should be 97 * handled. Typically the default translation's active language is the language 98 * of the content that was originally entered and served as source for the other 99 * translations. 100 * @see \Drupal\Core\Entity\TranslatableInterface 101 * @see \Drupal\Core\Entity\TranslatableStorageInterface 102 * 103 * An entity that is both revisionable and translatable has all the features 104 * described above: every revision can contain one or more translations. The 105 * canonical variant of the entity is the default translation of the default 106 * revision. Any revision will be initially loaded as the default translation, 107 * the other revision translations can be instantiated from this one. If a 108 * translation has changes in a certain revision, the translation is considered 109 * "affected" by that revision, and will be flagged as such via the 110 * "revision_translation_affected" field. With the built-in UI, every time a new 111 * revision is saved, the changes for the edited translations will be stored, 112 * while all field values for the other translations will be copied as-is. 113 * However, if multiple translations of the default revision are being 114 * subsequently modified without creating a new revision when saving, they will 115 * all be affected by the default revision. Additionally, all revision 116 * translations will be affected when saving a revision containing changes for 117 * untranslatable fields. On the other hand, pending revisions are not supposed 118 * to contain multiple affected translations, even when they are being 119 * manipulated via the API. 120 * @see \Drupal\Core\Entity\TranslatableRevisionableInterface 121 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface 122 * 123 * @section create Create operations 124 * To create an entity: 125 * @code 126 * $entity = $storage->create(); 127 * 128 * // Add code here to set properties on the entity. 129 * 130 * // Until you call save(), the entity is just in memory. 131 * $entity->save(); 132 * @endcode 133 * There is also a shortcut method on entity classes, which creates an entity 134 * with an array of provided property values: \Drupal\Core\Entity::create(). 135 * 136 * Hooks invoked during the create operation: 137 * - hook_ENTITY_TYPE_create() 138 * - hook_entity_create() 139 * - When handling content entities, if a new translation is added to the entity 140 * object: 141 * - hook_ENTITY_TYPE_translation_create() 142 * - hook_entity_translation_create() 143 * 144 * See @ref save below for the save portion of the operation. 145 * 146 * @section load Read/Load operations 147 * To load (read) a single entity: 148 * @code 149 * $entity = $storage->load($id); 150 * @endcode 151 * To load multiple entities: 152 * @code 153 * $entities = $storage->loadMultiple($ids); 154 * @endcode 155 * Since load() calls loadMultiple(), these are really the same operation. 156 * Here is the order of hooks and other operations that take place during 157 * entity loading: 158 * - Entity is loaded from storage. 159 * - postLoad() is called on the entity class, passing in all of the loaded 160 * entities. 161 * - hook_entity_load() 162 * - hook_ENTITY_TYPE_load() 163 * 164 * When an entity is loaded, normally the default entity revision is loaded. 165 * It is also possible to load a different revision, for entities that support 166 * revisions, with this code: 167 * @code 168 * $entity = $storage->loadRevision($revision_id); 169 * @endcode 170 * This involves the same hooks and operations as regular entity loading. 171 * 172 * The "latest revision" of an entity is the most recently created one, 173 * regardless of it being default or pending. If the entity is translatable, 174 * revision translations are not taken into account either. In other words, any 175 * time a new revision is created, that becomes the latest revision for the 176 * entity overall, regardless of the affected translations. To load the latest 177 * revision of an entity: 178 * @code 179 * $revision_id = $storage->getLatestRevisionId($entity_id); 180 * $entity = $storage->loadRevision($revision_id); 181 * @endcode 182 * As usual, if the entity is translatable, this code instantiates into $entity 183 * the default translation of the revision, even if the latest revision contains 184 * only changes to a different translation: 185 * @code 186 * $is_default = $entity->isDefaultTranslation(); // returns TRUE 187 * @endcode 188 * 189 * The "latest translation-affected revision" is the most recently created one 190 * that affects the specified translation. For example, when a new revision 191 * introducing some changes to an English translation is saved, that becomes the 192 * new "latest revision". However, if an existing Italian translation was not 193 * affected by those changes, then the "latest translation-affected revision" 194 * for Italian remains what it was. To load the Italian translation at its 195 * latest translation-affected revision: 196 * @code 197 * $revision_id = $storage->getLatestTranslationAffectedRevisionId($entity_id, 'it'); 198 * $it_translation = $storage->loadRevision($revision_id)->getTranslation('it'); 199 * @endcode 200 * 201 * @section save Save operations 202 * To update an existing entity, you will need to load it, change properties, 203 * and then save; as described above, when creating a new entity, you will also 204 * need to save it. Here is the order of hooks and other events that happen 205 * during an entity save: 206 * - preSave() is called on the entity object, and field objects. 207 * - hook_ENTITY_TYPE_presave() 208 * - hook_entity_presave() 209 * - Entity is saved to storage. 210 * - For updates on content entities, if there is a translation added that 211 * was not previously present: 212 * - hook_ENTITY_TYPE_translation_insert() 213 * - hook_entity_translation_insert() 214 * - For updates on content entities, if there was a translation removed: 215 * - hook_ENTITY_TYPE_translation_delete() 216 * - hook_entity_translation_delete() 217 * - postSave() is called on the entity object. 218 * - hook_ENTITY_TYPE_insert() (new) or hook_ENTITY_TYPE_update() (update) 219 * - hook_entity_insert() (new) or hook_entity_update() (update) 220 * 221 * Some specific entity types invoke hooks during preSave() or postSave() 222 * operations. Examples: 223 * - Field configuration preSave(): hook_field_storage_config_update_forbid() 224 * - Node postSave(): hook_node_access_records() and 225 * hook_node_access_records_alter() 226 * - Config entities that are acting as entity bundles in postSave(): 227 * hook_entity_bundle_create() 228 * - Comment: hook_comment_publish() and hook_comment_unpublish() as 229 * appropriate. 230 * 231 * Note that all translations available for the entity are stored during a save 232 * operation. When saving a new revision, a copy of every translation is stored, 233 * regardless of it being affected by the revision. 234 * 235 * @section edit Editing operations 236 * When an entity's add/edit form is used to add or edit an entity, there 237 * are several hooks that are invoked: 238 * - hook_entity_prepare_form() 239 * - hook_ENTITY_TYPE_prepare_form() 240 * - hook_entity_form_display_alter() (for content entities only) 241 * 242 * @section delete Delete operations 243 * To delete one or more entities, load them and then delete them: 244 * @code 245 * $entities = $storage->loadMultiple($ids); 246 * $storage->delete($entities); 247 * @endcode 248 * 249 * During the delete operation, the following hooks and other events happen: 250 * - preDelete() is called on the entity class. 251 * - hook_ENTITY_TYPE_predelete() 252 * - hook_entity_predelete() 253 * - Entity and field information is removed from storage. 254 * - postDelete() is called on the entity class. 255 * - hook_ENTITY_TYPE_delete() 256 * - hook_entity_delete() 257 * 258 * Some specific entity types invoke hooks during the delete process. Examples: 259 * - Entity bundle postDelete(): hook_entity_bundle_delete() 260 * 261 * Individual revisions of an entity can also be deleted: 262 * @code 263 * $storage->deleteRevision($revision_id); 264 * @endcode 265 * This operation invokes the following operations and hooks: 266 * - Revision is loaded (see @ref load above). 267 * - Revision and field information is removed from the database. 268 * - hook_ENTITY_TYPE_revision_delete() 269 * - hook_entity_revision_delete() 270 * 271 * @section view View/render operations 272 * To make a render array for a loaded entity: 273 * @code 274 * // You can omit the language ID if the default language is being used. 275 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId()); 276 * @endcode 277 * You can also use the viewMultiple() method to view multiple entities. 278 * 279 * Hooks invoked during the operation of building a render array: 280 * - hook_entity_view_mode_alter() 281 * - hook_ENTITY_TYPE_build_defaults_alter() 282 * - hook_entity_build_defaults_alter() 283 * 284 * View builders for some types override these hooks, notably: 285 * - The Tour view builder does not invoke any hooks. 286 * - The Block view builder invokes hook_block_view_alter() and 287 * hook_block_view_BASE_BLOCK_ID_alter(). Note that in other view builders, 288 * the view alter hooks are run later in the process. 289 * 290 * During the rendering operation, the default entity viewer runs the following 291 * hooks and operations in the pre-render step: 292 * - hook_entity_view_display_alter() 293 * - hook_entity_prepare_view() 294 * - Entity fields are loaded, and render arrays are built for them using 295 * their formatters. 296 * - hook_entity_display_build_alter() 297 * - hook_ENTITY_TYPE_view() 298 * - hook_entity_view() 299 * - hook_ENTITY_TYPE_view_alter() 300 * - hook_entity_view_alter() 301 * 302 * Some specific builders have specific hooks: 303 * - The Node view builder invokes hook_node_links_alter(). 304 * - The Comment view builder invokes hook_comment_links_alter(). 305 * 306 * After this point in rendering, the theme system takes over. See the 307 * @link theme_render Theme system and render API topic @endlink for more 308 * information. 309 * 310 * @section misc Other entity hooks 311 * Some types of entities invoke hooks for specific operations: 312 * - Searching nodes: 313 * - hook_ranking() 314 * - Query is executed to find matching nodes 315 * - Resulting node is loaded 316 * - Node render array is built 317 * - comment_node_update_index() is called (this adds "N comments" text) 318 * - hook_node_search_result() 319 * - Search indexing nodes: 320 * - Node is loaded 321 * - Node render array is built 322 * - hook_node_update_index() 323 * @} 324 */ 325 326/** 327 * @defgroup entity_api Entity API 328 * @{ 329 * Describes how to define and manipulate content and configuration entities. 330 * 331 * Entities, in Drupal, are objects that are used for persistent storage of 332 * content and configuration information. See the 333 * @link info_types Information types topic @endlink for an overview of the 334 * different types of information, and the 335 * @link config_api Configuration API topic @endlink for more about the 336 * configuration API. 337 * 338 * Each entity is an instance of a particular "entity type". Some content entity 339 * types have sub-types, which are known as "bundles", while for other entity 340 * types, there is only a single bundle. For example, the Node content entity 341 * type, which is used for the main content pages in Drupal, has bundles that 342 * are known as "content types", while the User content type, which is used for 343 * user accounts, has only one bundle. 344 * 345 * The sections below have more information about entities and the Entity API; 346 * for more detailed information, see 347 * https://www.drupal.org/developing/api/entity. 348 * 349 * @section define Defining an entity type 350 * Entity types are defined by modules, using Drupal's Plugin API (see the 351 * @link plugin_api Plugin API topic @endlink for more information about plugins 352 * in general). Here are the steps to follow to define a new entity type: 353 * - Choose a unique machine name, or ID, for your entity type. This normally 354 * starts with (or is the same as) your module's machine name. It should be 355 * as short as possible, and may not exceed 32 characters. 356 * - Define an interface for your entity's get/set methods, usually extending 357 * either \Drupal\Core\Config\Entity\ConfigEntityInterface or 358 * \Drupal\Core\Entity\ContentEntityInterface. 359 * - Define a class for your entity, implementing your interface and extending 360 * either \Drupal\Core\Config\Entity\ConfigEntityBase or 361 * \Drupal\Core\Entity\ContentEntityBase, with annotation for 362 * \@ConfigEntityType or \@ContentEntityType in its documentation block. 363 * If you are defining a content entity type, it is recommended to extend the 364 * \Drupal\Core\Entity\EditorialContentEntityBase base class in order to get 365 * out-of-the-box support for Entity API's revisioning and publishing 366 * features, which will allow your entity type to be used with Drupal's 367 * editorial workflow provided by the Content Moderation module. 368 * - In the annotation, the 'id' property gives the entity type ID, and the 369 * 'label' property gives the human-readable name of the entity type. If you 370 * are defining a content entity type that uses bundles, the 'bundle_label' 371 * property gives the human-readable name to use for a bundle of this entity 372 * type (for example, "Content type" for the Node entity). 373 * - The annotation will refer to several handler classes, which you will also 374 * need to define: 375 * - list_builder: Define a class that extends 376 * \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration 377 * entities) or \Drupal\Core\Entity\EntityListBuilder (for content 378 * entities), to provide an administrative overview for your entities. 379 * - add and edit forms, or default form: Define a class (or two) that 380 * extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms 381 * for your entities. For content entities, base class 382 * \Drupal\Core\Entity\ContentEntityForm is a better starting point. 383 * - delete form: Define a class that extends 384 * \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete 385 * confirmation form for your entities. 386 * - view_builder: For content entities and config entities that need to be 387 * viewed, define a class that implements 388 * \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending 389 * \Drupal\Core\Entity\EntityViewBuilder), to display a single entity. 390 * - translation: For translatable content entities (if the 'translatable' 391 * annotation property has value TRUE), define a class that extends 392 * \Drupal\content_translation\ContentTranslationHandler, to translate 393 * the content. Configuration translation is handled automatically by the 394 * Configuration Translation module, without the need of a handler class. 395 * - access: If your configuration entity has complex permissions, you might 396 * need an access control handling, implementing 397 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most 398 * entities can just use the 'admin_permission' annotation property 399 * instead. Note that if you are creating your own access control handler, 400 * you should override the checkAccess() and checkCreateAccess() methods, 401 * not access(). 402 * - storage: A class implementing 403 * \Drupal\Core\Entity\EntityStorageInterface. If not specified, content 404 * entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and 405 * config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage. 406 * You can extend one of these classes to provide custom behavior. 407 * - views_data: A class implementing \Drupal\views\EntityViewsDataInterface 408 * to provide views data for the entity type. You can autogenerate most of 409 * the views data by extending \Drupal\views\EntityViewsData. 410 * - For content entities, the annotation will refer to a number of database 411 * tables and their fields. These annotation properties, such as 'base_table', 412 * 'data_table', 'entity_keys', etc., are documented on 413 * \Drupal\Core\Entity\EntityType. 414 * - For content entities that are displayed on their own pages, the annotation 415 * will refer to a 'uri_callback' function, which takes an object of the 416 * entity interface you have defined as its parameter, and returns routing 417 * information for the entity page; see node_uri() for an example. You will 418 * also need to add a corresponding route to your module's routing.yml file; 419 * see the entity.node.canonical route in node.routing.yml for an example, and see 420 * @ref sec_routes below for some notes. 421 * - Optionally, instead of defining routes, routes can be auto generated by 422 * providing a route handler. See @ref sec_routes. Otherwise, define routes 423 * and links for the various URLs associated with the entity. 424 * These go into the 'links' annotation, with the link type as the key, and 425 * the path of this link template as the value. The corresponding route 426 * requires the following route name: 427 * "entity.$entity_type_id.$link_template_type". See @ref sec_routes below for 428 * some routing notes. Typical link types are: 429 * - canonical: Default link, either to view (if entities are viewed on their 430 * own pages) or edit the entity. 431 * - delete-form: Confirmation form to delete the entity. 432 * - edit-form: Editing form. 433 * - Other link types specific to your entity type can also be defined. 434 * - If your content entity is fieldable, provide the 'field_ui_base_route' 435 * annotation property, giving the name of the route that the Manage Fields, 436 * Manage Display, and Manage Form Display pages from the Field UI module 437 * will be attached to. This is usually the bundle settings edit page, or an 438 * entity type settings page if there are no bundles. 439 * - If your content entity has bundles, you will also need to define a second 440 * plugin to handle the bundles. This plugin is itself a configuration entity 441 * type, so follow the steps here to define it. The machine name ('id' 442 * annotation property) of this configuration entity class goes into the 443 * 'bundle_entity_type' annotation property on the entity type class. For 444 * example, for the Node entity, the bundle class is 445 * \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is 446 * the annotation property 'bundle_entity_type' on the 447 * \Drupal\node\Entity\Node class. Also, the 448 * bundle config entity type annotation must have a 'bundle_of' property, 449 * giving the machine name of the entity type it is acting as a bundle for. 450 * These machine names are considered permanent, they may not be renamed. 451 * - Additional annotation properties can be seen on entity class examples such 452 * as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role 453 * (configuration). These annotation properties are documented on 454 * \Drupal\Core\Entity\EntityType. 455 * 456 * @section sec_routes Entity routes 457 * Entity routes can be defined in *.routing.yml files, like any other route: 458 * see the @link routing Routing API @endlink topic for more information. 459 * Another option for entity routes is to use a route provider class, and 460 * reference it in the annotations on the entity class: see the end of this 461 * section for an example. 462 * 463 * It's possible to use both a YAML file and a provider class for entity 464 * routes, at the same time. Avoid duplicating route names between the two: 465 * if a duplicate route name is found in both locations, the one in the YAML 466 * file takes precedence; regardless, such duplication can be confusing. 467 * 468 * Here's an example YAML route specification, for the block configure form: 469 * @code 470 * entity.block.edit_form: 471 * path: '/admin/structure/block/manage/{block}' 472 * defaults: 473 * _entity_form: 'block.default' 474 * _title: 'Configure block' 475 * requirements: 476 * _entity_access: 'block.update' 477 * @endcode 478 * Some notes on this example: 479 * - path: The {block} in the path is a placeholder, which (for an entity) must 480 * always take the form of {machine_name_of_entity_type}. In the URL, the 481 * placeholder value will be the ID of an entity item. When the route is used, 482 * the entity system will load the corresponding entity item and pass it in as 483 * an object to the controller for the route. 484 * - defaults: For entity form routes, use _entity_form rather than the generic 485 * _controller or _form. The value is composed of the entity type machine name 486 * and a form handler type from the entity annotation (see @ref define above 487 * for more on handlers and annotation). So, in this example, block.default 488 * refers to the 'default' form handler on the block entity type, whose 489 * annotation contains: 490 * @code 491 * handlers = { 492 * "form" = { 493 * "default" = "Drupal\block\BlockForm", 494 * @endcode 495 * If instead of YAML you want to use a route provider class: 496 * - \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical, 497 * edit-form, and delete-form routes. 498 * - \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same 499 * routes, set up to use the administrative theme for edit and delete pages. 500 * - You can also create your own class, extending one of these two classes if 501 * you only want to modify their behavior slightly. 502 * 503 * To register any route provider class, add lines like the following to your 504 * entity class annotation: 505 * @code 506 * handlers = { 507 * "route_provider" = { 508 * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider", 509 * @endcode 510 * 511 * @section bundle Defining a content entity bundle 512 * For entity types that use bundles, such as Node (bundles are content types) 513 * and Taxonomy (bundles are vocabularies), modules and install profiles can 514 * define bundles by supplying default configuration in their config/install 515 * directories. (See the @link config_api Configuration API topic @endlink for 516 * general information about configuration.) 517 * 518 * There are several good examples of this in Drupal Core: 519 * - The Forum module defines a content type in node.type.forum.yml and a 520 * vocabulary in taxonomy.vocabulary.forums.yml 521 * - The Book module defines a content type in node.type.book.yml 522 * - The Standard install profile defines Page and Article content types in 523 * node.type.page.yml and node.type.article.yml, a Tags vocabulary in 524 * taxonomy.vocabulary.tags.yml, and a Node comment type in 525 * comment.type.comment.yml. This profile's configuration is especially 526 * instructive, because it also adds several fields to the Article type, and 527 * it sets up view and form display modes for the node types. 528 * 529 * @section load_query Loading, querying, and rendering entities 530 * To load entities, use the entity storage manager, which is an object 531 * implementing \Drupal\Core\Entity\EntityStorageInterface that you can 532 * retrieve with: 533 * @code 534 * $storage = \Drupal::entityTypeManager()->getStorage('your_entity_type'); 535 * // Or if you have a $container variable: 536 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type'); 537 * @endcode 538 * Here, 'your_entity_type' is the machine name of your entity type ('id' 539 * annotation property on the entity class), and note that you should use 540 * dependency injection to retrieve this object if possible. See the 541 * @link container Services and Dependency Injection topic @endlink for more 542 * about how to properly retrieve services. 543 * 544 * To query to find entities to load, use an entity query, which is an object 545 * implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve 546 * with: 547 * @code 548 * // Simple query: 549 * $query = \Drupal::entityQuery('your_entity_type'); 550 * // Or, if you have a $container variable: 551 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type'); 552 * $query = $storage->getQuery(); 553 * @endcode 554 * If you need aggregation, there is an aggregate query available, which 555 * implements \Drupal\Core\Entity\Query\QueryAggregateInterface: 556 * @code 557 * $query \Drupal::entityQueryAggregate('your_entity_type'); 558 * // Or: 559 * $query = $storage->getAggregateQuery('your_entity_type'); 560 * @endcode 561 * 562 * In either case, you can then add conditions to your query, using methods 563 * like condition(), exists(), etc. on $query; add sorting, pager, and range 564 * if needed, and execute the query to return a list of entity IDs that match 565 * the query. 566 * 567 * Here is an example, using the core File entity: 568 * @code 569 * $fids = Drupal::entityQuery('file') 570 * ->condition('status', FILE_STATUS_PERMANENT, '<>') 571 * ->condition('changed', REQUEST_TIME - $age, '<') 572 * ->range(0, 100) 573 * ->execute(); 574 * $files = $storage->loadMultiple($fids); 575 * @endcode 576 * 577 * The normal way of viewing entities is by using a route, as described in the 578 * sections above. If for some reason you need to render an entity in code in a 579 * particular view mode, you can use an entity view builder, which is an object 580 * implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can 581 * retrieve with: 582 * @code 583 * $view_builder = \Drupal::entityTypeManager()->getViewBuilder('your_entity_type'); 584 * // Or if you have a $container variable: 585 * $view_builder = $container->get('entity_type.manager')->getViewBuilder('your_entity_type'); 586 * @endcode 587 * Then, to build and render the entity: 588 * @code 589 * // You can omit the language ID, by default the current content language will 590 * // be used. If no translation is available for the current language, fallback 591 * // rules will be used. 592 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId()); 593 * // $build is a render array. 594 * $rendered = \Drupal::service('renderer')->render($build); 595 * @endcode 596 * 597 * @section sec_access Access checking on entities 598 * Entity types define their access permission scheme in their annotation. 599 * Access permissions can be quite complex, so you should not assume any 600 * particular permission scheme. Instead, once you have an entity object 601 * loaded, you can check for permission for a particular operation (such as 602 * 'view') at the entity or field level by calling: 603 * @code 604 * $entity->access($operation); 605 * $entity->nameOfField->access($operation); 606 * @endcode 607 * The interface related to access checking in entities and fields is 608 * \Drupal\Core\Access\AccessibleInterface. 609 * 610 * The default entity access control handler invokes two hooks while checking 611 * access on a single entity: hook_entity_access() is invoked first, and 612 * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name 613 * of the entity type). If no module returns a TRUE or FALSE value from 614 * either of these hooks, then the entity's default access checking takes 615 * place. For create operations (creating a new entity), the hooks that 616 * are invoked are hook_entity_create_access() and 617 * hook_ENTITY_TYPE_create_access() instead. 618 * 619 * The access to an entity can be influenced in several ways: 620 * - To explicitly allow access, return an AccessResultInterface object with 621 * isAllowed() returning TRUE. Other modules can override this access by 622 * returning TRUE for isForbidden(). 623 * - To explicitly forbid access, return an AccessResultInterface object with 624 * isForbidden() returning TRUE. Access will be forbidden even if your module 625 * (or another module) also returns TRUE for isNeutral() or isAllowed(). 626 * - To neither allow nor explicitly forbid access, return an 627 * AccessResultInterface object with isNeutral() returning TRUE. 628 * - If your module does not return an AccessResultInterface object, neutral 629 * access will be assumed. 630 * 631 * The Node entity type has a complex system for determining access, which 632 * developers can interact with. This is described in the 633 * @link node_access Node access topic. @endlink 634 * 635 * @see i18n 636 * @see entity_crud 637 * @see \Drupal\Core\Entity\EntityRepositoryInterface::getTranslationFromContext() 638 * @} 639 */ 640 641/** 642 * @addtogroup hooks 643 * @{ 644 */ 645 646/** 647 * Control entity operation access. 648 * 649 * Note that this hook is not called for listings (e.g., from entity queries 650 * and Views). For nodes, see @link node_access Node access rights @endlink for 651 * a full explanation. For other entity types, see hook_query_TAG_alter(). 652 * 653 * @param \Drupal\Core\Entity\EntityInterface $entity 654 * The entity to check access to. 655 * @param string $operation 656 * The operation that is to be performed on $entity. Usually one of: 657 * - "view" 658 * - "update" 659 * - "delete" 660 * @param \Drupal\Core\Session\AccountInterface $account 661 * The account trying to access the entity. 662 * 663 * @return \Drupal\Core\Access\AccessResultInterface 664 * The access result. The final result is calculated by using 665 * \Drupal\Core\Access\AccessResultInterface::orIf() on the result of every 666 * hook_entity_access() and hook_ENTITY_TYPE_access() implementation, and the 667 * result of the entity-specific checkAccess() method in the entity access 668 * control handler. Be careful when writing generalized access checks shared 669 * between routing and entity checks: routing uses the andIf() operator. So 670 * returning an isNeutral() does not determine entity access at all but it 671 * always ends up denying access while routing. 672 * 673 * @see \Drupal\Core\Entity\EntityAccessControlHandler 674 * @see hook_entity_create_access() 675 * @see hook_ENTITY_TYPE_access() 676 * @see hook_query_TAG_alter() 677 * 678 * @ingroup entity_api 679 */ 680function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) { 681 // No opinion. 682 return AccessResult::neutral(); 683} 684 685/** 686 * Control entity operation access for a specific entity type. 687 * 688 * Note that this hook is not called for listings (e.g., from entity queries 689 * and Views). For nodes, see @link node_access Node access rights @endlink for 690 * a full explanation. For other entity types, see hook_query_TAG_alter(). 691 * 692 * @param \Drupal\Core\Entity\EntityInterface $entity 693 * The entity to check access to. 694 * @param string $operation 695 * The operation that is to be performed on $entity. Usually one of: 696 * - "view" 697 * - "update" 698 * - "delete" 699 * @param \Drupal\Core\Session\AccountInterface $account 700 * The account trying to access the entity. 701 * 702 * @return \Drupal\Core\Access\AccessResultInterface 703 * The access result. hook_entity_access() has detailed documentation. 704 * 705 * @see \Drupal\Core\Entity\EntityAccessControlHandler 706 * @see hook_ENTITY_TYPE_create_access() 707 * @see hook_entity_access() 708 * @see hook_query_TAG_alter() 709 * 710 * @ingroup entity_api 711 */ 712function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) { 713 // No opinion. 714 return AccessResult::neutral(); 715} 716 717/** 718 * Control entity create access. 719 * 720 * @param \Drupal\Core\Session\AccountInterface $account 721 * The account trying to access the entity. 722 * @param array $context 723 * An associative array of additional context values. By default it contains 724 * language and the entity type ID: 725 * - entity_type_id - the entity type ID. 726 * - langcode - the current language code. 727 * @param string $entity_bundle 728 * The entity bundle name. 729 * 730 * @return \Drupal\Core\Access\AccessResultInterface 731 * The access result. 732 * 733 * @see \Drupal\Core\Entity\EntityAccessControlHandler 734 * @see hook_entity_access() 735 * @see hook_ENTITY_TYPE_create_access() 736 * 737 * @ingroup entity_api 738 */ 739function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) { 740 // No opinion. 741 return AccessResult::neutral(); 742} 743 744/** 745 * Control entity create access for a specific entity type. 746 * 747 * @param \Drupal\Core\Session\AccountInterface $account 748 * The account trying to access the entity. 749 * @param array $context 750 * An associative array of additional context values. By default it contains 751 * language: 752 * - langcode - the current language code. 753 * @param string $entity_bundle 754 * The entity bundle name. 755 * 756 * @return \Drupal\Core\Access\AccessResultInterface 757 * The access result. 758 * 759 * @see \Drupal\Core\Entity\EntityAccessControlHandler 760 * @see hook_ENTITY_TYPE_access() 761 * @see hook_entity_create_access() 762 * 763 * @ingroup entity_api 764 */ 765function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) { 766 // No opinion. 767 return AccessResult::neutral(); 768} 769 770/** 771 * Add to entity type definitions. 772 * 773 * Modules may implement this hook to add information to defined entity types, 774 * as defined in \Drupal\Core\Entity\EntityTypeInterface. 775 * 776 * To alter existing information or to add information dynamically, use 777 * hook_entity_type_alter(). 778 * 779 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types 780 * An associative array of all entity type definitions, keyed by the entity 781 * type name. Passed by reference. 782 * 783 * @see \Drupal\Core\Entity\Entity 784 * @see \Drupal\Core\Entity\EntityTypeInterface 785 * @see hook_entity_type_alter() 786 */ 787function hook_entity_type_build(array &$entity_types) { 788 /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ 789 // Add a form for a custom node form without overriding the default 790 // node form. To override the default node form, use hook_entity_type_alter(). 791 $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm'); 792} 793 794/** 795 * Alter the entity type definitions. 796 * 797 * Modules may implement this hook to alter the information that defines an 798 * entity type. All properties that are available in 799 * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally 800 * provided by modules can be altered here. 801 * 802 * Do not use this hook to add information to entity types, unless one of the 803 * following is true: 804 * - You are filling in default values. 805 * - You need to dynamically add information only in certain circumstances. 806 * - Your hook needs to run after hook_entity_type_build() implementations. 807 * Use hook_entity_type_build() instead in all other cases. 808 * 809 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types 810 * An associative array of all entity type definitions, keyed by the entity 811 * type name. Passed by reference. 812 * 813 * @see \Drupal\Core\Entity\Entity 814 * @see \Drupal\Core\Entity\EntityTypeInterface 815 */ 816function hook_entity_type_alter(array &$entity_types) { 817 /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ 818 // Set the controller class for nodes to an alternate implementation of the 819 // Drupal\Core\Entity\EntityStorageInterface interface. 820 $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage'); 821} 822 823/** 824 * Alter the view modes for entity types. 825 * 826 * @param array $view_modes 827 * An array of view modes, keyed first by entity type, then by view mode name. 828 * 829 * @see \Drupal\Core\Entity\EntityDisplayRepositoryInterface::getAllViewModes() 830 * @see \Drupal\Core\Entity\EntityDisplayRepositoryInterface::getViewModes() 831 */ 832function hook_entity_view_mode_info_alter(&$view_modes) { 833 $view_modes['user']['full']['status'] = TRUE; 834} 835 836/** 837 * Describe the bundles for entity types. 838 * 839 * @return array 840 * An associative array of all entity bundles, keyed by the entity 841 * type name, and then the bundle name, with the following keys: 842 * - label: The human-readable name of the bundle. 843 * - uri_callback: (optional) The same as the 'uri_callback' key defined for 844 * the entity type in the EntityTypeManager, but for the bundle only. When 845 * determining the URI of an entity, if a 'uri_callback' is defined for both 846 * the entity type and the bundle, the one for the bundle is used. 847 * - translatable: (optional) A boolean value specifying whether this bundle 848 * has translation support enabled. Defaults to FALSE. 849 * 850 * @see \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo() 851 * @see hook_entity_bundle_info_alter() 852 */ 853function hook_entity_bundle_info() { 854 $bundles['user']['user']['label'] = t('User'); 855 return $bundles; 856} 857 858/** 859 * Alter the bundles for entity types. 860 * 861 * @param array $bundles 862 * An array of bundles, keyed first by entity type, then by bundle name. 863 * 864 * @see Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo() 865 * @see hook_entity_bundle_info() 866 */ 867function hook_entity_bundle_info_alter(&$bundles) { 868 $bundles['user']['user']['label'] = t('Full account'); 869} 870 871/** 872 * Act on entity_bundle_create(). 873 * 874 * This hook is invoked after the operation has been performed. 875 * 876 * @param string $entity_type_id 877 * The type of $entity; e.g. 'node' or 'user'. 878 * @param string $bundle 879 * The name of the bundle. 880 * 881 * @see entity_crud 882 */ 883function hook_entity_bundle_create($entity_type_id, $bundle) { 884 // When a new bundle is created, the menu needs to be rebuilt to add the 885 // Field UI menu item tabs. 886 \Drupal::service('router.builder')->setRebuildNeeded(); 887} 888 889/** 890 * Act on entity_bundle_delete(). 891 * 892 * This hook is invoked after the operation has been performed. 893 * 894 * @param string $entity_type_id 895 * The type of entity; for example, 'node' or 'user'. 896 * @param string $bundle 897 * The bundle that was just deleted. 898 * 899 * @ingroup entity_crud 900 */ 901function hook_entity_bundle_delete($entity_type_id, $bundle) { 902 // Remove the settings associated with the bundle in my_module.settings. 903 $config = \Drupal::config('my_module.settings'); 904 $bundle_settings = $config->get('bundle_settings'); 905 if (isset($bundle_settings[$entity_type_id][$bundle])) { 906 unset($bundle_settings[$entity_type_id][$bundle]); 907 $config->set('bundle_settings', $bundle_settings); 908 } 909} 910 911/** 912 * Acts when creating a new entity. 913 * 914 * This hook runs after a new entity object has just been instantiated. 915 * 916 * @param \Drupal\Core\Entity\EntityInterface $entity 917 * The entity object. 918 * 919 * @ingroup entity_crud 920 * @see hook_ENTITY_TYPE_create() 921 */ 922function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) { 923 \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]); 924} 925 926/** 927 * Acts when creating a new entity of a specific type. 928 * 929 * This hook runs after a new entity object has just been instantiated. 930 * 931 * @param \Drupal\Core\Entity\EntityInterface $entity 932 * The entity object. 933 * 934 * @ingroup entity_crud 935 * @see hook_entity_create() 936 */ 937function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) { 938 \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]); 939} 940 941/** 942 * Respond to entity revision creation. 943 * 944 * @param \Drupal\Core\Entity\EntityInterface $new_revision 945 * The new revision that was created. 946 * @param \Drupal\Core\Entity\EntityInterface $entity 947 * The original entity that was used to create the revision from. 948 * @param bool|null $keep_untranslatable_fields 949 * Whether untranslatable field values were kept (TRUE) or copied from the 950 * default revision (FALSE) when generating a merged revision. If no value was 951 * explicitly specified (NULL), a default value of TRUE should be assumed if 952 * the provided entity is the default translation and untranslatable fields 953 * should only affect the default translation, FALSE otherwise. 954 * 955 * @ingroup entity_crud 956 * @see \Drupal\Core\Entity\RevisionableStorageInterface::createRevision() 957 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface::createRevision() 958 */ 959function hook_entity_revision_create(\Drupal\Core\Entity\EntityInterface $new_revision, \Drupal\Core\Entity\EntityInterface $entity, $keep_untranslatable_fields) { 960 // Retain the value from an untranslatable field, which are by default 961 // synchronized from the default revision. 962 $new_revision->set('untranslatable_field', $entity->get('untranslatable_field')); 963} 964 965/** 966 * Respond to entity revision creation. 967 * 968 * @param \Drupal\Core\Entity\EntityInterface $new_revision 969 * The new revision that was created. 970 * @param \Drupal\Core\Entity\EntityInterface $entity 971 * The original entity that was used to create the revision from. 972 * @param bool|null $keep_untranslatable_fields 973 * Whether untranslatable field values were kept (TRUE) or copied from the 974 * default revision (FALSE) when generating a merged revision. If no value was 975 * explicitly specified (NULL), a default value of TRUE should be assumed if 976 * the provided entity is the default translation and untranslatable fields 977 * should only affect the default translation, FALSE otherwise. 978 * 979 * @ingroup entity_crud 980 * @see \Drupal\Core\Entity\RevisionableStorageInterface::createRevision() 981 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface::createRevision() 982 */ 983function hook_ENTITY_TYPE_revision_create(\Drupal\Core\Entity\EntityInterface $new_revision, \Drupal\Core\Entity\EntityInterface $entity, $keep_untranslatable_fields) { 984 // Retain the value from an untranslatable field, which are by default 985 // synchronized from the default revision. 986 $new_revision->set('untranslatable_field', $entity->get('untranslatable_field')); 987} 988 989/** 990 * Act on an array of entity IDs before they are loaded. 991 * 992 * This hook can be used by modules that need, for example, to return a 993 * different revision than the default one. 994 * 995 * @param array $ids 996 * An array of entity IDs that have to be loaded. 997 * @param string $entity_type_id 998 * The type of entities being loaded (i.e. node, user, comment). 999 * 1000 * @return \Drupal\Core\Entity\EntityInterface[] 1001 * An array of pre-loaded entity objects. 1002 * 1003 * @ingroup entity_crud 1004 */ 1005function hook_entity_preload(array $ids, $entity_type_id) { 1006 $entities = []; 1007 1008 foreach ($ids as $id) { 1009 $entities[] = mymodule_swap_revision($id); 1010 } 1011 1012 return $entities; 1013} 1014 1015/** 1016 * Act on entities when loaded. 1017 * 1018 * This is a generic load hook called for all entity types loaded via the 1019 * entity API. 1020 * 1021 * hook_entity_storage_load() should be used to load additional data for 1022 * content entities. 1023 * 1024 * @param \Drupal\Core\Entity\EntityInterface[] $entities 1025 * The entities keyed by entity ID. 1026 * @param string $entity_type_id 1027 * The type of entities being loaded (i.e. node, user, comment). 1028 * 1029 * @ingroup entity_crud 1030 * @see hook_ENTITY_TYPE_load() 1031 */ 1032function hook_entity_load(array $entities, $entity_type_id) { 1033 foreach ($entities as $entity) { 1034 $entity->foo = mymodule_add_something($entity); 1035 } 1036} 1037 1038/** 1039 * Act on entities of a specific type when loaded. 1040 * 1041 * @param array $entities 1042 * The entities keyed by entity ID. 1043 * 1044 * @ingroup entity_crud 1045 * @see hook_entity_load() 1046 */ 1047function hook_ENTITY_TYPE_load($entities) { 1048 foreach ($entities as $entity) { 1049 $entity->foo = mymodule_add_something($entity); 1050 } 1051} 1052 1053/** 1054 * Act on content entities when loaded from the storage. 1055 * 1056 * The results of this hook will be cached. 1057 * 1058 * @param \Drupal\Core\Entity\EntityInterface[] $entities 1059 * The entities keyed by entity ID. 1060 * @param string $entity_type 1061 * The type of entities being loaded (i.e. node, user, comment). 1062 * 1063 * @see hook_entity_load() 1064 */ 1065function hook_entity_storage_load(array $entities, $entity_type) { 1066 foreach ($entities as $entity) { 1067 $entity->foo = mymodule_add_something_uncached($entity); 1068 } 1069} 1070 1071/** 1072 * Act on content entities of a given type when loaded from the storage. 1073 * 1074 * The results of this hook will be cached if the entity type supports it. 1075 * 1076 * @param \Drupal\Core\Entity\EntityInterface[] $entities 1077 * The entities keyed by entity ID. 1078 * 1079 * @see hook_entity_storage_load() 1080 */ 1081function hook_ENTITY_TYPE_storage_load(array $entities) { 1082 foreach ($entities as $entity) { 1083 $entity->foo = mymodule_add_something_uncached($entity); 1084 } 1085} 1086 1087/** 1088 * Act on an entity before it is created or updated. 1089 * 1090 * You can get the original entity object from $entity->original when it is an 1091 * update of the entity. 1092 * 1093 * @param \Drupal\Core\Entity\EntityInterface $entity 1094 * The entity object. 1095 * 1096 * @ingroup entity_crud 1097 * @see hook_ENTITY_TYPE_presave() 1098 */ 1099function hook_entity_presave(\Drupal\Core\Entity\EntityInterface $entity) { 1100 if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) { 1101 $route_match = \Drupal::routeMatch(); 1102 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode')); 1103 } 1104} 1105 1106/** 1107 * Act on a specific type of entity before it is created or updated. 1108 * 1109 * You can get the original entity object from $entity->original when it is an 1110 * update of the entity. 1111 * 1112 * @param \Drupal\Core\Entity\EntityInterface $entity 1113 * The entity object. 1114 * 1115 * @ingroup entity_crud 1116 * @see hook_entity_presave() 1117 */ 1118function hook_ENTITY_TYPE_presave(\Drupal\Core\Entity\EntityInterface $entity) { 1119 if ($entity->isTranslatable()) { 1120 $route_match = \Drupal::routeMatch(); 1121 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode')); 1122 } 1123} 1124 1125/** 1126 * Respond to creation of a new entity. 1127 * 1128 * This hook runs once the entity has been stored. Note that hook 1129 * implementations may not alter the stored entity data. 1130 * 1131 * @param \Drupal\Core\Entity\EntityInterface $entity 1132 * The entity object. 1133 * 1134 * @ingroup entity_crud 1135 * @see hook_ENTITY_TYPE_insert() 1136 */ 1137function hook_entity_insert(\Drupal\Core\Entity\EntityInterface $entity) { 1138 // Insert the new entity into a fictional table of all entities. 1139 \Drupal::database()->insert('example_entity') 1140 ->fields([ 1141 'type' => $entity->getEntityTypeId(), 1142 'id' => $entity->id(), 1143 'created' => REQUEST_TIME, 1144 'updated' => REQUEST_TIME, 1145 ]) 1146 ->execute(); 1147} 1148 1149/** 1150 * Respond to creation of a new entity of a particular type. 1151 * 1152 * This hook runs once the entity has been stored. Note that hook 1153 * implementations may not alter the stored entity data. 1154 * 1155 * @param \Drupal\Core\Entity\EntityInterface $entity 1156 * The entity object. 1157 * 1158 * @ingroup entity_crud 1159 * @see hook_entity_insert() 1160 */ 1161function hook_ENTITY_TYPE_insert(\Drupal\Core\Entity\EntityInterface $entity) { 1162 // Insert the new entity into a fictional table of this type of entity. 1163 \Drupal::database()->insert('example_entity') 1164 ->fields([ 1165 'id' => $entity->id(), 1166 'created' => REQUEST_TIME, 1167 'updated' => REQUEST_TIME, 1168 ]) 1169 ->execute(); 1170} 1171 1172/** 1173 * Respond to updates to an entity. 1174 * 1175 * This hook runs once the entity storage has been updated. Note that hook 1176 * implementations may not alter the stored entity data. Get the original entity 1177 * object from $entity->original. 1178 * 1179 * @param \Drupal\Core\Entity\EntityInterface $entity 1180 * The entity object. 1181 * 1182 * @ingroup entity_crud 1183 * @see hook_ENTITY_TYPE_update() 1184 */ 1185function hook_entity_update(\Drupal\Core\Entity\EntityInterface $entity) { 1186 // Update the entity's entry in a fictional table of all entities. 1187 \Drupal::database()->update('example_entity') 1188 ->fields([ 1189 'updated' => REQUEST_TIME, 1190 ]) 1191 ->condition('type', $entity->getEntityTypeId()) 1192 ->condition('id', $entity->id()) 1193 ->execute(); 1194} 1195 1196/** 1197 * Respond to updates to an entity of a particular type. 1198 * 1199 * This hook runs once the entity storage has been updated. Note that hook 1200 * implementations may not alter the stored entity data. Get the original entity 1201 * object from $entity->original. 1202 * 1203 * @param \Drupal\Core\Entity\EntityInterface $entity 1204 * The entity object. 1205 * 1206 * @ingroup entity_crud 1207 * @see hook_entity_update() 1208 */ 1209function hook_ENTITY_TYPE_update(\Drupal\Core\Entity\EntityInterface $entity) { 1210 // Update the entity's entry in a fictional table of this type of entity. 1211 \Drupal::database()->update('example_entity') 1212 ->fields([ 1213 'updated' => REQUEST_TIME, 1214 ]) 1215 ->condition('id', $entity->id()) 1216 ->execute(); 1217} 1218 1219/** 1220 * Acts when creating a new entity translation. 1221 * 1222 * This hook runs after a new entity translation object has just been 1223 * instantiated. 1224 * 1225 * @param \Drupal\Core\Entity\EntityInterface $translation 1226 * The entity object. 1227 * 1228 * @ingroup entity_crud 1229 * @see hook_ENTITY_TYPE_translation_create() 1230 */ 1231function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) { 1232 \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]); 1233} 1234 1235/** 1236 * Acts when creating a new entity translation of a specific type. 1237 * 1238 * This hook runs after a new entity translation object has just been 1239 * instantiated. 1240 * 1241 * @param \Drupal\Core\Entity\EntityInterface $translation 1242 * The entity object. 1243 * 1244 * @ingroup entity_crud 1245 * @see hook_entity_translation_create() 1246 */ 1247function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) { 1248 \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]); 1249} 1250 1251/** 1252 * Respond to creation of a new entity translation. 1253 * 1254 * This hook runs once the entity translation has been stored. Note that hook 1255 * implementations may not alter the stored entity translation data. 1256 * 1257 * @param \Drupal\Core\Entity\EntityInterface $translation 1258 * The entity object of the translation just stored. 1259 * 1260 * @ingroup entity_crud 1261 * @see hook_ENTITY_TYPE_translation_insert() 1262 */ 1263function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) { 1264 $variables = [ 1265 '@language' => $translation->language()->getName(), 1266 '@label' => $translation->getUntranslated()->label(), 1267 ]; 1268 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables); 1269} 1270 1271/** 1272 * Respond to creation of a new entity translation of a particular type. 1273 * 1274 * This hook runs once the entity translation has been stored. Note that hook 1275 * implementations may not alter the stored entity translation data. 1276 * 1277 * @param \Drupal\Core\Entity\EntityInterface $translation 1278 * The entity object of the translation just stored. 1279 * 1280 * @ingroup entity_crud 1281 * @see hook_entity_translation_insert() 1282 */ 1283function hook_ENTITY_TYPE_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) { 1284 $variables = [ 1285 '@language' => $translation->language()->getName(), 1286 '@label' => $translation->getUntranslated()->label(), 1287 ]; 1288 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables); 1289} 1290 1291/** 1292 * Respond to entity translation deletion. 1293 * 1294 * This hook runs once the entity translation has been deleted from storage. 1295 * 1296 * @param \Drupal\Core\Entity\EntityInterface $translation 1297 * The original entity object. 1298 * 1299 * @ingroup entity_crud 1300 * @see hook_ENTITY_TYPE_translation_delete() 1301 */ 1302function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) { 1303 $variables = [ 1304 '@language' => $translation->language()->getName(), 1305 '@label' => $translation->label(), 1306 ]; 1307 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables); 1308} 1309 1310/** 1311 * Respond to entity translation deletion of a particular type. 1312 * 1313 * This hook runs once the entity translation has been deleted from storage. 1314 * 1315 * @param \Drupal\Core\Entity\EntityInterface $translation 1316 * The original entity object. 1317 * 1318 * @ingroup entity_crud 1319 * @see hook_entity_translation_delete() 1320 */ 1321function hook_ENTITY_TYPE_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) { 1322 $variables = [ 1323 '@language' => $translation->language()->getName(), 1324 '@label' => $translation->label(), 1325 ]; 1326 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables); 1327} 1328 1329/** 1330 * Act before entity deletion. 1331 * 1332 * @param \Drupal\Core\Entity\EntityInterface $entity 1333 * The entity object for the entity that is about to be deleted. 1334 * 1335 * @ingroup entity_crud 1336 * @see hook_ENTITY_TYPE_predelete() 1337 */ 1338function hook_entity_predelete(\Drupal\Core\Entity\EntityInterface $entity) { 1339 $connection = \Drupal::database(); 1340 // Count references to this entity in a custom table before they are removed 1341 // upon entity deletion. 1342 $id = $entity->id(); 1343 $type = $entity->getEntityTypeId(); 1344 $count = \Drupal::database()->select('example_entity_data') 1345 ->condition('type', $type) 1346 ->condition('id', $id) 1347 ->countQuery() 1348 ->execute() 1349 ->fetchField(); 1350 1351 // Log the count in a table that records this statistic for deleted entities. 1352 $connection->merge('example_deleted_entity_statistics') 1353 ->key(['type' => $type, 'id' => $id]) 1354 ->fields(['count' => $count]) 1355 ->execute(); 1356} 1357 1358/** 1359 * Act before entity deletion of a particular entity type. 1360 * 1361 * @param \Drupal\Core\Entity\EntityInterface $entity 1362 * The entity object for the entity that is about to be deleted. 1363 * 1364 * @ingroup entity_crud 1365 * @see hook_entity_predelete() 1366 */ 1367function hook_ENTITY_TYPE_predelete(\Drupal\Core\Entity\EntityInterface $entity) { 1368 $connection = \Drupal::database(); 1369 // Count references to this entity in a custom table before they are removed 1370 // upon entity deletion. 1371 $id = $entity->id(); 1372 $type = $entity->getEntityTypeId(); 1373 $count = \Drupal::database()->select('example_entity_data') 1374 ->condition('type', $type) 1375 ->condition('id', $id) 1376 ->countQuery() 1377 ->execute() 1378 ->fetchField(); 1379 1380 // Log the count in a table that records this statistic for deleted entities. 1381 $connection->merge('example_deleted_entity_statistics') 1382 ->key(['type' => $type, 'id' => $id]) 1383 ->fields(['count' => $count]) 1384 ->execute(); 1385} 1386 1387/** 1388 * Respond to entity deletion. 1389 * 1390 * This hook runs once the entity has been deleted from the storage. 1391 * 1392 * @param \Drupal\Core\Entity\EntityInterface $entity 1393 * The entity object for the entity that has been deleted. 1394 * 1395 * @ingroup entity_crud 1396 * @see hook_ENTITY_TYPE_delete() 1397 */ 1398function hook_entity_delete(\Drupal\Core\Entity\EntityInterface $entity) { 1399 // Delete the entity's entry from a fictional table of all entities. 1400 \Drupal::database()->delete('example_entity') 1401 ->condition('type', $entity->getEntityTypeId()) 1402 ->condition('id', $entity->id()) 1403 ->execute(); 1404} 1405 1406/** 1407 * Respond to entity deletion of a particular type. 1408 * 1409 * This hook runs once the entity has been deleted from the storage. 1410 * 1411 * @param \Drupal\Core\Entity\EntityInterface $entity 1412 * The entity object for the entity that has been deleted. 1413 * 1414 * @ingroup entity_crud 1415 * @see hook_entity_delete() 1416 */ 1417function hook_ENTITY_TYPE_delete(\Drupal\Core\Entity\EntityInterface $entity) { 1418 // Delete the entity's entry from a fictional table of all entities. 1419 \Drupal::database()->delete('example_entity') 1420 ->condition('type', $entity->getEntityTypeId()) 1421 ->condition('id', $entity->id()) 1422 ->execute(); 1423} 1424 1425/** 1426 * Respond to entity revision deletion. 1427 * 1428 * This hook runs once the entity revision has been deleted from the storage. 1429 * 1430 * @param \Drupal\Core\Entity\EntityInterface $entity 1431 * The entity object for the entity revision that has been deleted. 1432 * 1433 * @ingroup entity_crud 1434 * @see hook_ENTITY_TYPE_revision_delete() 1435 */ 1436function hook_entity_revision_delete(\Drupal\Core\Entity\EntityInterface $entity) { 1437 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity); 1438 foreach ($referenced_files_by_field as $field => $uuids) { 1439 _editor_delete_file_usage($uuids, $entity, 1); 1440 } 1441} 1442 1443/** 1444 * Respond to entity revision deletion of a particular type. 1445 * 1446 * This hook runs once the entity revision has been deleted from the storage. 1447 * 1448 * @param \Drupal\Core\Entity\EntityInterface $entity 1449 * The entity object for the entity revision that has been deleted. 1450 * 1451 * @ingroup entity_crud 1452 * @see hook_entity_revision_delete() 1453 */ 1454function hook_ENTITY_TYPE_revision_delete(\Drupal\Core\Entity\EntityInterface $entity) { 1455 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity); 1456 foreach ($referenced_files_by_field as $field => $uuids) { 1457 _editor_delete_file_usage($uuids, $entity, 1); 1458 } 1459} 1460 1461/** 1462 * Act on entities being assembled before rendering. 1463 * 1464 * @param &$build 1465 * A renderable array representing the entity content. The module may add 1466 * elements to $build prior to rendering. The structure of $build is a 1467 * renderable array as expected by 1468 * \Drupal\Core\Render\RendererInterface::render(). 1469 * @param \Drupal\Core\Entity\EntityInterface $entity 1470 * The entity object. 1471 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display 1472 * The entity view display holding the display options configured for the 1473 * entity components. 1474 * @param $view_mode 1475 * The view mode the entity is rendered in. 1476 * 1477 * @see hook_entity_view_alter() 1478 * @see hook_ENTITY_TYPE_view() 1479 * 1480 * @ingroup entity_crud 1481 */ 1482function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) { 1483 // Only do the extra work if the component is configured to be displayed. 1484 // This assumes a 'mymodule_addition' extra field has been defined for the 1485 // entity bundle in hook_entity_extra_field_info(). 1486 if ($display->getComponent('mymodule_addition')) { 1487 $build['mymodule_addition'] = [ 1488 '#markup' => mymodule_addition($entity), 1489 '#theme' => 'mymodule_my_additional_field', 1490 ]; 1491 } 1492} 1493 1494/** 1495 * Act on entities of a particular type being assembled before rendering. 1496 * 1497 * @param &$build 1498 * A renderable array representing the entity content. The module may add 1499 * elements to $build prior to rendering. The structure of $build is a 1500 * renderable array as expected by 1501 * \Drupal\Core\Render\RendererInterface::render(). 1502 * @param \Drupal\Core\Entity\EntityInterface $entity 1503 * The entity object. 1504 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display 1505 * The entity view display holding the display options configured for the 1506 * entity components. 1507 * @param $view_mode 1508 * The view mode the entity is rendered in. 1509 * 1510 * @see hook_ENTITY_TYPE_view_alter() 1511 * @see hook_entity_view() 1512 * 1513 * @ingroup entity_crud 1514 */ 1515function hook_ENTITY_TYPE_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) { 1516 // Only do the extra work if the component is configured to be displayed. 1517 // This assumes a 'mymodule_addition' extra field has been defined for the 1518 // entity bundle in hook_entity_extra_field_info(). 1519 if ($display->getComponent('mymodule_addition')) { 1520 $build['mymodule_addition'] = [ 1521 '#markup' => mymodule_addition($entity), 1522 '#theme' => 'mymodule_my_additional_field', 1523 ]; 1524 } 1525} 1526 1527/** 1528 * Alter the results of the entity build array. 1529 * 1530 * This hook is called after the content has been assembled in a structured 1531 * array and may be used for doing processing which requires that the complete 1532 * entity content structure has been built. 1533 * 1534 * If a module wishes to act on the rendered HTML of the entity rather than the 1535 * structured content array, it may use this hook to add a #post_render 1536 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for 1537 * the particular entity type template, if there is one (e.g., node.html.twig). 1538 * 1539 * See the @link themeable Default theme implementations topic @endlink and 1540 * \Drupal\Core\Render\RendererInterface::render() for details. 1541 * 1542 * @param array &$build 1543 * A renderable array representing the entity content. 1544 * @param \Drupal\Core\Entity\EntityInterface $entity 1545 * The entity object being rendered. 1546 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display 1547 * The entity view display holding the display options configured for the 1548 * entity components. 1549 * 1550 * @ingroup entity_crud 1551 * 1552 * @see hook_entity_view() 1553 * @see hook_ENTITY_TYPE_view_alter() 1554 */ 1555function hook_entity_view_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { 1556 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { 1557 // Change its weight. 1558 $build['an_additional_field']['#weight'] = -10; 1559 1560 // Add a #post_render callback to act on the rendered HTML of the entity. 1561 // The object must implement \Drupal\Core\Security\TrustedCallbackInterface. 1562 $build['#post_render'][] = '\Drupal\my_module\NodeCallback::postRender'; 1563 } 1564} 1565 1566/** 1567 * Alter the results of the entity build array for a particular entity type. 1568 * 1569 * This hook is called after the content has been assembled in a structured 1570 * array and may be used for doing processing which requires that the complete 1571 * entity content structure has been built. 1572 * 1573 * If a module wishes to act on the rendered HTML of the entity rather than the 1574 * structured content array, it may use this hook to add a #post_render 1575 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for 1576 * the particular entity type template, if there is one (e.g., node.html.twig). 1577 * 1578 * See the @link themeable Default theme implementations topic @endlink and 1579 * \Drupal\Core\Render\RendererInterface::render() for details. 1580 * 1581 * @param array &$build 1582 * A renderable array representing the entity content. 1583 * @param \Drupal\Core\Entity\EntityInterface $entity 1584 * The entity object being rendered. 1585 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display 1586 * The entity view display holding the display options configured for the 1587 * entity components. 1588 * 1589 * @ingroup entity_crud 1590 * 1591 * @see hook_ENTITY_TYPE_view() 1592 * @see hook_entity_view_alter() 1593 */ 1594function hook_ENTITY_TYPE_view_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) { 1595 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { 1596 // Change its weight. 1597 $build['an_additional_field']['#weight'] = -10; 1598 1599 // Add a #post_render callback to act on the rendered HTML of the entity. 1600 $build['#post_render'][] = 'my_module_node_post_render'; 1601 } 1602} 1603 1604/** 1605 * Act on entities as they are being prepared for view. 1606 * 1607 * Allows you to operate on multiple entities as they are being prepared for 1608 * view. Only use this if attaching the data during the entity loading phase 1609 * is not appropriate, for example when attaching other 'entity' style objects. 1610 * 1611 * @param string $entity_type_id 1612 * The type of entities being viewed (i.e. node, user, comment). 1613 * @param array $entities 1614 * The entities keyed by entity ID. 1615 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays 1616 * The array of entity view displays holding the display options configured 1617 * for the entity components, keyed by bundle name. 1618 * @param string $view_mode 1619 * The view mode. 1620 * 1621 * @ingroup entity_crud 1622 */ 1623function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) { 1624 // Load a specific node into the user object for later theming. 1625 if (!empty($entities) && $entity_type_id == 'user') { 1626 // Only do the extra work if the component is configured to be 1627 // displayed. This assumes a 'mymodule_addition' extra field has been 1628 // defined for the entity bundle in hook_entity_extra_field_info(). 1629 $ids = []; 1630 foreach ($entities as $id => $entity) { 1631 if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) { 1632 $ids[] = $id; 1633 } 1634 } 1635 if ($ids) { 1636 $nodes = mymodule_get_user_nodes($ids); 1637 foreach ($ids as $id) { 1638 $entities[$id]->user_node = $nodes[$id]; 1639 } 1640 } 1641 } 1642} 1643 1644/** 1645 * Change the view mode of an entity that is being displayed. 1646 * 1647 * @param string $view_mode 1648 * The view_mode that is to be used to display the entity. 1649 * @param \Drupal\Core\Entity\EntityInterface $entity 1650 * The entity that is being viewed. 1651 * 1652 * @ingroup entity_crud 1653 */ 1654function hook_entity_view_mode_alter(&$view_mode, \Drupal\Core\Entity\EntityInterface $entity) { 1655 // For nodes, change the view mode when it is teaser. 1656 if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') { 1657 $view_mode = 'my_custom_view_mode'; 1658 } 1659} 1660 1661/** 1662 * Alter entity renderable values before cache checking during rendering. 1663 * 1664 * Invoked for a specific entity type. 1665 * 1666 * The values in the #cache key of the renderable array are used to determine if 1667 * a cache entry exists for the entity's rendered output. Ideally only values 1668 * that pertain to caching should be altered in this hook. 1669 * 1670 * @param array &$build 1671 * A renderable array containing the entity's caching and view mode values. 1672 * @param \Drupal\Core\Entity\EntityInterface $entity 1673 * The entity that is being viewed. 1674 * @param string $view_mode 1675 * The view_mode that is to be used to display the entity. 1676 * 1677 * @see \Drupal\Core\Render\RendererInterface::render() 1678 * @see \Drupal\Core\Entity\EntityViewBuilder 1679 * @see hook_entity_build_defaults_alter() 1680 * 1681 * @ingroup entity_crud 1682 */ 1683function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) { 1684 1685} 1686 1687/** 1688 * Alter entity renderable values before cache checking during rendering. 1689 * 1690 * The values in the #cache key of the renderable array are used to determine if 1691 * a cache entry exists for the entity's rendered output. Ideally only values 1692 * that pertain to caching should be altered in this hook. 1693 * 1694 * @param array &$build 1695 * A renderable array containing the entity's caching and view mode values. 1696 * @param \Drupal\Core\Entity\EntityInterface $entity 1697 * The entity that is being viewed. 1698 * @param string $view_mode 1699 * The view_mode that is to be used to display the entity. 1700 * 1701 * @see \Drupal\Core\Render\RendererInterface::render() 1702 * @see \Drupal\Core\Entity\EntityViewBuilder 1703 * @see hook_ENTITY_TYPE_build_defaults_alter() 1704 * 1705 * @ingroup entity_crud 1706 */ 1707function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) { 1708 1709} 1710 1711/** 1712 * Alter the settings used for displaying an entity. 1713 * 1714 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display 1715 * The entity view display that will be used to display the entity 1716 * components. 1717 * @param array $context 1718 * An associative array containing: 1719 * - entity_type: The entity type, e.g., 'node' or 'user'. 1720 * - bundle: The bundle, e.g., 'page' or 'article'. 1721 * - view_mode: The view mode, e.g., 'full', 'teaser', etc. 1722 * 1723 * @ingroup entity_crud 1724 */ 1725function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) { 1726 // Leave field labels out of the search index. 1727 if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') { 1728 foreach ($display->getComponents() as $name => $options) { 1729 if (isset($options['label'])) { 1730 $options['label'] = 'hidden'; 1731 $display->setComponent($name, $options); 1732 } 1733 } 1734 } 1735} 1736 1737/** 1738 * Alter the render array generated by an EntityDisplay for an entity. 1739 * 1740 * @param array $build 1741 * The renderable array generated by the EntityDisplay. 1742 * @param array $context 1743 * An associative array containing: 1744 * - entity: The entity being rendered. 1745 * - view_mode: The view mode; for example, 'full' or 'teaser'. 1746 * - display: The EntityDisplay holding the display options. 1747 * 1748 * @ingroup entity_crud 1749 */ 1750function hook_entity_display_build_alter(&$build, $context) { 1751 // Append RDF term mappings on displayed taxonomy links. 1752 foreach (Element::children($build) as $field_name) { 1753 $element = &$build[$field_name]; 1754 if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') { 1755 foreach ($element['#items'] as $delta => $item) { 1756 $term = $item->entity; 1757 if (!empty($term->rdf_mapping['rdftype'])) { 1758 $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; 1759 } 1760 if (!empty($term->rdf_mapping['name']['predicates'])) { 1761 $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; 1762 } 1763 } 1764 } 1765 } 1766} 1767 1768/** 1769 * Acts on an entity object about to be shown on an entity form. 1770 * 1771 * This can be typically used to pre-fill entity values or change the form state 1772 * before the entity form is built. It is invoked just once when first building 1773 * the entity form. Rebuilds will not trigger a new invocation. 1774 * 1775 * @param \Drupal\Core\Entity\EntityInterface $entity 1776 * The entity that is about to be shown on the form. 1777 * @param $operation 1778 * The current operation. 1779 * @param \Drupal\Core\Form\FormStateInterface $form_state 1780 * The current state of the form. 1781 * 1782 * @see \Drupal\Core\Entity\EntityForm::prepareEntity() 1783 * @see hook_ENTITY_TYPE_prepare_form() 1784 * 1785 * @ingroup entity_crud 1786 */ 1787function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) { 1788 if ($operation == 'edit') { 1789 $entity->label->value = 'Altered label'; 1790 $form_state->set('label_altered', TRUE); 1791 } 1792} 1793 1794/** 1795 * Acts on a particular type of entity object about to be in an entity form. 1796 * 1797 * This can be typically used to pre-fill entity values or change the form state 1798 * before the entity form is built. It is invoked just once when first building 1799 * the entity form. Rebuilds will not trigger a new invocation. 1800 * 1801 * @param \Drupal\Core\Entity\EntityInterface $entity 1802 * The entity that is about to be shown on the form. 1803 * @param $operation 1804 * The current operation. 1805 * @param \Drupal\Core\Form\FormStateInterface $form_state 1806 * The current state of the form. 1807 * 1808 * @see \Drupal\Core\Entity\EntityForm::prepareEntity() 1809 * @see hook_entity_prepare_form() 1810 * 1811 * @ingroup entity_crud 1812 */ 1813function hook_ENTITY_TYPE_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) { 1814 if ($operation == 'edit') { 1815 $entity->label->value = 'Altered label'; 1816 $form_state->set('label_altered', TRUE); 1817 } 1818} 1819 1820/** 1821 * Change the form mode used to build an entity form. 1822 * 1823 * @param string $form_mode 1824 * The form_mode that is to be used to build the entity form. 1825 * @param \Drupal\Core\Entity\EntityInterface $entity 1826 * The entity for which the form is being built. 1827 * 1828 * @ingroup entity_crud 1829 */ 1830function hook_entity_form_mode_alter(&$form_mode, \Drupal\Core\Entity\EntityInterface $entity) { 1831 // Change the form mode for users with Administrator role. 1832 if ($entity->getEntityTypeId() == 'user' && $entity->hasRole('administrator')) { 1833 $form_mode = 'my_custom_form_mode'; 1834 } 1835} 1836 1837/** 1838 * Alter the settings used for displaying an entity form. 1839 * 1840 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display 1841 * The entity_form_display object that will be used to display the entity form 1842 * components. 1843 * @param array $context 1844 * An associative array containing: 1845 * - entity_type: The entity type, e.g., 'node' or 'user'. 1846 * - bundle: The bundle, e.g., 'page' or 'article'. 1847 * - form_mode: The form mode; e.g., 'default', 'profile', 'register', etc. 1848 * 1849 * @ingroup entity_crud 1850 */ 1851function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) { 1852 // Hide the 'user_picture' field from the register form. 1853 if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') { 1854 $form_display->setComponent('user_picture', [ 1855 'region' => 'hidden', 1856 ]); 1857 } 1858} 1859 1860/** 1861 * Provides custom base field definitions for a content entity type. 1862 * 1863 * Field (storage) definitions returned by this hook must run through the 1864 * regular field storage life-cycle operations: they need to be properly 1865 * installed, updated, and uninstalled. This would typically be done through the 1866 * Entity Update API provided by the entity definition update manager. 1867 * 1868 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 1869 * The entity type definition. 1870 * 1871 * @return \Drupal\Core\Field\FieldDefinitionInterface[] 1872 * An array of field definitions, keyed by field name. 1873 * 1874 * @see hook_entity_base_field_info_alter() 1875 * @see hook_entity_bundle_field_info() 1876 * @see hook_entity_bundle_field_info_alter() 1877 * @see \Drupal\Core\Field\FieldDefinitionInterface 1878 * @see \Drupal\Core\Entity\EntityFieldManagerInterface::getFieldDefinitions() 1879 * @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface 1880 * @see https://www.drupal.org/node/3034742 1881 */ 1882function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) { 1883 if ($entity_type->id() == 'node') { 1884 $fields = []; 1885 $fields['mymodule_text'] = BaseFieldDefinition::create('string') 1886 ->setLabel(t('The text')) 1887 ->setDescription(t('A text property added by mymodule.')) 1888 ->setComputed(TRUE) 1889 ->setClass('\Drupal\mymodule\EntityComputedText'); 1890 1891 return $fields; 1892 } 1893} 1894 1895/** 1896 * Alter base field definitions for a content entity type. 1897 * 1898 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields 1899 * The array of base field definitions for the entity type. 1900 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 1901 * The entity type definition. 1902 * 1903 * @see hook_entity_base_field_info() 1904 * @see hook_entity_bundle_field_info() 1905 * @see hook_entity_bundle_field_info_alter() 1906 * 1907 * @todo WARNING: This hook will be changed in 1908 * https://www.drupal.org/node/2346329. 1909 */ 1910function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) { 1911 // Alter the mymodule_text field to use a custom class. 1912 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) { 1913 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText'); 1914 } 1915} 1916 1917/** 1918 * Provides field definitions for a specific bundle within an entity type. 1919 * 1920 * Bundle fields either have to override an existing base field, or need to 1921 * provide a field storage definition via hook_entity_field_storage_info() 1922 * unless they are computed. 1923 * 1924 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 1925 * The entity type definition. 1926 * @param string $bundle 1927 * The bundle. 1928 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions 1929 * The list of base field definitions for the entity type. 1930 * 1931 * @return \Drupal\Core\Field\FieldDefinitionInterface[] 1932 * An array of bundle field definitions, keyed by field name. 1933 * 1934 * @see hook_entity_base_field_info() 1935 * @see hook_entity_base_field_info_alter() 1936 * @see hook_entity_field_storage_info() 1937 * @see hook_entity_field_storage_info_alter() 1938 * @see hook_entity_bundle_field_info_alter() 1939 * @see \Drupal\Core\Field\FieldDefinitionInterface 1940 * @see \Drupal\Core\Field\FieldDefinition 1941 * @see \Drupal\Core\Entity\EntityFieldManagerInterface::getFieldDefinitions() 1942 * 1943 * @todo WARNING: This hook will be changed in 1944 * https://www.drupal.org/node/2346347. 1945 */ 1946function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { 1947 // Add a property only to nodes of the 'article' bundle. 1948 if ($entity_type->id() == 'node' && $bundle == 'article') { 1949 $fields = []; 1950 $storage_definitions = mymodule_entity_field_storage_info($entity_type); 1951 $fields['mymodule_bundle_field'] = FieldDefinition::createFromFieldStorageDefinition($storage_definitions['mymodule_bundle_field']) 1952 ->setLabel(t('Bundle Field')); 1953 return $fields; 1954 } 1955 1956} 1957 1958/** 1959 * Alter bundle field definitions. 1960 * 1961 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields 1962 * The array of bundle field definitions. 1963 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 1964 * The entity type definition. 1965 * @param string $bundle 1966 * The bundle. 1967 * 1968 * @see hook_entity_base_field_info() 1969 * @see hook_entity_base_field_info_alter() 1970 * @see hook_entity_bundle_field_info() 1971 * 1972 * @todo WARNING: This hook will be changed in 1973 * https://www.drupal.org/node/2346347. 1974 */ 1975function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) { 1976 if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) { 1977 // Alter the mymodule_text field to use a custom class. 1978 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText'); 1979 } 1980} 1981 1982/** 1983 * Provides field storage definitions for a content entity type. 1984 * 1985 * Field storage definitions returned by this hook must run through the regular 1986 * field storage life-cycle operations: they need to be properly installed, 1987 * updated, and uninstalled. This would typically be done through the Entity 1988 * Update API provided by the entity definition update manager. 1989 * 1990 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 1991 * The entity type definition. 1992 * 1993 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[] 1994 * An array of field storage definitions, keyed by field name. 1995 * 1996 * @see hook_entity_field_storage_info_alter() 1997 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface 1998 * @see \Drupal\Core\Entity\EntityFieldManagerInterface::getFieldStorageDefinitions() 1999 * @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface 2000 * @see https://www.drupal.org/node/3034742 2001 */ 2002function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) { 2003 if (\Drupal::entityTypeManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) { 2004 // Query by filtering on the ID as this is more efficient than filtering 2005 // on the entity_type property directly. 2006 $ids = \Drupal::entityQuery('field_storage_config') 2007 ->condition('id', $entity_type->id() . '.', 'STARTS_WITH') 2008 ->execute(); 2009 // Fetch all fields and key them by field name. 2010 $field_storages = FieldStorageConfig::loadMultiple($ids); 2011 $result = []; 2012 foreach ($field_storages as $field_storage) { 2013 $result[$field_storage->getName()] = $field_storage; 2014 } 2015 2016 return $result; 2017 } 2018} 2019 2020/** 2021 * Alter field storage definitions for a content entity type. 2022 * 2023 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields 2024 * The array of field storage definitions for the entity type. 2025 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type 2026 * The entity type definition. 2027 * 2028 * @see hook_entity_field_storage_info() 2029 */ 2030function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) { 2031 // Alter the max_length setting. 2032 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) { 2033 $fields['mymodule_text']->setSetting('max_length', 128); 2034 } 2035} 2036 2037/** 2038 * Declares entity operations. 2039 * 2040 * @param \Drupal\Core\Entity\EntityInterface $entity 2041 * The entity on which the linked operations will be performed. 2042 * 2043 * @return array 2044 * An operations array as returned by 2045 * EntityListBuilderInterface::getOperations(). 2046 * 2047 * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations() 2048 */ 2049function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) { 2050 $operations = []; 2051 $operations['translate'] = [ 2052 'title' => t('Translate'), 2053 'url' => \Drupal\Core\Url::fromRoute('foo_module.entity.translate'), 2054 'weight' => 50, 2055 ]; 2056 2057 return $operations; 2058} 2059 2060/** 2061 * Alter entity operations. 2062 * 2063 * @param array $operations 2064 * Operations array as returned by 2065 * \Drupal\Core\Entity\EntityListBuilderInterface::getOperations(). 2066 * @param \Drupal\Core\Entity\EntityInterface $entity 2067 * The entity on which the linked operations will be performed. 2068 */ 2069function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) { 2070 // Alter the title and weight. 2071 $operations['translate']['title'] = t('Translate @entity_type', [ 2072 '@entity_type' => $entity->getEntityTypeId(), 2073 ]); 2074 $operations['translate']['weight'] = 99; 2075} 2076 2077/** 2078 * Control access to fields. 2079 * 2080 * This hook is invoked from 2081 * \Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess() to let modules 2082 * grant or deny operations on fields. 2083 * 2084 * @param string $operation 2085 * The operation to be performed. See 2086 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess() 2087 * for possible values. 2088 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition 2089 * The field definition. 2090 * @param \Drupal\Core\Session\AccountInterface $account 2091 * The user account to check. 2092 * @param \Drupal\Core\Field\FieldItemListInterface $items 2093 * (optional) The entity field object for which to check access, or NULL if 2094 * access is checked for the field definition, without any specific value 2095 * available. Defaults to NULL. 2096 * 2097 * @return \Drupal\Core\Access\AccessResultInterface 2098 * The access result. 2099 * 2100 * @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess() 2101 */ 2102function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) { 2103 if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') { 2104 return AccessResult::allowedIfHasPermission($account, 'update field of interest'); 2105 } 2106 return AccessResult::neutral(); 2107} 2108 2109/** 2110 * Alter the default access behavior for a given field. 2111 * 2112 * Use this hook to override access grants from another module. Note that the 2113 * original default access flag is masked under the ':default' key. 2114 * 2115 * @param \Drupal\Core\Access\AccessResultInterface[] $grants 2116 * An array of grants gathered by hook_entity_field_access(). The array is 2117 * keyed by the module that defines the field's access control; the values are 2118 * grant responses for each module (\Drupal\Core\Access\AccessResult). 2119 * @param array $context 2120 * Context array on the performed operation with the following keys: 2121 * - operation: The operation to be performed (string). 2122 * - field_definition: The field definition object 2123 * (\Drupal\Core\Field\FieldDefinitionInterface) 2124 * - account: The user account to check access for 2125 * (Drupal\user\Entity\User). 2126 * - items: (optional) The entity field items 2127 * (\Drupal\Core\Field\FieldItemListInterface). 2128 */ 2129function hook_entity_field_access_alter(array &$grants, array $context) { 2130 /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */ 2131 $field_definition = $context['field_definition']; 2132 if ($field_definition->getName() == 'field_of_interest' && $grants['node']->isForbidden()) { 2133 // Override node module's restriction to no opinion (neither allowed nor 2134 // forbidden). We don't want to provide our own access hook, we only want to 2135 // take out node module's part in the access handling of this field. We also 2136 // don't want to switch node module's grant to 2137 // AccessResultInterface::isAllowed() , because the grants of other modules 2138 // should still decide on their own if this field is accessible or not 2139 $grants['node'] = AccessResult::neutral()->inheritCacheability($grants['node']); 2140 } 2141} 2142 2143/** 2144 * Acts when initializing a fieldable entity object. 2145 * 2146 * This hook runs after a new entity object or a new entity translation object 2147 * has just been instantiated. It can be used to set initial values, e.g. to 2148 * provide defaults. 2149 * 2150 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity 2151 * The entity object. 2152 * 2153 * @ingroup entity_crud 2154 * @see hook_ENTITY_TYPE_field_values_init() 2155 */ 2156function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) { 2157 if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) { 2158 $entity->foo->value = 'some_initial_value'; 2159 } 2160} 2161 2162/** 2163 * Acts when initializing a fieldable entity object. 2164 * 2165 * This hook runs after a new entity object or a new entity translation object 2166 * has just been instantiated. It can be used to set initial values, e.g. to 2167 * provide defaults. 2168 * 2169 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity 2170 * The entity object. 2171 * 2172 * @ingroup entity_crud 2173 * @see hook_entity_field_values_init() 2174 */ 2175function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) { 2176 if (!$entity->foo->value) { 2177 $entity->foo->value = 'some_initial_value'; 2178 } 2179} 2180 2181/** 2182 * Exposes "pseudo-field" components on content entities. 2183 * 2184 * Field UI's "Manage fields" and "Manage display" pages let users re-order 2185 * fields, but also non-field components. For nodes, these include elements 2186 * exposed by modules through hook_form_alter(), for instance. 2187 * 2188 * Content entities or modules that want to have their components supported 2189 * should expose them using this hook. The user-defined settings (weight, 2190 * visible) are automatically applied when entities or entity forms are 2191 * rendered. 2192 * 2193 * @see hook_entity_extra_field_info_alter() 2194 * 2195 * @return array 2196 * The array structure is identical to that of the return value of 2197 * \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields(). 2198 */ 2199function hook_entity_extra_field_info() { 2200 $extra = []; 2201 $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language'); 2202 $description = t('Node module element'); 2203 2204 foreach (NodeType::loadMultiple() as $bundle) { 2205 2206 // Add also the 'language' select if Language module is enabled and the 2207 // bundle has multilingual support. 2208 // Visibility of the ordering of the language selector is the same as on the 2209 // node/add form. 2210 if ($module_language_enabled) { 2211 $configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id()); 2212 if ($configuration->isLanguageAlterable()) { 2213 $extra['node'][$bundle->id()]['form']['language'] = [ 2214 'label' => t('Language'), 2215 'description' => $description, 2216 'weight' => 0, 2217 ]; 2218 } 2219 } 2220 $extra['node'][$bundle->id()]['display']['language'] = [ 2221 'label' => t('Language'), 2222 'description' => $description, 2223 'weight' => 0, 2224 'visible' => FALSE, 2225 ]; 2226 } 2227 2228 return $extra; 2229} 2230 2231/** 2232 * Alter "pseudo-field" components on content entities. 2233 * 2234 * @param array $info 2235 * The array structure is identical to that of the return value of 2236 * \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields(). 2237 * 2238 * @see hook_entity_extra_field_info() 2239 */ 2240function hook_entity_extra_field_info_alter(&$info) { 2241 // Force node title to always be at the top of the list by default. 2242 foreach (NodeType::loadMultiple() as $bundle) { 2243 if (isset($info['node'][$bundle->id()]['form']['title'])) { 2244 $info['node'][$bundle->id()]['form']['title']['weight'] = -20; 2245 } 2246 } 2247} 2248 2249/** 2250 * @} End of "addtogroup hooks". 2251 */ 2252