5 * Hooks and documentation related to entities.
8 use Drupal\Core\Access\AccessResult;
9 use Drupal\Core\Entity\ContentEntityInterface;
10 use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface;
11 use Drupal\Core\Field\BaseFieldDefinition;
12 use Drupal\Core\Render\Element;
13 use Drupal\language\Entity\ContentLanguageSettings;
14 use Drupal\node\Entity\NodeType;
17 * @defgroup entity_crud Entity CRUD, editing, and view hooks
19 * Hooks used in various entity operations.
21 * Entity create, read, update, and delete (CRUD) operations are performed by
22 * entity storage classes; see the
23 * @link entity_api Entity API topic @endlink for more information. Most
24 * entities use or extend the default classes:
25 * \Drupal\Core\Entity\Sql\SqlContentEntityStorage for content entities, and
26 * \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities.
27 * For these entities, there is a set of hooks that is invoked for each
28 * CRUD operation, which module developers can implement to affect these
29 * operations; these hooks are actually invoked from methods on
30 * \Drupal\Core\Entity\EntityStorageBase.
32 * For content entities, viewing and rendering are handled by a view builder
33 * class; see the @link entity_api Entity API topic @endlink for more
34 * information. Most view builders extend or use the default class
35 * \Drupal\Core\Entity\EntityViewBuilder.
37 * Entity editing (including adding new entities) is handled by entity form
38 * classes; see the @link entity_api Entity API topic @endlink for more
39 * information. Most entity editing forms extend base classes
40 * \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm.
41 * Note that many other operations, such as confirming deletion of entities,
42 * also use entity form classes.
44 * This topic lists all of the entity CRUD and view operations, and the hooks
45 * and other operations that are invoked (in order) for each operation. Some
47 * - Whenever an entity hook is invoked, there is both a type-specific entity
48 * hook, and a generic entity hook. For instance, during a create operation on
49 * a node, first hook_node_create() and then hook_entity_create() would be
51 * - The entity-type-specific hooks are represented in the list below as
52 * hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To
53 * implement one of these hooks for an entity whose machine name is "foo",
54 * define a function called mymodule_foo_create(), for instance. Also note
55 * that the entity or array of entities that are passed into a specific-type
56 * hook are of the specific entity class, not the generic Entity class, so in
57 * your implementation, you can make the $entity argument something like $node
58 * and give it a specific type hint (which should normally be to the specific
59 * interface, such as \Drupal\Node\NodeInterface for nodes).
60 * - $storage in the code examples is assumed to be an entity storage
61 * class. See the @link entity_api Entity API topic @endlink for
62 * information on how to instantiate the correct storage class for an
64 * - $view_builder in the code examples is assumed to be an entity view builder
65 * class. See the @link entity_api Entity API topic @endlink for
66 * information on how to instantiate the correct view builder class for
68 * - During many operations, static methods are called on the entity class,
69 * which implements \Drupal\Entity\EntityInterface.
71 * @section create Create operations
72 * To create an entity:
74 * $entity = $storage->create();
76 * // Add code here to set properties on the entity.
78 * // Until you call save(), the entity is just in memory.
81 * There is also a shortcut method on entity classes, which creates an entity
82 * with an array of provided property values: \Drupal\Core\Entity::create().
84 * Hooks invoked during the create operation:
85 * - hook_ENTITY_TYPE_create()
86 * - hook_entity_create()
88 * See @ref save below for the save portion of the operation.
90 * @section load Read/Load operations
91 * To load (read) a single entity:
93 * $entity = $storage->load($id);
95 * To load multiple entities:
97 * $entities = $storage->loadMultiple($ids);
99 * Since load() calls loadMultiple(), these are really the same operation.
100 * Here is the order of hooks and other operations that take place during
102 * - Entity is loaded from storage.
103 * - postLoad() is called on the entity class, passing in all of the loaded
105 * - hook_entity_load()
106 * - hook_ENTITY_TYPE_load()
108 * When an entity is loaded, normally the default entity revision is loaded.
109 * It is also possible to load a different revision, for entities that support
110 * revisions, with this code:
112 * $entity = $storage->loadRevision($revision_id);
114 * This involves the same hooks and operations as regular entity loading.
116 * @section save Save operations
117 * To update an existing entity, you will need to load it, change properties,
118 * and then save; as described above, when creating a new entity, you will also
119 * need to save it. Here is the order of hooks and other events that happen
120 * during an entity save:
121 * - preSave() is called on the entity object, and field objects.
122 * - hook_ENTITY_TYPE_presave()
123 * - hook_entity_presave()
124 * - Entity is saved to storage.
125 * - For updates on content entities, if there is a translation added that
126 * was not previously present:
127 * - hook_ENTITY_TYPE_translation_insert()
128 * - hook_entity_translation_insert()
129 * - For updates on content entities, if there was a translation removed:
130 * - hook_ENTITY_TYPE_translation_delete()
131 * - hook_entity_translation_delete()
132 * - postSave() is called on the entity object.
133 * - hook_ENTITY_TYPE_insert() (new) or hook_ENTITY_TYPE_update() (update)
134 * - hook_entity_insert() (new) or hook_entity_update() (update)
136 * Some specific entity types invoke hooks during preSave() or postSave()
137 * operations. Examples:
138 * - Field configuration preSave(): hook_field_storage_config_update_forbid()
139 * - Node postSave(): hook_node_access_records() and
140 * hook_node_access_records_alter()
141 * - Config entities that are acting as entity bundles in postSave():
142 * hook_entity_bundle_create()
143 * - Comment: hook_comment_publish() and hook_comment_unpublish() as
146 * @section edit Editing operations
147 * When an entity's add/edit form is used to add or edit an entity, there
148 * are several hooks that are invoked:
149 * - hook_entity_prepare_form()
150 * - hook_ENTITY_TYPE_prepare_form()
151 * - hook_entity_form_display_alter() (for content entities only)
153 * @section delete Delete operations
154 * To delete one or more entities, load them and then delete them:
156 * $entities = $storage->loadMultiple($ids);
157 * $storage->delete($entities);
160 * During the delete operation, the following hooks and other events happen:
161 * - preDelete() is called on the entity class.
162 * - hook_ENTITY_TYPE_predelete()
163 * - hook_entity_predelete()
164 * - Entity and field information is removed from storage.
165 * - postDelete() is called on the entity class.
166 * - hook_ENTITY_TYPE_delete()
167 * - hook_entity_delete()
169 * Some specific entity types invoke hooks during the delete process. Examples:
170 * - Entity bundle postDelete(): hook_entity_bundle_delete()
172 * Individual revisions of an entity can also be deleted:
174 * $storage->deleteRevision($revision_id);
176 * This operation invokes the following operations and hooks:
177 * - Revision is loaded (see @ref load above).
178 * - Revision and field information is removed from the database.
179 * - hook_ENTITY_TYPE_revision_delete()
180 * - hook_entity_revision_delete()
182 * @section view View/render operations
183 * To make a render array for a loaded entity:
185 * // You can omit the language ID if the default language is being used.
186 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
188 * You can also use the viewMultiple() method to view multiple entities.
190 * Hooks invoked during the operation of building a render array:
191 * - hook_entity_view_mode_alter()
192 * - hook_ENTITY_TYPE_build_defaults_alter()
193 * - hook_entity_build_defaults_alter()
195 * View builders for some types override these hooks, notably:
196 * - The Tour view builder does not invoke any hooks.
197 * - The Block view builder invokes hook_block_view_alter() and
198 * hook_block_view_BASE_BLOCK_ID_alter(). Note that in other view builders,
199 * the view alter hooks are run later in the process.
201 * During the rendering operation, the default entity viewer runs the following
202 * hooks and operations in the pre-render step:
203 * - hook_entity_view_display_alter()
204 * - hook_entity_prepare_view()
205 * - Entity fields are loaded, and render arrays are built for them using
207 * - hook_entity_display_build_alter()
208 * - hook_ENTITY_TYPE_view()
209 * - hook_entity_view()
210 * - hook_ENTITY_TYPE_view_alter()
211 * - hook_entity_view_alter()
213 * Some specific builders have specific hooks:
214 * - The Node view builder invokes hook_node_links_alter().
215 * - The Comment view builder invokes hook_comment_links_alter().
217 * After this point in rendering, the theme system takes over. See the
218 * @link theme_render Theme system and render API topic @endlink for more
221 * @section misc Other entity hooks
222 * Some types of entities invoke hooks for specific operations:
225 * - Query is executed to find matching nodes
226 * - Resulting node is loaded
227 * - Node render array is built
228 * - comment_node_update_index() is called (this adds "N comments" text)
229 * - hook_node_search_result()
230 * - Search indexing nodes:
232 * - Node render array is built
233 * - hook_node_update_index()
238 * @defgroup entity_api Entity API
240 * Describes how to define and manipulate content and configuration entities.
242 * Entities, in Drupal, are objects that are used for persistent storage of
243 * content and configuration information. See the
244 * @link info_types Information types topic @endlink for an overview of the
245 * different types of information, and the
246 * @link config_api Configuration API topic @endlink for more about the
249 * Each entity is an instance of a particular "entity type". Some content entity
250 * types have sub-types, which are known as "bundles", while for other entity
251 * types, there is only a single bundle. For example, the Node content entity
252 * type, which is used for the main content pages in Drupal, has bundles that
253 * are known as "content types", while the User content type, which is used for
254 * user accounts, has only one bundle.
256 * The sections below have more information about entities and the Entity API;
257 * for more detailed information, see
258 * https://www.drupal.org/developing/api/entity.
260 * @section define Defining an entity type
261 * Entity types are defined by modules, using Drupal's Plugin API (see the
262 * @link plugin_api Plugin API topic @endlink for more information about plugins
263 * in general). Here are the steps to follow to define a new entity type:
264 * - Choose a unique machine name, or ID, for your entity type. This normally
265 * starts with (or is the same as) your module's machine name. It should be
266 * as short as possible, and may not exceed 32 characters.
267 * - Define an interface for your entity's get/set methods, usually extending
268 * either \Drupal\Core\Config\Entity\ConfigEntityInterface or
269 * \Drupal\Core\Entity\ContentEntityInterface.
270 * - Define a class for your entity, implementing your interface and extending
271 * either \Drupal\Core\Config\Entity\ConfigEntityBase or
272 * \Drupal\Core\Entity\ContentEntityBase, with annotation for
273 * \@ConfigEntityType or \@ContentEntityType in its documentation block.
274 * - The 'id' annotation gives the entity type ID, and the 'label' annotation
275 * gives the human-readable name of the entity type. If you are defining a
276 * content entity type that uses bundles, the 'bundle_label' annotation gives
277 * the human-readable name to use for a bundle of this entity type (for
278 * example, "Content type" for the Node entity).
279 * - The annotation will refer to several handler classes, which you will also
281 * - list_builder: Define a class that extends
282 * \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration
283 * entities) or \Drupal\Core\Entity\EntityListBuilder (for content
284 * entities), to provide an administrative overview for your entities.
285 * - add and edit forms, or default form: Define a class (or two) that
286 * extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms
287 * for your entities. For content entities, base class
288 * \Drupal\Core\Entity\ContentEntityForm is a better starting point.
289 * - delete form: Define a class that extends
290 * \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete
291 * confirmation form for your entities.
292 * - view_builder: For content entities and config entities that need to be
293 * viewed, define a class that implements
294 * \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
295 * \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
296 * - translation: For translatable content entities (if the 'translatable'
297 * annotation has value TRUE), define a class that extends
298 * \Drupal\content_translation\ContentTranslationHandler, to translate
299 * the content. Configuration translation is handled automatically by the
300 * Configuration Translation module, without the need of a handler class.
301 * - access: If your configuration entity has complex permissions, you might
302 * need an access control handling, implementing
303 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities
304 * can just use the 'admin_permission' annotation instead. Note that if you
305 * are creating your own access control handler, you should override the
306 * checkAccess() and checkCreateAccess() methods, not access().
307 * - storage: A class implementing
308 * \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
309 * entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
310 * config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage.
311 * You can extend one of these classes to provide custom behavior.
312 * - views_data: A class implementing \Drupal\views\EntityViewsDataInterface
313 * to provide views data for the entity type. You can autogenerate most of
314 * the views data by extending \Drupal\views\EntityViewsData.
315 * - For content entities, the annotation will refer to a number of database
316 * tables and their fields. These annotation properties, such as 'base_table',
317 * 'data_table', 'entity_keys', etc., are documented on
318 * \Drupal\Core\Entity\EntityType.
319 * - For content entities that are displayed on their own pages, the annotation
320 * will refer to a 'uri_callback' function, which takes an object of the
321 * entity interface you have defined as its parameter, and returns routing
322 * information for the entity page; see node_uri() for an example. You will
323 * also need to add a corresponding route to your module's routing.yml file;
324 * see the entity.node.canonical route in node.routing.yml for an example, and see
325 * @ref sec_routes below for some notes.
326 * - Optionally, instead of defining routes, routes can be auto generated by
327 * providing a route handler. See @ref sec_routes. Otherwise, define routes
328 * and links for the various URLs associated with the entity.
329 * These go into the 'links' annotation, with the link type as the key, and
330 * the path of this link template as the value. The corresponding route
331 * requires the following route name:
332 * "entity.$entity_type_id.$link_template_type". See @ref sec_routes below for
333 * some routing notes. Typical link types are:
334 * - canonical: Default link, either to view (if entities are viewed on their
335 * own pages) or edit the entity.
336 * - delete-form: Confirmation form to delete the entity.
337 * - edit-form: Editing form.
338 * - Other link types specific to your entity type can also be defined.
339 * - If your content entity is fieldable, provide 'field_ui_base_route'
340 * annotation, giving the name of the route that the Manage Fields, Manage
341 * Display, and Manage Form Display pages from the Field UI module will be
342 * attached to. This is usually the bundle settings edit page, or an entity
343 * type settings page if there are no bundles.
344 * - If your content entity has bundles, you will also need to define a second
345 * plugin to handle the bundles. This plugin is itself a configuration entity
346 * type, so follow the steps here to define it. The machine name ('id'
347 * annotation) of this configuration entity class goes into the
348 * 'bundle_entity_type' annotation on the entity type class. For example, for
349 * the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose
350 * machine name is 'node_type'. This is the annotation value for
351 * 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the
352 * bundle config entity type annotation must have a 'bundle_of' entry,
353 * giving the machine name of the entity type it is acting as a bundle for.
354 * These machine names are considered permanent, they may not be renamed.
355 * - Additional annotations can be seen on entity class examples such as
356 * \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
357 * (configuration). These annotations are documented on
358 * \Drupal\Core\Entity\EntityType.
360 * @section sec_routes Entity routes
361 * Entity routes can be defined in *.routing.yml files, like any other route:
362 * see the @link routing Routing API @endlink topic for more information.
363 * Another option for entity routes is to use a route provider class, and
364 * reference it in the annotations on the entity class: see the end of this
365 * section for an example.
367 * It's possible to use both a YAML file and a provider class for entity
368 * routes, at the same time. Avoid duplicating route names between the two:
369 * if a duplicate route name is found in both locations, the one in the YAML
370 * file takes precedence; regardless, such duplication can be confusing.
372 * Here's an example YAML route specification, for the block configure form:
374 * entity.block.edit_form:
375 * path: '/admin/structure/block/manage/{block}'
377 * _entity_form: 'block.default'
378 * _title: 'Configure block'
380 * _entity_access: 'block.update'
382 * Some notes on this example:
383 * - path: The {block} in the path is a placeholder, which (for an entity) must
384 * always take the form of {machine_name_of_entity_type}. In the URL, the
385 * placeholder value will be the ID of an entity item. When the route is used,
386 * the entity system will load the corresponding entity item and pass it in as
387 * an object to the controller for the route.
388 * - defaults: For entity form routes, use _entity_form rather than the generic
389 * _controller or _form. The value is composed of the entity type machine name
390 * and a form handler type from the entity annotation (see @ref define above
391 * more more on handlers and annotation). So, in this example, block.default
392 * refers to the 'default' form handler on the block entity type, whose
393 * annotation contains:
397 * "default" = "Drupal\block\BlockForm",
399 * If instead of YAML you want to use a route provider class:
400 * - \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical,
401 * edit-form, and delete-form routes.
402 * - \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same
403 * routes, set up to use the administrative theme for edit and delete pages.
404 * - You can also create your own class, extending one of these two classes if
405 * you only want to modify their behaviour slightly.
407 * To register any route provider class, add lines like the following to your
408 * entity class annotation:
411 * "route_provider" = {
412 * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
415 * @section bundle Defining a content entity bundle
416 * For entity types that use bundles, such as Node (bundles are content types)
417 * and Taxonomy (bundles are vocabularies), modules and install profiles can
418 * define bundles by supplying default configuration in their config/install
419 * directories. (See the @link config_api Configuration API topic @endlink for
420 * general information about configuration.)
422 * There are several good examples of this in Drupal Core:
423 * - The Forum module defines a content type in node.type.forum.yml and a
424 * vocabulary in taxonomy.vocabulary.forums.yml
425 * - The Book module defines a content type in node.type.book.yml
426 * - The Standard install profile defines Page and Article content types in
427 * node.type.page.yml and node.type.article.yml, a Tags vocabulary in
428 * taxonomy.vocabulary.tags.yml, and a Node comment type in
429 * comment.type.comment.yml. This profile's configuration is especially
430 * instructive, because it also adds several fields to the Article type, and
431 * it sets up view and form display modes for the node types.
433 * @section load_query Loading, querying, and rendering entities
434 * To load entities, use the entity storage manager, which is an object
435 * implementing \Drupal\Core\Entity\EntityStorageInterface that you can
438 * $storage = \Drupal::entityManager()->getStorage('your_entity_type');
439 * // Or if you have a $container variable:
440 * $storage = $container->get('entity.manager')->getStorage('your_entity_type');
442 * Here, 'your_entity_type' is the machine name of your entity type ('id'
443 * annotation on the entity class), and note that you should use dependency
444 * injection to retrieve this object if possible. See the
445 * @link container Services and Dependency Injection topic @endlink for more
446 * about how to properly retrieve services.
448 * To query to find entities to load, use an entity query, which is a object
449 * implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve
453 * $query = \Drupal::entityQuery('your_entity_type');
454 * // Or, if you have a $container variable:
455 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type');
456 * $query = $storage->getQuery();
458 * If you need aggregation, there is an aggregate query available, which
459 * implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
461 * $query \Drupal::entityQueryAggregate('your_entity_type');
463 * $query = $storage->getAggregateQuery('your_entity_type');
466 * In either case, you can then add conditions to your query, using methods
467 * like condition(), exists(), etc. on $query; add sorting, pager, and range
468 * if needed, and execute the query to return a list of entity IDs that match
471 * Here is an example, using the core File entity:
473 * $fids = Drupal::entityQuery('file')
474 * ->condition('status', FILE_STATUS_PERMANENT, '<>')
475 * ->condition('changed', REQUEST_TIME - $age, '<')
478 * $files = $storage->loadMultiple($fids);
481 * The normal way of viewing entities is by using a route, as described in the
482 * sections above. If for some reason you need to render an entity in code in a
483 * particular view mode, you can use an entity view builder, which is an object
484 * implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can
487 * $view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type');
488 * // Or if you have a $container variable:
489 * $view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type');
491 * Then, to build and render the entity:
493 * // You can omit the language ID, by default the current content language will
494 * // be used. If no translation is available for the current language, fallback
495 * // rules will be used.
496 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
497 * // $build is a render array.
498 * $rendered = drupal_render($build);
501 * @section sec_access Access checking on entities
502 * Entity types define their access permission scheme in their annotation.
503 * Access permissions can be quite complex, so you should not assume any
504 * particular permission scheme. Instead, once you have an entity object
505 * loaded, you can check for permission for a particular operation (such as
506 * 'view') at the entity or field level by calling:
508 * $entity->access($operation);
509 * $entity->nameOfField->access($operation);
511 * The interface related to access checking in entities and fields is
512 * \Drupal\Core\Access\AccessibleInterface.
514 * The default entity access control handler invokes two hooks while checking
515 * access on a single entity: hook_entity_access() is invoked first, and
516 * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name
517 * of the entity type). If no module returns a TRUE or FALSE value from
518 * either of these hooks, then the entity's default access checking takes
519 * place. For create operations (creating a new entity), the hooks that
520 * are invoked are hook_entity_create_access() and
521 * hook_ENTITY_TYPE_create_access() instead.
523 * The Node entity type has a complex system for determining access, which
524 * developers can interact with. This is described in the
525 * @link node_access Node access topic. @endlink
529 * @see \Drupal\Core\Entity\EntityManagerInterface::getTranslationFromContext()
539 * Control entity operation access.
541 * @param \Drupal\Core\Entity\EntityInterface $entity
542 * The entity to check access to.
543 * @param string $operation
544 * The operation that is to be performed on $entity.
545 * @param \Drupal\Core\Session\AccountInterface $account
546 * The account trying to access the entity.
548 * @return \Drupal\Core\Access\AccessResultInterface
549 * The access result. The final result is calculated by using
550 * \Drupal\Core\Access\AccessResultInterface::orIf() on the result of every
551 * hook_entity_access() and hook_ENTITY_TYPE_access() implementation, and the
552 * result of the entity-specific checkAccess() method in the entity access
553 * control handler. Be careful when writing generalized access checks shared
554 * between routing and entity checks: routing uses the andIf() operator. So
555 * returning an isNeutral() does not determine entity access at all but it
556 * always ends up denying access while routing.
558 * @see \Drupal\Core\Entity\EntityAccessControlHandler
559 * @see hook_entity_create_access()
560 * @see hook_ENTITY_TYPE_access()
562 * @ingroup entity_api
564 function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
566 return AccessResult::neutral();
570 * Control entity operation access for a specific entity type.
572 * @param \Drupal\Core\Entity\EntityInterface $entity
573 * The entity to check access to.
574 * @param string $operation
575 * The operation that is to be performed on $entity.
576 * @param \Drupal\Core\Session\AccountInterface $account
577 * The account trying to access the entity.
579 * @return \Drupal\Core\Access\AccessResultInterface
580 * The access result. hook_entity_access() has detailed documentation.
582 * @see \Drupal\Core\Entity\EntityAccessControlHandler
583 * @see hook_ENTITY_TYPE_create_access()
584 * @see hook_entity_access()
586 * @ingroup entity_api
588 function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
590 return AccessResult::neutral();
594 * Control entity create access.
596 * @param \Drupal\Core\Session\AccountInterface $account
597 * The account trying to access the entity.
598 * @param array $context
599 * An associative array of additional context values. By default it contains
600 * language and the entity type ID:
601 * - entity_type_id - the entity type ID.
602 * - langcode - the current language code.
603 * @param string $entity_bundle
604 * The entity bundle name.
606 * @return \Drupal\Core\Access\AccessResultInterface
609 * @see \Drupal\Core\Entity\EntityAccessControlHandler
610 * @see hook_entity_access()
611 * @see hook_ENTITY_TYPE_create_access()
613 * @ingroup entity_api
615 function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
617 return AccessResult::neutral();
621 * Control entity create access for a specific entity type.
623 * @param \Drupal\Core\Session\AccountInterface $account
624 * The account trying to access the entity.
625 * @param array $context
626 * An associative array of additional context values. By default it contains
628 * - langcode - the current language code.
629 * @param string $entity_bundle
630 * The entity bundle name.
632 * @return \Drupal\Core\Access\AccessResultInterface
635 * @see \Drupal\Core\Entity\EntityAccessControlHandler
636 * @see hook_ENTITY_TYPE_access()
637 * @see hook_entity_create_access()
639 * @ingroup entity_api
641 function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
643 return AccessResult::neutral();
647 * Add to entity type definitions.
649 * Modules may implement this hook to add information to defined entity types,
650 * as defined in \Drupal\Core\Entity\EntityTypeInterface.
652 * To alter existing information or to add information dynamically, use
653 * hook_entity_type_alter().
655 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
656 * An associative array of all entity type definitions, keyed by the entity
657 * type name. Passed by reference.
659 * @see \Drupal\Core\Entity\Entity
660 * @see \Drupal\Core\Entity\EntityTypeInterface
661 * @see hook_entity_type_alter()
663 function hook_entity_type_build(array &$entity_types) {
664 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
665 // Add a form for a custom node form without overriding the default
666 // node form. To override the default node form, use hook_entity_type_alter().
667 $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
671 * Alter the entity type definitions.
673 * Modules may implement this hook to alter the information that defines an
674 * entity type. All properties that are available in
675 * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally
676 * provided by modules can be altered here.
678 * Do not use this hook to add information to entity types, unless one of the
680 * - You are filling in default values.
681 * - You need to dynamically add information only in certain circumstances.
682 * - Your hook needs to run after hook_entity_type_build() implementations.
683 * Use hook_entity_type_build() instead in all other cases.
685 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
686 * An associative array of all entity type definitions, keyed by the entity
687 * type name. Passed by reference.
689 * @see \Drupal\Core\Entity\Entity
690 * @see \Drupal\Core\Entity\EntityTypeInterface
692 function hook_entity_type_alter(array &$entity_types) {
693 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
694 // Set the controller class for nodes to an alternate implementation of the
695 // Drupal\Core\Entity\EntityStorageInterface interface.
696 $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
700 * Alter the view modes for entity types.
702 * @param array $view_modes
703 * An array of view modes, keyed first by entity type, then by view mode name.
705 * @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
706 * @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
708 function hook_entity_view_mode_info_alter(&$view_modes) {
709 $view_modes['user']['full']['status'] = TRUE;
713 * Describe the bundles for entity types.
716 * An associative array of all entity bundles, keyed by the entity
717 * type name, and then the bundle name, with the following keys:
718 * - label: The human-readable name of the bundle.
719 * - uri_callback: The same as the 'uri_callback' key defined for the entity
720 * type in the EntityManager, but for the bundle only. When determining
721 * the URI of an entity, if a 'uri_callback' is defined for both the
722 * entity type and the bundle, the one for the bundle is used.
723 * - translatable: (optional) A boolean value specifying whether this bundle
724 * has translation support enabled. Defaults to FALSE.
726 * @see \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
727 * @see hook_entity_bundle_info_alter()
729 function hook_entity_bundle_info() {
730 $bundles['user']['user']['label'] = t('User');
735 * Alter the bundles for entity types.
737 * @param array $bundles
738 * An array of bundles, keyed first by entity type, then by bundle name.
740 * @see Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
741 * @see hook_entity_bundle_info()
743 function hook_entity_bundle_info_alter(&$bundles) {
744 $bundles['user']['user']['label'] = t('Full account');
748 * Act on entity_bundle_create().
750 * This hook is invoked after the operation has been performed.
752 * @param string $entity_type_id
753 * The type of $entity; e.g. 'node' or 'user'.
754 * @param string $bundle
755 * The name of the bundle.
759 function hook_entity_bundle_create($entity_type_id, $bundle) {
760 // When a new bundle is created, the menu needs to be rebuilt to add the
761 // Field UI menu item tabs.
762 \Drupal::service('router.builder')->setRebuildNeeded();
766 * Act on entity_bundle_delete().
768 * This hook is invoked after the operation has been performed.
770 * @param string $entity_type_id
771 * The type of entity; for example, 'node' or 'user'.
772 * @param string $bundle
773 * The bundle that was just deleted.
775 * @ingroup entity_crud
777 function hook_entity_bundle_delete($entity_type_id, $bundle) {
778 // Remove the settings associated with the bundle in my_module.settings.
779 $config = \Drupal::config('my_module.settings');
780 $bundle_settings = $config->get('bundle_settings');
781 if (isset($bundle_settings[$entity_type_id][$bundle])) {
782 unset($bundle_settings[$entity_type_id][$bundle]);
783 $config->set('bundle_settings', $bundle_settings);
788 * Acts when creating a new entity.
790 * This hook runs after a new entity object has just been instantiated.
792 * @param \Drupal\Core\Entity\EntityInterface $entity
795 * @ingroup entity_crud
796 * @see hook_ENTITY_TYPE_create()
798 function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
799 \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
803 * Acts when creating a new entity of a specific type.
805 * This hook runs after a new entity object has just been instantiated.
807 * @param \Drupal\Core\Entity\EntityInterface $entity
810 * @ingroup entity_crud
811 * @see hook_entity_create()
813 function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
814 \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
818 * Act on entities when loaded.
820 * This is a generic load hook called for all entity types loaded via the
823 * hook_entity_storage_load() should be used to load additional data for
826 * @param \Drupal\Core\Entity\EntityInterface[] $entities
827 * The entities keyed by entity ID.
828 * @param string $entity_type_id
829 * The type of entities being loaded (i.e. node, user, comment).
831 * @ingroup entity_crud
832 * @see hook_ENTITY_TYPE_load()
834 function hook_entity_load(array $entities, $entity_type_id) {
835 foreach ($entities as $entity) {
836 $entity->foo = mymodule_add_something($entity);
841 * Act on entities of a specific type when loaded.
843 * @param array $entities
844 * The entities keyed by entity ID.
846 * @ingroup entity_crud
847 * @see hook_entity_load()
849 function hook_ENTITY_TYPE_load($entities) {
850 foreach ($entities as $entity) {
851 $entity->foo = mymodule_add_something($entity);
856 * Act on content entities when loaded from the storage.
858 * The results of this hook will be cached.
860 * @param \Drupal\Core\Entity\EntityInterface[] $entities
861 * The entities keyed by entity ID.
862 * @param string $entity_type
863 * The type of entities being loaded (i.e. node, user, comment).
865 * @see hook_entity_load()
867 function hook_entity_storage_load(array $entities, $entity_type) {
868 foreach ($entities as $entity) {
869 $entity->foo = mymodule_add_something_uncached($entity);
874 * Act on content entities of a given type when loaded from the storage.
876 * The results of this hook will be cached if the entity type supports it.
878 * @param \Drupal\Core\Entity\EntityInterface[] $entities
879 * The entities keyed by entity ID.
881 * @see hook_entity_storage_load()
883 function hook_ENTITY_TYPE_storage_load(array $entities) {
884 foreach ($entities as $entity) {
885 $entity->foo = mymodule_add_something_uncached($entity);
890 * Act on an entity before it is created or updated.
892 * You can get the original entity object from $entity->original when it is an
893 * update of the entity.
895 * @param \Drupal\Core\Entity\EntityInterface $entity
898 * @ingroup entity_crud
899 * @see hook_ENTITY_TYPE_presave()
901 function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
902 if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
903 $route_match = \Drupal::routeMatch();
904 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
909 * Act on a specific type of entity before it is created or updated.
911 * You can get the original entity object from $entity->original when it is an
912 * update of the entity.
914 * @param \Drupal\Core\Entity\EntityInterface $entity
917 * @ingroup entity_crud
918 * @see hook_entity_presave()
920 function hook_ENTITY_TYPE_presave(Drupal\Core\Entity\EntityInterface $entity) {
921 if ($entity->isTranslatable()) {
922 $route_match = \Drupal::routeMatch();
923 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
928 * Respond to creation of a new entity.
930 * This hook runs once the entity has been stored. Note that hook
931 * implementations may not alter the stored entity data.
933 * @param \Drupal\Core\Entity\EntityInterface $entity
936 * @ingroup entity_crud
937 * @see hook_ENTITY_TYPE_insert()
939 function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
940 // Insert the new entity into a fictional table of all entities.
941 db_insert('example_entity')
943 'type' => $entity->getEntityTypeId(),
944 'id' => $entity->id(),
945 'created' => REQUEST_TIME,
946 'updated' => REQUEST_TIME,
952 * Respond to creation of a new entity of a particular type.
954 * This hook runs once the entity has been stored. Note that hook
955 * implementations may not alter the stored entity data.
957 * @param \Drupal\Core\Entity\EntityInterface $entity
960 * @ingroup entity_crud
961 * @see hook_entity_insert()
963 function hook_ENTITY_TYPE_insert(Drupal\Core\Entity\EntityInterface $entity) {
964 // Insert the new entity into a fictional table of this type of entity.
965 db_insert('example_entity')
967 'id' => $entity->id(),
968 'created' => REQUEST_TIME,
969 'updated' => REQUEST_TIME,
975 * Respond to updates to an entity.
977 * This hook runs once the entity storage has been updated. Note that hook
978 * implementations may not alter the stored entity data. Get the original entity
979 * object from $entity->original.
981 * @param \Drupal\Core\Entity\EntityInterface $entity
984 * @ingroup entity_crud
985 * @see hook_ENTITY_TYPE_update()
987 function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
988 // Update the entity's entry in a fictional table of all entities.
989 db_update('example_entity')
991 'updated' => REQUEST_TIME,
993 ->condition('type', $entity->getEntityTypeId())
994 ->condition('id', $entity->id())
999 * Respond to updates to an entity of a particular type.
1001 * This hook runs once the entity storage has been updated. Note that hook
1002 * implementations may not alter the stored entity data. Get the original entity
1003 * object from $entity->original.
1005 * @param \Drupal\Core\Entity\EntityInterface $entity
1006 * The entity object.
1008 * @ingroup entity_crud
1009 * @see hook_entity_update()
1011 function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
1012 // Update the entity's entry in a fictional table of this type of entity.
1013 db_update('example_entity')
1015 'updated' => REQUEST_TIME,
1017 ->condition('id', $entity->id())
1022 * Acts when creating a new entity translation.
1024 * This hook runs after a new entity translation object has just been
1027 * @param \Drupal\Core\Entity\EntityInterface $translation
1028 * The entity object.
1030 * @ingroup entity_crud
1031 * @see hook_ENTITY_TYPE_translation_create()
1033 function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
1034 \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
1038 * Acts when creating a new entity translation of a specific type.
1040 * This hook runs after a new entity translation object has just been
1043 * @param \Drupal\Core\Entity\EntityInterface $translation
1044 * The entity object.
1046 * @ingroup entity_crud
1047 * @see hook_entity_translation_create()
1049 function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
1050 \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
1054 * Respond to creation of a new entity translation.
1056 * This hook runs once the entity translation has been stored. Note that hook
1057 * implementations may not alter the stored entity translation data.
1059 * @param \Drupal\Core\Entity\EntityInterface $translation
1060 * The entity object of the translation just stored.
1062 * @ingroup entity_crud
1063 * @see hook_ENTITY_TYPE_translation_insert()
1065 function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
1067 '@language' => $translation->language()->getName(),
1068 '@label' => $translation->getUntranslated()->label(),
1070 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
1074 * Respond to creation of a new entity translation of a particular type.
1076 * This hook runs once the entity translation has been stored. Note that hook
1077 * implementations may not alter the stored entity translation data.
1079 * @param \Drupal\Core\Entity\EntityInterface $translation
1080 * The entity object of the translation just stored.
1082 * @ingroup entity_crud
1083 * @see hook_entity_translation_insert()
1085 function hook_ENTITY_TYPE_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
1087 '@language' => $translation->language()->getName(),
1088 '@label' => $translation->getUntranslated()->label(),
1090 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
1094 * Respond to entity translation deletion.
1096 * This hook runs once the entity translation has been deleted from storage.
1098 * @param \Drupal\Core\Entity\EntityInterface $translation
1099 * The original entity object.
1101 * @ingroup entity_crud
1102 * @see hook_ENTITY_TYPE_translation_delete()
1104 function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
1106 '@language' => $translation->language()->getName(),
1107 '@label' => $translation->label(),
1109 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
1113 * Respond to entity translation deletion of a particular type.
1115 * This hook runs once the entity translation has been deleted from storage.
1117 * @param \Drupal\Core\Entity\EntityInterface $translation
1118 * The original entity object.
1120 * @ingroup entity_crud
1121 * @see hook_entity_translation_delete()
1123 function hook_ENTITY_TYPE_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
1125 '@language' => $translation->language()->getName(),
1126 '@label' => $translation->label(),
1128 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
1132 * Act before entity deletion.
1134 * @param \Drupal\Core\Entity\EntityInterface $entity
1135 * The entity object for the entity that is about to be deleted.
1137 * @ingroup entity_crud
1138 * @see hook_ENTITY_TYPE_predelete()
1140 function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
1141 // Count references to this entity in a custom table before they are removed
1142 // upon entity deletion.
1143 $id = $entity->id();
1144 $type = $entity->getEntityTypeId();
1145 $count = db_select('example_entity_data')
1146 ->condition('type', $type)
1147 ->condition('id', $id)
1152 // Log the count in a table that records this statistic for deleted entities.
1153 db_merge('example_deleted_entity_statistics')
1154 ->key(['type' => $type, 'id' => $id])
1155 ->fields(['count' => $count])
1160 * Act before entity deletion of a particular entity type.
1162 * @param \Drupal\Core\Entity\EntityInterface $entity
1163 * The entity object for the entity that is about to be deleted.
1165 * @ingroup entity_crud
1166 * @see hook_entity_predelete()
1168 function hook_ENTITY_TYPE_predelete(Drupal\Core\Entity\EntityInterface $entity) {
1169 // Count references to this entity in a custom table before they are removed
1170 // upon entity deletion.
1171 $id = $entity->id();
1172 $type = $entity->getEntityTypeId();
1173 $count = db_select('example_entity_data')
1174 ->condition('type', $type)
1175 ->condition('id', $id)
1180 // Log the count in a table that records this statistic for deleted entities.
1181 db_merge('example_deleted_entity_statistics')
1182 ->key(['type' => $type, 'id' => $id])
1183 ->fields(['count' => $count])
1188 * Respond to entity deletion.
1190 * This hook runs once the entity has been deleted from the storage.
1192 * @param \Drupal\Core\Entity\EntityInterface $entity
1193 * The entity object for the entity that has been deleted.
1195 * @ingroup entity_crud
1196 * @see hook_ENTITY_TYPE_delete()
1198 function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
1199 // Delete the entity's entry from a fictional table of all entities.
1200 db_delete('example_entity')
1201 ->condition('type', $entity->getEntityTypeId())
1202 ->condition('id', $entity->id())
1207 * Respond to entity deletion of a particular type.
1209 * This hook runs once the entity has been deleted from the storage.
1211 * @param \Drupal\Core\Entity\EntityInterface $entity
1212 * The entity object for the entity that has been deleted.
1214 * @ingroup entity_crud
1215 * @see hook_entity_delete()
1217 function hook_ENTITY_TYPE_delete(Drupal\Core\Entity\EntityInterface $entity) {
1218 // Delete the entity's entry from a fictional table of all entities.
1219 db_delete('example_entity')
1220 ->condition('type', $entity->getEntityTypeId())
1221 ->condition('id', $entity->id())
1226 * Respond to entity revision deletion.
1228 * This hook runs once the entity revision has been deleted from the storage.
1230 * @param \Drupal\Core\Entity\EntityInterface $entity
1231 * The entity object for the entity revision that has been deleted.
1233 * @ingroup entity_crud
1234 * @see hook_ENTITY_TYPE_revision_delete()
1236 function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
1237 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
1238 foreach ($referenced_files_by_field as $field => $uuids) {
1239 _editor_delete_file_usage($uuids, $entity, 1);
1244 * Respond to entity revision deletion of a particular type.
1246 * This hook runs once the entity revision has been deleted from the storage.
1248 * @param \Drupal\Core\Entity\EntityInterface $entity
1249 * The entity object for the entity revision that has been deleted.
1251 * @ingroup entity_crud
1252 * @see hook_entity_revision_delete()
1254 function hook_ENTITY_TYPE_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
1255 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
1256 foreach ($referenced_files_by_field as $field => $uuids) {
1257 _editor_delete_file_usage($uuids, $entity, 1);
1262 * Act on entities being assembled before rendering.
1265 * A renderable array representing the entity content. The module may add
1266 * elements to $build prior to rendering. The structure of $build is a
1267 * renderable array as expected by drupal_render().
1268 * @param \Drupal\Core\Entity\EntityInterface $entity
1269 * The entity object.
1270 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1271 * The entity view display holding the display options configured for the
1272 * entity components.
1274 * The view mode the entity is rendered in.
1276 * @see hook_entity_view_alter()
1277 * @see hook_ENTITY_TYPE_view()
1279 * @ingroup entity_crud
1281 function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
1282 // Only do the extra work if the component is configured to be displayed.
1283 // This assumes a 'mymodule_addition' extra field has been defined for the
1284 // entity bundle in hook_entity_extra_field_info().
1285 if ($display->getComponent('mymodule_addition')) {
1286 $build['mymodule_addition'] = [
1287 '#markup' => mymodule_addition($entity),
1288 '#theme' => 'mymodule_my_additional_field',
1294 * Act on entities of a particular type being assembled before rendering.
1297 * A renderable array representing the entity content. The module may add
1298 * elements to $build prior to rendering. The structure of $build is a
1299 * renderable array as expected by drupal_render().
1300 * @param \Drupal\Core\Entity\EntityInterface $entity
1301 * The entity object.
1302 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1303 * The entity view display holding the display options configured for the
1304 * entity components.
1306 * The view mode the entity is rendered in.
1308 * @see hook_ENTITY_TYPE_view_alter()
1309 * @see hook_entity_view()
1311 * @ingroup entity_crud
1313 function hook_ENTITY_TYPE_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
1314 // Only do the extra work if the component is configured to be displayed.
1315 // This assumes a 'mymodule_addition' extra field has been defined for the
1316 // entity bundle in hook_entity_extra_field_info().
1317 if ($display->getComponent('mymodule_addition')) {
1318 $build['mymodule_addition'] = [
1319 '#markup' => mymodule_addition($entity),
1320 '#theme' => 'mymodule_my_additional_field',
1326 * Alter the results of the entity build array.
1328 * This hook is called after the content has been assembled in a structured
1329 * array and may be used for doing processing which requires that the complete
1330 * entity content structure has been built.
1332 * If a module wishes to act on the rendered HTML of the entity rather than the
1333 * structured content array, it may use this hook to add a #post_render
1334 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
1335 * the particular entity type template, if there is one (e.g., node.html.twig).
1337 * See the @link themeable Default theme implementations topic @endlink and
1338 * drupal_render() for details.
1340 * @param array &$build
1341 * A renderable array representing the entity content.
1342 * @param \Drupal\Core\Entity\EntityInterface $entity
1343 * The entity object being rendered.
1344 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1345 * The entity view display holding the display options configured for the
1346 * entity components.
1348 * @ingroup entity_crud
1350 * @see hook_entity_view()
1351 * @see hook_ENTITY_TYPE_view_alter()
1353 function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
1354 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
1355 // Change its weight.
1356 $build['an_additional_field']['#weight'] = -10;
1358 // Add a #post_render callback to act on the rendered HTML of the entity.
1359 $build['#post_render'][] = 'my_module_node_post_render';
1364 * Alter the results of the entity build array for a particular entity type.
1366 * This hook is called after the content has been assembled in a structured
1367 * array and may be used for doing processing which requires that the complete
1368 * entity content structure has been built.
1370 * If a module wishes to act on the rendered HTML of the entity rather than the
1371 * structured content array, it may use this hook to add a #post_render
1372 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
1373 * the particular entity type template, if there is one (e.g., node.html.twig).
1375 * See the @link themeable Default theme implementations topic @endlink and
1376 * drupal_render() for details.
1378 * @param array &$build
1379 * A renderable array representing the entity content.
1380 * @param \Drupal\Core\Entity\EntityInterface $entity
1381 * The entity object being rendered.
1382 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1383 * The entity view display holding the display options configured for the
1384 * entity components.
1386 * @ingroup entity_crud
1388 * @see hook_ENTITY_TYPE_view()
1389 * @see hook_entity_view_alter()
1391 function hook_ENTITY_TYPE_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
1392 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
1393 // Change its weight.
1394 $build['an_additional_field']['#weight'] = -10;
1396 // Add a #post_render callback to act on the rendered HTML of the entity.
1397 $build['#post_render'][] = 'my_module_node_post_render';
1402 * Act on entities as they are being prepared for view.
1404 * Allows you to operate on multiple entities as they are being prepared for
1405 * view. Only use this if attaching the data during the entity loading phase
1406 * is not appropriate, for example when attaching other 'entity' style objects.
1408 * @param string $entity_type_id
1409 * The type of entities being viewed (i.e. node, user, comment).
1410 * @param array $entities
1411 * The entities keyed by entity ID.
1412 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
1413 * The array of entity view displays holding the display options configured
1414 * for the entity components, keyed by bundle name.
1415 * @param string $view_mode
1418 * @ingroup entity_crud
1420 function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
1421 // Load a specific node into the user object for later theming.
1422 if (!empty($entities) && $entity_type_id == 'user') {
1423 // Only do the extra work if the component is configured to be
1424 // displayed. This assumes a 'mymodule_addition' extra field has been
1425 // defined for the entity bundle in hook_entity_extra_field_info().
1427 foreach ($entities as $id => $entity) {
1428 if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) {
1433 $nodes = mymodule_get_user_nodes($ids);
1434 foreach ($ids as $id) {
1435 $entities[$id]->user_node = $nodes[$id];
1442 * Change the view mode of an entity that is being displayed.
1444 * @param string $view_mode
1445 * The view_mode that is to be used to display the entity.
1446 * @param \Drupal\Core\Entity\EntityInterface $entity
1447 * The entity that is being viewed.
1448 * @param array $context
1449 * Array with additional context information, currently only contains the
1450 * langcode the entity is viewed in.
1452 * @ingroup entity_crud
1454 function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
1455 // For nodes, change the view mode when it is teaser.
1456 if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
1457 $view_mode = 'my_custom_view_mode';
1462 * Alter entity renderable values before cache checking in drupal_render().
1464 * Invoked for a specific entity type.
1466 * The values in the #cache key of the renderable array are used to determine if
1467 * a cache entry exists for the entity's rendered output. Ideally only values
1468 * that pertain to caching should be altered in this hook.
1470 * @param array &$build
1471 * A renderable array containing the entity's caching and view mode values.
1472 * @param \Drupal\Core\Entity\EntityInterface $entity
1473 * The entity that is being viewed.
1474 * @param string $view_mode
1475 * The view_mode that is to be used to display the entity.
1477 * @see drupal_render()
1478 * @see \Drupal\Core\Entity\EntityViewBuilder
1479 * @see hook_entity_build_defaults_alter()
1481 * @ingroup entity_crud
1483 function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
1488 * Alter entity renderable values before cache checking in drupal_render().
1490 * The values in the #cache key of the renderable array are used to determine if
1491 * a cache entry exists for the entity's rendered output. Ideally only values
1492 * that pertain to caching should be altered in this hook.
1494 * @param array &$build
1495 * A renderable array containing the entity's caching and view mode values.
1496 * @param \Drupal\Core\Entity\EntityInterface $entity
1497 * The entity that is being viewed.
1498 * @param string $view_mode
1499 * The view_mode that is to be used to display the entity.
1501 * @see drupal_render()
1502 * @see \Drupal\Core\Entity\EntityViewBuilder
1503 * @see hook_ENTITY_TYPE_build_defaults_alter()
1505 * @ingroup entity_crud
1507 function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
1512 * Alter the settings used for displaying an entity.
1514 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1515 * The entity view display that will be used to display the entity
1517 * @param array $context
1518 * An associative array containing:
1519 * - entity_type: The entity type, e.g., 'node' or 'user'.
1520 * - bundle: The bundle, e.g., 'page' or 'article'.
1521 * - view_mode: The view mode, e.g., 'full', 'teaser', etc.
1523 * @ingroup entity_crud
1525 function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
1526 // Leave field labels out of the search index.
1527 if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
1528 foreach ($display->getComponents() as $name => $options) {
1529 if (isset($options['label'])) {
1530 $options['label'] = 'hidden';
1531 $display->setComponent($name, $options);
1538 * Alter the render array generated by an EntityDisplay for an entity.
1540 * @param array $build
1541 * The renderable array generated by the EntityDisplay.
1542 * @param array $context
1543 * An associative array containing:
1544 * - entity: The entity being rendered.
1545 * - view_mode: The view mode; for example, 'full' or 'teaser'.
1546 * - display: The EntityDisplay holding the display options.
1548 * @ingroup entity_crud
1550 function hook_entity_display_build_alter(&$build, $context) {
1551 // Append RDF term mappings on displayed taxonomy links.
1552 foreach (Element::children($build) as $field_name) {
1553 $element = &$build[$field_name];
1554 if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') {
1555 foreach ($element['#items'] as $delta => $item) {
1556 $term = $item->entity;
1557 if (!empty($term->rdf_mapping['rdftype'])) {
1558 $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
1560 if (!empty($term->rdf_mapping['name']['predicates'])) {
1561 $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
1569 * Acts on an entity object about to be shown on an entity form.
1571 * This can be typically used to pre-fill entity values or change the form state
1572 * before the entity form is built. It is invoked just once when first building
1573 * the entity form. Rebuilds will not trigger a new invocation.
1575 * @param \Drupal\Core\Entity\EntityInterface $entity
1576 * The entity that is about to be shown on the form.
1578 * The current operation.
1579 * @param \Drupal\Core\Form\FormStateInterface $form_state
1580 * The current state of the form.
1582 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
1583 * @see hook_ENTITY_TYPE_prepare_form()
1585 * @ingroup entity_crud
1587 function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
1588 if ($operation == 'edit') {
1589 $entity->label->value = 'Altered label';
1590 $form_state->set('label_altered', TRUE);
1595 * Acts on a particular type of entity object about to be in an entity form.
1597 * This can be typically used to pre-fill entity values or change the form state
1598 * before the entity form is built. It is invoked just once when first building
1599 * the entity form. Rebuilds will not trigger a new invocation.
1601 * @param \Drupal\Core\Entity\EntityInterface $entity
1602 * The entity that is about to be shown on the form.
1604 * The current operation.
1605 * @param \Drupal\Core\Form\FormStateInterface $form_state
1606 * The current state of the form.
1608 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
1609 * @see hook_entity_prepare_form()
1611 * @ingroup entity_crud
1613 function hook_ENTITY_TYPE_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
1614 if ($operation == 'edit') {
1615 $entity->label->value = 'Altered label';
1616 $form_state->set('label_altered', TRUE);
1621 * Alter the settings used for displaying an entity form.
1623 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
1624 * The entity_form_display object that will be used to display the entity form
1626 * @param array $context
1627 * An associative array containing:
1628 * - entity_type: The entity type, e.g., 'node' or 'user'.
1629 * - bundle: The bundle, e.g., 'page' or 'article'.
1630 * - form_mode: The form mode; e.g., 'default', 'profile', 'register', etc.
1632 * @ingroup entity_crud
1634 function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
1635 // Hide the 'user_picture' field from the register form.
1636 if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
1637 $form_display->setComponent('user_picture', [
1638 'region' => 'hidden',
1644 * Provides custom base field definitions for a content entity type.
1646 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1647 * The entity type definition.
1649 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
1650 * An array of field definitions, keyed by field name.
1652 * @see hook_entity_base_field_info_alter()
1653 * @see hook_entity_bundle_field_info()
1654 * @see hook_entity_bundle_field_info_alter()
1655 * @see \Drupal\Core\Field\FieldDefinitionInterface
1656 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
1658 function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1659 if ($entity_type->id() == 'node') {
1661 $fields['mymodule_text'] = BaseFieldDefinition::create('string')
1662 ->setLabel(t('The text'))
1663 ->setDescription(t('A text property added by mymodule.'))
1665 ->setClass('\Drupal\mymodule\EntityComputedText');
1672 * Alter base field definitions for a content entity type.
1674 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
1675 * The array of base field definitions for the entity type.
1676 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1677 * The entity type definition.
1679 * @see hook_entity_base_field_info()
1680 * @see hook_entity_bundle_field_info()
1681 * @see hook_entity_bundle_field_info_alter()
1683 * @todo WARNING: This hook will be changed in
1684 * https://www.drupal.org/node/2346329.
1686 function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1687 // Alter the mymodule_text field to use a custom class.
1688 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
1689 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
1694 * Provides field definitions for a specific bundle within an entity type.
1696 * Bundle fields either have to override an existing base field, or need to
1697 * provide a field storage definition via hook_entity_field_storage_info()
1698 * unless they are computed.
1700 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1701 * The entity type definition.
1702 * @param string $bundle
1704 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
1705 * The list of base field definitions for the entity type.
1707 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
1708 * An array of bundle field definitions, keyed by field name.
1710 * @see hook_entity_base_field_info()
1711 * @see hook_entity_base_field_info_alter()
1712 * @see hook_entity_field_storage_info()
1713 * @see hook_entity_field_storage_info_alter()
1714 * @see hook_entity_bundle_field_info_alter()
1715 * @see \Drupal\Core\Field\FieldDefinitionInterface
1716 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
1718 * @todo WARNING: This hook will be changed in
1719 * https://www.drupal.org/node/2346347.
1721 function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
1722 // Add a property only to nodes of the 'article' bundle.
1723 if ($entity_type->id() == 'node' && $bundle == 'article') {
1725 $fields['mymodule_text_more'] = BaseFieldDefinition::create('string')
1726 ->setLabel(t('More text'))
1728 ->setClass('\Drupal\mymodule\EntityComputedMoreText');
1734 * Alter bundle field definitions.
1736 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
1737 * The array of bundle field definitions.
1738 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1739 * The entity type definition.
1740 * @param string $bundle
1743 * @see hook_entity_base_field_info()
1744 * @see hook_entity_base_field_info_alter()
1745 * @see hook_entity_bundle_field_info()
1747 * @todo WARNING: This hook will be changed in
1748 * https://www.drupal.org/node/2346347.
1750 function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
1751 if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
1752 // Alter the mymodule_text field to use a custom class.
1753 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
1758 * Provides field storage definitions for a content entity type.
1760 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1761 * The entity type definition.
1763 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
1764 * An array of field storage definitions, keyed by field name.
1766 * @see hook_entity_field_storage_info_alter()
1767 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface
1768 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldStorageDefinitions()
1770 function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1771 if (\Drupal::entityManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) {
1772 // Query by filtering on the ID as this is more efficient than filtering
1773 // on the entity_type property directly.
1774 $ids = \Drupal::entityQuery('field_storage_config')
1775 ->condition('id', $entity_type->id() . '.', 'STARTS_WITH')
1777 // Fetch all fields and key them by field name.
1778 $field_storages = FieldStorageConfig::loadMultiple($ids);
1780 foreach ($field_storages as $field_storage) {
1781 $result[$field_storage->getName()] = $field_storage;
1789 * Alter field storage definitions for a content entity type.
1791 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields
1792 * The array of field storage definitions for the entity type.
1793 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1794 * The entity type definition.
1796 * @see hook_entity_field_storage_info()
1798 function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1799 // Alter the max_length setting.
1800 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
1801 $fields['mymodule_text']->setSetting('max_length', 128);
1806 * Declares entity operations.
1808 * @param \Drupal\Core\Entity\EntityInterface $entity
1809 * The entity on which the linked operations will be performed.
1812 * An operations array as returned by
1813 * EntityListBuilderInterface::getOperations().
1815 * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
1817 function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
1819 $operations['translate'] = [
1820 'title' => t('Translate'),
1821 'url' => \Drupal\Core\Url::fromRoute('foo_module.entity.translate'),
1829 * Alter entity operations.
1831 * @param array $operations
1832 * Operations array as returned by
1833 * \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
1834 * @param \Drupal\Core\Entity\EntityInterface $entity
1835 * The entity on which the linked operations will be performed.
1837 function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) {
1838 // Alter the title and weight.
1839 $operations['translate']['title'] = t('Translate @entity_type', [
1840 '@entity_type' => $entity->getEntityTypeId(),
1842 $operations['translate']['weight'] = 99;
1846 * Control access to fields.
1848 * This hook is invoked from
1849 * \Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess() to let modules
1850 * grant or deny operations on fields.
1852 * @param string $operation
1853 * The operation to be performed. See
1854 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
1855 * for possible values.
1856 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
1857 * The field definition.
1858 * @param \Drupal\Core\Session\AccountInterface $account
1859 * The user account to check.
1860 * @param \Drupal\Core\Field\FieldItemListInterface $items
1861 * (optional) The entity field object on which the operation is to be
1864 * @return \Drupal\Core\Access\AccessResultInterface
1865 * The access result.
1867 * @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
1869 function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
1870 if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
1871 return AccessResult::allowedIfHasPermission($account, 'update field of interest');
1873 return AccessResult::neutral();
1877 * Alter the default access behavior for a given field.
1879 * Use this hook to override access grants from another module. Note that the
1880 * original default access flag is masked under the ':default' key.
1882 * @param \Drupal\Core\Access\AccessResultInterface[] $grants
1883 * An array of grants gathered by hook_entity_field_access(). The array is
1884 * keyed by the module that defines the field's access control; the values are
1885 * grant responses for each module (\Drupal\Core\Access\AccessResult).
1886 * @param array $context
1887 * Context array on the performed operation with the following keys:
1888 * - operation: The operation to be performed (string).
1889 * - field_definition: The field definition object
1890 * (\Drupal\Core\Field\FieldDefinitionInterface)
1891 * - account: The user account to check access for
1892 * (Drupal\user\Entity\User).
1893 * - items: (optional) The entity field items
1894 * (\Drupal\Core\Field\FieldItemListInterface).
1896 function hook_entity_field_access_alter(array &$grants, array $context) {
1897 /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
1898 $field_definition = $context['field_definition'];
1899 if ($field_definition->getName() == 'field_of_interest' && $grants['node']->isForbidden()) {
1900 // Override node module's restriction to no opinion (neither allowed nor
1901 // forbidden). We don't want to provide our own access hook, we only want to
1902 // take out node module's part in the access handling of this field. We also
1903 // don't want to switch node module's grant to
1904 // AccessResultInterface::isAllowed() , because the grants of other modules
1905 // should still decide on their own if this field is accessible or not
1906 $grants['node'] = AccessResult::neutral()->inheritCacheability($grants['node']);
1911 * Acts when initializing a fieldable entity object.
1913 * This hook runs after a new entity object or a new entity translation object
1914 * has just been instantiated. It can be used to set initial values, e.g. to
1917 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
1918 * The entity object.
1920 * @ingroup entity_crud
1921 * @see hook_ENTITY_TYPE_field_values_init()
1923 function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
1924 if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
1925 $entity->foo->value = 'some_initial_value';
1930 * Acts when initializing a fieldable entity object.
1932 * This hook runs after a new entity object or a new entity translation object
1933 * has just been instantiated. It can be used to set initial values, e.g. to
1936 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
1937 * The entity object.
1939 * @ingroup entity_crud
1940 * @see hook_entity_field_values_init()
1942 function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
1943 if (!$entity->foo->value) {
1944 $entity->foo->value = 'some_initial_value';
1949 * Exposes "pseudo-field" components on content entities.
1951 * Field UI's "Manage fields" and "Manage display" pages let users re-order
1952 * fields, but also non-field components. For nodes, these include elements
1953 * exposed by modules through hook_form_alter(), for instance.
1955 * Content entities or modules that want to have their components supported
1956 * should expose them using this hook. The user-defined settings (weight,
1957 * visible) are automatically applied when entities or entity forms are
1960 * @see hook_entity_extra_field_info_alter()
1963 * The array structure is identical to that of the return value of
1964 * \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields().
1966 function hook_entity_extra_field_info() {
1968 $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
1969 $description = t('Node module element');
1971 foreach (NodeType::loadMultiple() as $bundle) {
1973 // Add also the 'language' select if Language module is enabled and the
1974 // bundle has multilingual support.
1975 // Visibility of the ordering of the language selector is the same as on the
1977 if ($module_language_enabled) {
1978 $configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
1979 if ($configuration->isLanguageAlterable()) {
1980 $extra['node'][$bundle->id()]['form']['language'] = [
1981 'label' => t('Language'),
1982 'description' => $description,
1987 $extra['node'][$bundle->id()]['display']['language'] = [
1988 'label' => t('Language'),
1989 'description' => $description,
1999 * Alter "pseudo-field" components on content entities.
2001 * @param array $info
2002 * The array structure is identical to that of the return value of
2003 * \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
2005 * @see hook_entity_extra_field_info()
2007 function hook_entity_extra_field_info_alter(&$info) {
2008 // Force node title to always be at the top of the list by default.
2009 foreach (NodeType::loadMultiple() as $bundle) {
2010 if (isset($info['node'][$bundle->id()]['form']['title'])) {
2011 $info['node'][$bundle->id()]['form']['title']['weight'] = -20;
2017 * @} End of "addtogroup hooks".