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 entities_revisions_translations Entities, revisions and translations
72 * A content entity can have multiple stored variants: based on its definition,
73 * it can be revisionable, translatable, or both.
75 * A revisionable entity can keep track of the changes that affect its data. In
76 * fact all previous revisions of the entity can be stored and made available as
77 * "historical" information. The "default" revision is the canonical variant of
78 * the entity, the one that is loaded when no specific revision is requested.
79 * Only changes to the default revision may be performed without triggering the
80 * creation of a new revision, in any other case revision data is not supposed
81 * to change. Aside from historical revisions, there can be "pending" revisions,
82 * that contain changes that did not make their way into the default revision.
83 * Typically these revisions contain data that is waiting for some form of
84 * approval, before being accepted as canonical.
85 * @see \Drupal\Core\Entity\RevisionableInterface
86 * @see \Drupal\Core\Entity\RevisionableStorageInterface
88 * A translatable entity can contain multiple translations of the same content.
89 * Content entity data is stored via fields, and each field can have one version
90 * for each enabled language. Some fields may be defined as untranslatable,
91 * which means that their values are shared among all translations. The
92 * "default" translation is the canonical variant of the entity, the one whose
93 * content will be accessible in the entity field data. Other translations
94 * can be instantiated from the default one. Every translation has an "active
95 * language" that is used to determine which field translation values should be
96 * handled. Typically the default translation's active language is the language
97 * of the content that was originally entered and served as source for the other
99 * @see \Drupal\Core\Entity\TranslatableInterface
100 * @see \Drupal\Core\Entity\TranslatableStorageInterface
102 * An entity that is both revisionable and translatable has all the features
103 * described above: every revision can contain one or more translations. The
104 * canonical variant of the entity is the default translation of the default
105 * revision. Any revision will be initially loaded as the default translation,
106 * the other revision translations can be instantiated from this one. If a
107 * translation has changes in a certain revision, the translation is considered
108 * "affected" by that revision, and will be flagged as such via the
109 * "revision_translation_affected" field. With the built-in UI, every time a new
110 * revision is saved, the changes for the edited translations will be stored,
111 * while all field values for the other translations will be copied as-is.
112 * However, if multiple translations of the default revision are being
113 * subsequently modified without creating a new revision when saving, they will
114 * all be affected by the default revision. Additionally, all revision
115 * translations will be affected when saving a revision containing changes for
116 * untranslatable fields. On the other hand, pending revisions are not supposed
117 * to contain multiple affected translations, even when they are being
118 * manipulated via the API.
119 * @see \Drupal\Core\Entity\TranslatableRevisionableInterface
120 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface
122 * @section create Create operations
123 * To create an entity:
125 * $entity = $storage->create();
127 * // Add code here to set properties on the entity.
129 * // Until you call save(), the entity is just in memory.
132 * There is also a shortcut method on entity classes, which creates an entity
133 * with an array of provided property values: \Drupal\Core\Entity::create().
135 * Hooks invoked during the create operation:
136 * - hook_ENTITY_TYPE_create()
137 * - hook_entity_create()
138 * - When handling content entities, if a new translation is added to the entity
140 * - hook_ENTITY_TYPE_translation_create()
141 * - hook_entity_translation_create()
143 * See @ref save below for the save portion of the operation.
145 * @section load Read/Load operations
146 * To load (read) a single entity:
148 * $entity = $storage->load($id);
150 * To load multiple entities:
152 * $entities = $storage->loadMultiple($ids);
154 * Since load() calls loadMultiple(), these are really the same operation.
155 * Here is the order of hooks and other operations that take place during
157 * - Entity is loaded from storage.
158 * - postLoad() is called on the entity class, passing in all of the loaded
160 * - hook_entity_load()
161 * - hook_ENTITY_TYPE_load()
163 * When an entity is loaded, normally the default entity revision is loaded.
164 * It is also possible to load a different revision, for entities that support
165 * revisions, with this code:
167 * $entity = $storage->loadRevision($revision_id);
169 * This involves the same hooks and operations as regular entity loading.
171 * The "latest revision" of an entity is the most recently created one,
172 * regardless of it being default or pending. If the entity is translatable,
173 * revision translations are not taken into account either. In other words, any
174 * time a new revision is created, that becomes the latest revision for the
175 * entity overall, regardless of the affected translations. To load the latest
176 * revision of an entity:
178 * $revision_id = $storage->getLatestRevisionId($entity_id);
179 * $entity = $storage->loadRevision($revision_id);
181 * As usual, if the entity is translatable, this code instantiates into $entity
182 * the default translation of the revision, even if the latest revision contains
183 * only changes to a different translation:
185 * $is_default = $entity->isDefaultTranslation(); // returns TRUE
188 * The "latest translation-affected revision" is the most recently created one
189 * that affects the specified translation. For example, when a new revision
190 * introducing some changes to an English translation is saved, that becomes the
191 * new "latest revision". However, if an existing Italian translation was not
192 * affected by those changes, then the "latest translation-affected revision"
193 * for Italian remains what it was. To load the Italian translation at its
194 * latest translation-affected revision:
196 * $revision_id = $storage->getLatestTranslationAffectedRevisionId($entity_id, 'it');
197 * $it_translation = $storage->loadRevision($revision_id)->getTranslation('it');
200 * @section save Save operations
201 * To update an existing entity, you will need to load it, change properties,
202 * and then save; as described above, when creating a new entity, you will also
203 * need to save it. Here is the order of hooks and other events that happen
204 * during an entity save:
205 * - preSave() is called on the entity object, and field objects.
206 * - hook_ENTITY_TYPE_presave()
207 * - hook_entity_presave()
208 * - Entity is saved to storage.
209 * - For updates on content entities, if there is a translation added that
210 * was not previously present:
211 * - hook_ENTITY_TYPE_translation_insert()
212 * - hook_entity_translation_insert()
213 * - For updates on content entities, if there was a translation removed:
214 * - hook_ENTITY_TYPE_translation_delete()
215 * - hook_entity_translation_delete()
216 * - postSave() is called on the entity object.
217 * - hook_ENTITY_TYPE_insert() (new) or hook_ENTITY_TYPE_update() (update)
218 * - hook_entity_insert() (new) or hook_entity_update() (update)
220 * Some specific entity types invoke hooks during preSave() or postSave()
221 * operations. Examples:
222 * - Field configuration preSave(): hook_field_storage_config_update_forbid()
223 * - Node postSave(): hook_node_access_records() and
224 * hook_node_access_records_alter()
225 * - Config entities that are acting as entity bundles in postSave():
226 * hook_entity_bundle_create()
227 * - Comment: hook_comment_publish() and hook_comment_unpublish() as
230 * Note that all translations available for the entity are stored during a save
231 * operation. When saving a new revision, a copy of every translation is stored,
232 * regardless of it being affected by the revision.
234 * @section edit Editing operations
235 * When an entity's add/edit form is used to add or edit an entity, there
236 * are several hooks that are invoked:
237 * - hook_entity_prepare_form()
238 * - hook_ENTITY_TYPE_prepare_form()
239 * - hook_entity_form_display_alter() (for content entities only)
241 * @section delete Delete operations
242 * To delete one or more entities, load them and then delete them:
244 * $entities = $storage->loadMultiple($ids);
245 * $storage->delete($entities);
248 * During the delete operation, the following hooks and other events happen:
249 * - preDelete() is called on the entity class.
250 * - hook_ENTITY_TYPE_predelete()
251 * - hook_entity_predelete()
252 * - Entity and field information is removed from storage.
253 * - postDelete() is called on the entity class.
254 * - hook_ENTITY_TYPE_delete()
255 * - hook_entity_delete()
257 * Some specific entity types invoke hooks during the delete process. Examples:
258 * - Entity bundle postDelete(): hook_entity_bundle_delete()
260 * Individual revisions of an entity can also be deleted:
262 * $storage->deleteRevision($revision_id);
264 * This operation invokes the following operations and hooks:
265 * - Revision is loaded (see @ref load above).
266 * - Revision and field information is removed from the database.
267 * - hook_ENTITY_TYPE_revision_delete()
268 * - hook_entity_revision_delete()
270 * @section view View/render operations
271 * To make a render array for a loaded entity:
273 * // You can omit the language ID if the default language is being used.
274 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
276 * You can also use the viewMultiple() method to view multiple entities.
278 * Hooks invoked during the operation of building a render array:
279 * - hook_entity_view_mode_alter()
280 * - hook_ENTITY_TYPE_build_defaults_alter()
281 * - hook_entity_build_defaults_alter()
283 * View builders for some types override these hooks, notably:
284 * - The Tour view builder does not invoke any hooks.
285 * - The Block view builder invokes hook_block_view_alter() and
286 * hook_block_view_BASE_BLOCK_ID_alter(). Note that in other view builders,
287 * the view alter hooks are run later in the process.
289 * During the rendering operation, the default entity viewer runs the following
290 * hooks and operations in the pre-render step:
291 * - hook_entity_view_display_alter()
292 * - hook_entity_prepare_view()
293 * - Entity fields are loaded, and render arrays are built for them using
295 * - hook_entity_display_build_alter()
296 * - hook_ENTITY_TYPE_view()
297 * - hook_entity_view()
298 * - hook_ENTITY_TYPE_view_alter()
299 * - hook_entity_view_alter()
301 * Some specific builders have specific hooks:
302 * - The Node view builder invokes hook_node_links_alter().
303 * - The Comment view builder invokes hook_comment_links_alter().
305 * After this point in rendering, the theme system takes over. See the
306 * @link theme_render Theme system and render API topic @endlink for more
309 * @section misc Other entity hooks
310 * Some types of entities invoke hooks for specific operations:
313 * - Query is executed to find matching nodes
314 * - Resulting node is loaded
315 * - Node render array is built
316 * - comment_node_update_index() is called (this adds "N comments" text)
317 * - hook_node_search_result()
318 * - Search indexing nodes:
320 * - Node render array is built
321 * - hook_node_update_index()
326 * @defgroup entity_api Entity API
328 * Describes how to define and manipulate content and configuration entities.
330 * Entities, in Drupal, are objects that are used for persistent storage of
331 * content and configuration information. See the
332 * @link info_types Information types topic @endlink for an overview of the
333 * different types of information, and the
334 * @link config_api Configuration API topic @endlink for more about the
337 * Each entity is an instance of a particular "entity type". Some content entity
338 * types have sub-types, which are known as "bundles", while for other entity
339 * types, there is only a single bundle. For example, the Node content entity
340 * type, which is used for the main content pages in Drupal, has bundles that
341 * are known as "content types", while the User content type, which is used for
342 * user accounts, has only one bundle.
344 * The sections below have more information about entities and the Entity API;
345 * for more detailed information, see
346 * https://www.drupal.org/developing/api/entity.
348 * @section define Defining an entity type
349 * Entity types are defined by modules, using Drupal's Plugin API (see the
350 * @link plugin_api Plugin API topic @endlink for more information about plugins
351 * in general). Here are the steps to follow to define a new entity type:
352 * - Choose a unique machine name, or ID, for your entity type. This normally
353 * starts with (or is the same as) your module's machine name. It should be
354 * as short as possible, and may not exceed 32 characters.
355 * - Define an interface for your entity's get/set methods, usually extending
356 * either \Drupal\Core\Config\Entity\ConfigEntityInterface or
357 * \Drupal\Core\Entity\ContentEntityInterface.
358 * - Define a class for your entity, implementing your interface and extending
359 * either \Drupal\Core\Config\Entity\ConfigEntityBase or
360 * \Drupal\Core\Entity\ContentEntityBase, with annotation for
361 * \@ConfigEntityType or \@ContentEntityType in its documentation block.
362 * If you are defining a content entity type, it is recommended to extend the
363 * \Drupal\Core\Entity\EditorialContentEntityBase base class in order to get
364 * out-of-the-box support for Entity API's revisioning and publishing
365 * features, which will allow your entity type to be used with Drupal's
366 * editorial workflow provided by the Content Moderation module.
367 * - In the annotation, the 'id' property gives the entity type ID, and the
368 * 'label' property gives the human-readable name of the entity type. If you
369 * are defining a content entity type that uses bundles, the 'bundle_label'
370 * property gives the human-readable name to use for a bundle of this entity
371 * type (for example, "Content type" for the Node entity).
372 * - The annotation will refer to several handler classes, which you will also
374 * - list_builder: Define a class that extends
375 * \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration
376 * entities) or \Drupal\Core\Entity\EntityListBuilder (for content
377 * entities), to provide an administrative overview for your entities.
378 * - add and edit forms, or default form: Define a class (or two) that
379 * extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms
380 * for your entities. For content entities, base class
381 * \Drupal\Core\Entity\ContentEntityForm is a better starting point.
382 * - delete form: Define a class that extends
383 * \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete
384 * confirmation form for your entities.
385 * - view_builder: For content entities and config entities that need to be
386 * viewed, define a class that implements
387 * \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
388 * \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
389 * - translation: For translatable content entities (if the 'translatable'
390 * annotation property has value TRUE), define a class that extends
391 * \Drupal\content_translation\ContentTranslationHandler, to translate
392 * the content. Configuration translation is handled automatically by the
393 * Configuration Translation module, without the need of a handler class.
394 * - access: If your configuration entity has complex permissions, you might
395 * need an access control handling, implementing
396 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most
397 * entities can just use the 'admin_permission' annotation property
398 * instead. Note that if you are creating your own access control handler,
399 * you should override the checkAccess() and checkCreateAccess() methods,
401 * - storage: A class implementing
402 * \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
403 * entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
404 * config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage.
405 * You can extend one of these classes to provide custom behavior.
406 * - views_data: A class implementing \Drupal\views\EntityViewsDataInterface
407 * to provide views data for the entity type. You can autogenerate most of
408 * the views data by extending \Drupal\views\EntityViewsData.
409 * - For content entities, the annotation will refer to a number of database
410 * tables and their fields. These annotation properties, such as 'base_table',
411 * 'data_table', 'entity_keys', etc., are documented on
412 * \Drupal\Core\Entity\EntityType.
413 * - For content entities that are displayed on their own pages, the annotation
414 * will refer to a 'uri_callback' function, which takes an object of the
415 * entity interface you have defined as its parameter, and returns routing
416 * information for the entity page; see node_uri() for an example. You will
417 * also need to add a corresponding route to your module's routing.yml file;
418 * see the entity.node.canonical route in node.routing.yml for an example, and see
419 * @ref sec_routes below for some notes.
420 * - Optionally, instead of defining routes, routes can be auto generated by
421 * providing a route handler. See @ref sec_routes. Otherwise, define routes
422 * and links for the various URLs associated with the entity.
423 * These go into the 'links' annotation, with the link type as the key, and
424 * the path of this link template as the value. The corresponding route
425 * requires the following route name:
426 * "entity.$entity_type_id.$link_template_type". See @ref sec_routes below for
427 * some routing notes. Typical link types are:
428 * - canonical: Default link, either to view (if entities are viewed on their
429 * own pages) or edit the entity.
430 * - delete-form: Confirmation form to delete the entity.
431 * - edit-form: Editing form.
432 * - Other link types specific to your entity type can also be defined.
433 * - If your content entity is fieldable, provide the 'field_ui_base_route'
434 * annotation property, giving the name of the route that the Manage Fields,
435 * Manage Display, and Manage Form Display pages from the Field UI module
436 * will be attached to. This is usually the bundle settings edit page, or an
437 * entity type settings page if there are no bundles.
438 * - If your content entity has bundles, you will also need to define a second
439 * plugin to handle the bundles. This plugin is itself a configuration entity
440 * type, so follow the steps here to define it. The machine name ('id'
441 * annotation property) of this configuration entity class goes into the
442 * 'bundle_entity_type' annotation property on the entity type class. For
443 * example, for the Node entity, the bundle class is
444 * \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is
445 * the annotation property 'bundle_entity_type' on the
446 * \Drupal\node\Entity\Node class. Also, the
447 * bundle config entity type annotation must have a 'bundle_of' property,
448 * giving the machine name of the entity type it is acting as a bundle for.
449 * These machine names are considered permanent, they may not be renamed.
450 * - Additional annotation properties can be seen on entity class examples such
451 * as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
452 * (configuration). These annotation properties are documented on
453 * \Drupal\Core\Entity\EntityType.
455 * @section sec_routes Entity routes
456 * Entity routes can be defined in *.routing.yml files, like any other route:
457 * see the @link routing Routing API @endlink topic for more information.
458 * Another option for entity routes is to use a route provider class, and
459 * reference it in the annotations on the entity class: see the end of this
460 * section for an example.
462 * It's possible to use both a YAML file and a provider class for entity
463 * routes, at the same time. Avoid duplicating route names between the two:
464 * if a duplicate route name is found in both locations, the one in the YAML
465 * file takes precedence; regardless, such duplication can be confusing.
467 * Here's an example YAML route specification, for the block configure form:
469 * entity.block.edit_form:
470 * path: '/admin/structure/block/manage/{block}'
472 * _entity_form: 'block.default'
473 * _title: 'Configure block'
475 * _entity_access: 'block.update'
477 * Some notes on this example:
478 * - path: The {block} in the path is a placeholder, which (for an entity) must
479 * always take the form of {machine_name_of_entity_type}. In the URL, the
480 * placeholder value will be the ID of an entity item. When the route is used,
481 * the entity system will load the corresponding entity item and pass it in as
482 * an object to the controller for the route.
483 * - defaults: For entity form routes, use _entity_form rather than the generic
484 * _controller or _form. The value is composed of the entity type machine name
485 * and a form handler type from the entity annotation (see @ref define above
486 * more more on handlers and annotation). So, in this example, block.default
487 * refers to the 'default' form handler on the block entity type, whose
488 * annotation contains:
492 * "default" = "Drupal\block\BlockForm",
494 * If instead of YAML you want to use a route provider class:
495 * - \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical,
496 * edit-form, and delete-form routes.
497 * - \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same
498 * routes, set up to use the administrative theme for edit and delete pages.
499 * - You can also create your own class, extending one of these two classes if
500 * you only want to modify their behaviour slightly.
502 * To register any route provider class, add lines like the following to your
503 * entity class annotation:
506 * "route_provider" = {
507 * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
510 * @section bundle Defining a content entity bundle
511 * For entity types that use bundles, such as Node (bundles are content types)
512 * and Taxonomy (bundles are vocabularies), modules and install profiles can
513 * define bundles by supplying default configuration in their config/install
514 * directories. (See the @link config_api Configuration API topic @endlink for
515 * general information about configuration.)
517 * There are several good examples of this in Drupal Core:
518 * - The Forum module defines a content type in node.type.forum.yml and a
519 * vocabulary in taxonomy.vocabulary.forums.yml
520 * - The Book module defines a content type in node.type.book.yml
521 * - The Standard install profile defines Page and Article content types in
522 * node.type.page.yml and node.type.article.yml, a Tags vocabulary in
523 * taxonomy.vocabulary.tags.yml, and a Node comment type in
524 * comment.type.comment.yml. This profile's configuration is especially
525 * instructive, because it also adds several fields to the Article type, and
526 * it sets up view and form display modes for the node types.
528 * @section load_query Loading, querying, and rendering entities
529 * To load entities, use the entity storage manager, which is an object
530 * implementing \Drupal\Core\Entity\EntityStorageInterface that you can
533 * $storage = \Drupal::entityManager()->getStorage('your_entity_type');
534 * // Or if you have a $container variable:
535 * $storage = $container->get('entity.manager')->getStorage('your_entity_type');
537 * Here, 'your_entity_type' is the machine name of your entity type ('id'
538 * annotation property on the entity class), and note that you should use
539 * dependency injection to retrieve this object if possible. See the
540 * @link container Services and Dependency Injection topic @endlink for more
541 * about how to properly retrieve services.
543 * To query to find entities to load, use an entity query, which is a object
544 * implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve
548 * $query = \Drupal::entityQuery('your_entity_type');
549 * // Or, if you have a $container variable:
550 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type');
551 * $query = $storage->getQuery();
553 * If you need aggregation, there is an aggregate query available, which
554 * implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
556 * $query \Drupal::entityQueryAggregate('your_entity_type');
558 * $query = $storage->getAggregateQuery('your_entity_type');
561 * In either case, you can then add conditions to your query, using methods
562 * like condition(), exists(), etc. on $query; add sorting, pager, and range
563 * if needed, and execute the query to return a list of entity IDs that match
566 * Here is an example, using the core File entity:
568 * $fids = Drupal::entityQuery('file')
569 * ->condition('status', FILE_STATUS_PERMANENT, '<>')
570 * ->condition('changed', REQUEST_TIME - $age, '<')
573 * $files = $storage->loadMultiple($fids);
576 * The normal way of viewing entities is by using a route, as described in the
577 * sections above. If for some reason you need to render an entity in code in a
578 * particular view mode, you can use an entity view builder, which is an object
579 * implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can
582 * $view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type');
583 * // Or if you have a $container variable:
584 * $view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type');
586 * Then, to build and render the entity:
588 * // You can omit the language ID, by default the current content language will
589 * // be used. If no translation is available for the current language, fallback
590 * // rules will be used.
591 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
592 * // $build is a render array.
593 * $rendered = drupal_render($build);
596 * @section sec_access Access checking on entities
597 * Entity types define their access permission scheme in their annotation.
598 * Access permissions can be quite complex, so you should not assume any
599 * particular permission scheme. Instead, once you have an entity object
600 * loaded, you can check for permission for a particular operation (such as
601 * 'view') at the entity or field level by calling:
603 * $entity->access($operation);
604 * $entity->nameOfField->access($operation);
606 * The interface related to access checking in entities and fields is
607 * \Drupal\Core\Access\AccessibleInterface.
609 * The default entity access control handler invokes two hooks while checking
610 * access on a single entity: hook_entity_access() is invoked first, and
611 * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name
612 * of the entity type). If no module returns a TRUE or FALSE value from
613 * either of these hooks, then the entity's default access checking takes
614 * place. For create operations (creating a new entity), the hooks that
615 * are invoked are hook_entity_create_access() and
616 * hook_ENTITY_TYPE_create_access() instead.
618 * The Node entity type has a complex system for determining access, which
619 * developers can interact with. This is described in the
620 * @link node_access Node access topic. @endlink
624 * @see \Drupal\Core\Entity\EntityManagerInterface::getTranslationFromContext()
634 * Control entity operation access.
636 * @param \Drupal\Core\Entity\EntityInterface $entity
637 * The entity to check access to.
638 * @param string $operation
639 * The operation that is to be performed on $entity.
640 * @param \Drupal\Core\Session\AccountInterface $account
641 * The account trying to access the entity.
643 * @return \Drupal\Core\Access\AccessResultInterface
644 * The access result. The final result is calculated by using
645 * \Drupal\Core\Access\AccessResultInterface::orIf() on the result of every
646 * hook_entity_access() and hook_ENTITY_TYPE_access() implementation, and the
647 * result of the entity-specific checkAccess() method in the entity access
648 * control handler. Be careful when writing generalized access checks shared
649 * between routing and entity checks: routing uses the andIf() operator. So
650 * returning an isNeutral() does not determine entity access at all but it
651 * always ends up denying access while routing.
653 * @see \Drupal\Core\Entity\EntityAccessControlHandler
654 * @see hook_entity_create_access()
655 * @see hook_ENTITY_TYPE_access()
657 * @ingroup entity_api
659 function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
661 return AccessResult::neutral();
665 * Control entity operation access for a specific entity type.
667 * @param \Drupal\Core\Entity\EntityInterface $entity
668 * The entity to check access to.
669 * @param string $operation
670 * The operation that is to be performed on $entity.
671 * @param \Drupal\Core\Session\AccountInterface $account
672 * The account trying to access the entity.
674 * @return \Drupal\Core\Access\AccessResultInterface
675 * The access result. hook_entity_access() has detailed documentation.
677 * @see \Drupal\Core\Entity\EntityAccessControlHandler
678 * @see hook_ENTITY_TYPE_create_access()
679 * @see hook_entity_access()
681 * @ingroup entity_api
683 function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
685 return AccessResult::neutral();
689 * Control entity create access.
691 * @param \Drupal\Core\Session\AccountInterface $account
692 * The account trying to access the entity.
693 * @param array $context
694 * An associative array of additional context values. By default it contains
695 * language and the entity type ID:
696 * - entity_type_id - the entity type ID.
697 * - langcode - the current language code.
698 * @param string $entity_bundle
699 * The entity bundle name.
701 * @return \Drupal\Core\Access\AccessResultInterface
704 * @see \Drupal\Core\Entity\EntityAccessControlHandler
705 * @see hook_entity_access()
706 * @see hook_ENTITY_TYPE_create_access()
708 * @ingroup entity_api
710 function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
712 return AccessResult::neutral();
716 * Control entity create access for a specific entity type.
718 * @param \Drupal\Core\Session\AccountInterface $account
719 * The account trying to access the entity.
720 * @param array $context
721 * An associative array of additional context values. By default it contains
723 * - langcode - the current language code.
724 * @param string $entity_bundle
725 * The entity bundle name.
727 * @return \Drupal\Core\Access\AccessResultInterface
730 * @see \Drupal\Core\Entity\EntityAccessControlHandler
731 * @see hook_ENTITY_TYPE_access()
732 * @see hook_entity_create_access()
734 * @ingroup entity_api
736 function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
738 return AccessResult::neutral();
742 * Add to entity type definitions.
744 * Modules may implement this hook to add information to defined entity types,
745 * as defined in \Drupal\Core\Entity\EntityTypeInterface.
747 * To alter existing information or to add information dynamically, use
748 * hook_entity_type_alter().
750 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
751 * An associative array of all entity type definitions, keyed by the entity
752 * type name. Passed by reference.
754 * @see \Drupal\Core\Entity\Entity
755 * @see \Drupal\Core\Entity\EntityTypeInterface
756 * @see hook_entity_type_alter()
758 function hook_entity_type_build(array &$entity_types) {
759 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
760 // Add a form for a custom node form without overriding the default
761 // node form. To override the default node form, use hook_entity_type_alter().
762 $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
766 * Alter the entity type definitions.
768 * Modules may implement this hook to alter the information that defines an
769 * entity type. All properties that are available in
770 * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally
771 * provided by modules can be altered here.
773 * Do not use this hook to add information to entity types, unless one of the
775 * - You are filling in default values.
776 * - You need to dynamically add information only in certain circumstances.
777 * - Your hook needs to run after hook_entity_type_build() implementations.
778 * Use hook_entity_type_build() instead in all other cases.
780 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
781 * An associative array of all entity type definitions, keyed by the entity
782 * type name. Passed by reference.
784 * @see \Drupal\Core\Entity\Entity
785 * @see \Drupal\Core\Entity\EntityTypeInterface
787 function hook_entity_type_alter(array &$entity_types) {
788 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
789 // Set the controller class for nodes to an alternate implementation of the
790 // Drupal\Core\Entity\EntityStorageInterface interface.
791 $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
795 * Alter the view modes for entity types.
797 * @param array $view_modes
798 * An array of view modes, keyed first by entity type, then by view mode name.
800 * @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
801 * @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
803 function hook_entity_view_mode_info_alter(&$view_modes) {
804 $view_modes['user']['full']['status'] = TRUE;
808 * Describe the bundles for entity types.
811 * An associative array of all entity bundles, keyed by the entity
812 * type name, and then the bundle name, with the following keys:
813 * - label: The human-readable name of the bundle.
814 * - uri_callback: The same as the 'uri_callback' key defined for the entity
815 * type in the EntityManager, but for the bundle only. When determining
816 * the URI of an entity, if a 'uri_callback' is defined for both the
817 * entity type and the bundle, the one for the bundle is used.
818 * - translatable: (optional) A boolean value specifying whether this bundle
819 * has translation support enabled. Defaults to FALSE.
821 * @see \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
822 * @see hook_entity_bundle_info_alter()
824 function hook_entity_bundle_info() {
825 $bundles['user']['user']['label'] = t('User');
830 * Alter the bundles for entity types.
832 * @param array $bundles
833 * An array of bundles, keyed first by entity type, then by bundle name.
835 * @see Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
836 * @see hook_entity_bundle_info()
838 function hook_entity_bundle_info_alter(&$bundles) {
839 $bundles['user']['user']['label'] = t('Full account');
843 * Act on entity_bundle_create().
845 * This hook is invoked after the operation has been performed.
847 * @param string $entity_type_id
848 * The type of $entity; e.g. 'node' or 'user'.
849 * @param string $bundle
850 * The name of the bundle.
854 function hook_entity_bundle_create($entity_type_id, $bundle) {
855 // When a new bundle is created, the menu needs to be rebuilt to add the
856 // Field UI menu item tabs.
857 \Drupal::service('router.builder')->setRebuildNeeded();
861 * Act on entity_bundle_delete().
863 * This hook is invoked after the operation has been performed.
865 * @param string $entity_type_id
866 * The type of entity; for example, 'node' or 'user'.
867 * @param string $bundle
868 * The bundle that was just deleted.
870 * @ingroup entity_crud
872 function hook_entity_bundle_delete($entity_type_id, $bundle) {
873 // Remove the settings associated with the bundle in my_module.settings.
874 $config = \Drupal::config('my_module.settings');
875 $bundle_settings = $config->get('bundle_settings');
876 if (isset($bundle_settings[$entity_type_id][$bundle])) {
877 unset($bundle_settings[$entity_type_id][$bundle]);
878 $config->set('bundle_settings', $bundle_settings);
883 * Acts when creating a new entity.
885 * This hook runs after a new entity object has just been instantiated.
887 * @param \Drupal\Core\Entity\EntityInterface $entity
890 * @ingroup entity_crud
891 * @see hook_ENTITY_TYPE_create()
893 function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
894 \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
898 * Acts when creating a new entity of a specific type.
900 * This hook runs after a new entity object has just been instantiated.
902 * @param \Drupal\Core\Entity\EntityInterface $entity
905 * @ingroup entity_crud
906 * @see hook_entity_create()
908 function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
909 \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
913 * Act on entities when loaded.
915 * This is a generic load hook called for all entity types loaded via the
918 * hook_entity_storage_load() should be used to load additional data for
921 * @param \Drupal\Core\Entity\EntityInterface[] $entities
922 * The entities keyed by entity ID.
923 * @param string $entity_type_id
924 * The type of entities being loaded (i.e. node, user, comment).
926 * @ingroup entity_crud
927 * @see hook_ENTITY_TYPE_load()
929 function hook_entity_load(array $entities, $entity_type_id) {
930 foreach ($entities as $entity) {
931 $entity->foo = mymodule_add_something($entity);
936 * Act on entities of a specific type when loaded.
938 * @param array $entities
939 * The entities keyed by entity ID.
941 * @ingroup entity_crud
942 * @see hook_entity_load()
944 function hook_ENTITY_TYPE_load($entities) {
945 foreach ($entities as $entity) {
946 $entity->foo = mymodule_add_something($entity);
951 * Act on content entities when loaded from the storage.
953 * The results of this hook will be cached.
955 * @param \Drupal\Core\Entity\EntityInterface[] $entities
956 * The entities keyed by entity ID.
957 * @param string $entity_type
958 * The type of entities being loaded (i.e. node, user, comment).
960 * @see hook_entity_load()
962 function hook_entity_storage_load(array $entities, $entity_type) {
963 foreach ($entities as $entity) {
964 $entity->foo = mymodule_add_something_uncached($entity);
969 * Act on content entities of a given type when loaded from the storage.
971 * The results of this hook will be cached if the entity type supports it.
973 * @param \Drupal\Core\Entity\EntityInterface[] $entities
974 * The entities keyed by entity ID.
976 * @see hook_entity_storage_load()
978 function hook_ENTITY_TYPE_storage_load(array $entities) {
979 foreach ($entities as $entity) {
980 $entity->foo = mymodule_add_something_uncached($entity);
985 * Act on an entity before it is created or updated.
987 * You can get the original entity object from $entity->original when it is an
988 * update of the entity.
990 * @param \Drupal\Core\Entity\EntityInterface $entity
993 * @ingroup entity_crud
994 * @see hook_ENTITY_TYPE_presave()
996 function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
997 if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
998 $route_match = \Drupal::routeMatch();
999 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
1004 * Act on a specific type of entity before it is created or updated.
1006 * You can get the original entity object from $entity->original when it is an
1007 * update of the entity.
1009 * @param \Drupal\Core\Entity\EntityInterface $entity
1010 * The entity object.
1012 * @ingroup entity_crud
1013 * @see hook_entity_presave()
1015 function hook_ENTITY_TYPE_presave(Drupal\Core\Entity\EntityInterface $entity) {
1016 if ($entity->isTranslatable()) {
1017 $route_match = \Drupal::routeMatch();
1018 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
1023 * Respond to creation of a new entity.
1025 * This hook runs once the entity has been stored. Note that hook
1026 * implementations may not alter the stored entity data.
1028 * @param \Drupal\Core\Entity\EntityInterface $entity
1029 * The entity object.
1031 * @ingroup entity_crud
1032 * @see hook_ENTITY_TYPE_insert()
1034 function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
1035 // Insert the new entity into a fictional table of all entities.
1036 db_insert('example_entity')
1038 'type' => $entity->getEntityTypeId(),
1039 'id' => $entity->id(),
1040 'created' => REQUEST_TIME,
1041 'updated' => REQUEST_TIME,
1047 * Respond to creation of a new entity of a particular type.
1049 * This hook runs once the entity has been stored. Note that hook
1050 * implementations may not alter the stored entity data.
1052 * @param \Drupal\Core\Entity\EntityInterface $entity
1053 * The entity object.
1055 * @ingroup entity_crud
1056 * @see hook_entity_insert()
1058 function hook_ENTITY_TYPE_insert(Drupal\Core\Entity\EntityInterface $entity) {
1059 // Insert the new entity into a fictional table of this type of entity.
1060 db_insert('example_entity')
1062 'id' => $entity->id(),
1063 'created' => REQUEST_TIME,
1064 'updated' => REQUEST_TIME,
1070 * Respond to updates to an entity.
1072 * This hook runs once the entity storage has been updated. Note that hook
1073 * implementations may not alter the stored entity data. Get the original entity
1074 * object from $entity->original.
1076 * @param \Drupal\Core\Entity\EntityInterface $entity
1077 * The entity object.
1079 * @ingroup entity_crud
1080 * @see hook_ENTITY_TYPE_update()
1082 function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
1083 // Update the entity's entry in a fictional table of all entities.
1084 db_update('example_entity')
1086 'updated' => REQUEST_TIME,
1088 ->condition('type', $entity->getEntityTypeId())
1089 ->condition('id', $entity->id())
1094 * Respond to updates to an entity of a particular type.
1096 * This hook runs once the entity storage has been updated. Note that hook
1097 * implementations may not alter the stored entity data. Get the original entity
1098 * object from $entity->original.
1100 * @param \Drupal\Core\Entity\EntityInterface $entity
1101 * The entity object.
1103 * @ingroup entity_crud
1104 * @see hook_entity_update()
1106 function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
1107 // Update the entity's entry in a fictional table of this type of entity.
1108 db_update('example_entity')
1110 'updated' => REQUEST_TIME,
1112 ->condition('id', $entity->id())
1117 * Acts when creating a new entity translation.
1119 * This hook runs after a new entity translation object has just been
1122 * @param \Drupal\Core\Entity\EntityInterface $translation
1123 * The entity object.
1125 * @ingroup entity_crud
1126 * @see hook_ENTITY_TYPE_translation_create()
1128 function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
1129 \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
1133 * Acts when creating a new entity translation of a specific type.
1135 * This hook runs after a new entity translation object has just been
1138 * @param \Drupal\Core\Entity\EntityInterface $translation
1139 * The entity object.
1141 * @ingroup entity_crud
1142 * @see hook_entity_translation_create()
1144 function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
1145 \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
1149 * Respond to creation of a new entity translation.
1151 * This hook runs once the entity translation has been stored. Note that hook
1152 * implementations may not alter the stored entity translation data.
1154 * @param \Drupal\Core\Entity\EntityInterface $translation
1155 * The entity object of the translation just stored.
1157 * @ingroup entity_crud
1158 * @see hook_ENTITY_TYPE_translation_insert()
1160 function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
1162 '@language' => $translation->language()->getName(),
1163 '@label' => $translation->getUntranslated()->label(),
1165 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
1169 * Respond to creation of a new entity translation of a particular type.
1171 * This hook runs once the entity translation has been stored. Note that hook
1172 * implementations may not alter the stored entity translation data.
1174 * @param \Drupal\Core\Entity\EntityInterface $translation
1175 * The entity object of the translation just stored.
1177 * @ingroup entity_crud
1178 * @see hook_entity_translation_insert()
1180 function hook_ENTITY_TYPE_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
1182 '@language' => $translation->language()->getName(),
1183 '@label' => $translation->getUntranslated()->label(),
1185 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
1189 * Respond to entity translation deletion.
1191 * This hook runs once the entity translation has been deleted from storage.
1193 * @param \Drupal\Core\Entity\EntityInterface $translation
1194 * The original entity object.
1196 * @ingroup entity_crud
1197 * @see hook_ENTITY_TYPE_translation_delete()
1199 function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
1201 '@language' => $translation->language()->getName(),
1202 '@label' => $translation->label(),
1204 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
1208 * Respond to entity translation deletion of a particular type.
1210 * This hook runs once the entity translation has been deleted from storage.
1212 * @param \Drupal\Core\Entity\EntityInterface $translation
1213 * The original entity object.
1215 * @ingroup entity_crud
1216 * @see hook_entity_translation_delete()
1218 function hook_ENTITY_TYPE_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
1220 '@language' => $translation->language()->getName(),
1221 '@label' => $translation->label(),
1223 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
1227 * Act before entity deletion.
1229 * @param \Drupal\Core\Entity\EntityInterface $entity
1230 * The entity object for the entity that is about to be deleted.
1232 * @ingroup entity_crud
1233 * @see hook_ENTITY_TYPE_predelete()
1235 function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
1236 // Count references to this entity in a custom table before they are removed
1237 // upon entity deletion.
1238 $id = $entity->id();
1239 $type = $entity->getEntityTypeId();
1240 $count = db_select('example_entity_data')
1241 ->condition('type', $type)
1242 ->condition('id', $id)
1247 // Log the count in a table that records this statistic for deleted entities.
1248 db_merge('example_deleted_entity_statistics')
1249 ->key(['type' => $type, 'id' => $id])
1250 ->fields(['count' => $count])
1255 * Act before entity deletion of a particular entity type.
1257 * @param \Drupal\Core\Entity\EntityInterface $entity
1258 * The entity object for the entity that is about to be deleted.
1260 * @ingroup entity_crud
1261 * @see hook_entity_predelete()
1263 function hook_ENTITY_TYPE_predelete(Drupal\Core\Entity\EntityInterface $entity) {
1264 // Count references to this entity in a custom table before they are removed
1265 // upon entity deletion.
1266 $id = $entity->id();
1267 $type = $entity->getEntityTypeId();
1268 $count = db_select('example_entity_data')
1269 ->condition('type', $type)
1270 ->condition('id', $id)
1275 // Log the count in a table that records this statistic for deleted entities.
1276 db_merge('example_deleted_entity_statistics')
1277 ->key(['type' => $type, 'id' => $id])
1278 ->fields(['count' => $count])
1283 * Respond to entity deletion.
1285 * This hook runs once the entity has been deleted from the storage.
1287 * @param \Drupal\Core\Entity\EntityInterface $entity
1288 * The entity object for the entity that has been deleted.
1290 * @ingroup entity_crud
1291 * @see hook_ENTITY_TYPE_delete()
1293 function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
1294 // Delete the entity's entry from a fictional table of all entities.
1295 db_delete('example_entity')
1296 ->condition('type', $entity->getEntityTypeId())
1297 ->condition('id', $entity->id())
1302 * Respond to entity deletion of a particular type.
1304 * This hook runs once the entity has been deleted from the storage.
1306 * @param \Drupal\Core\Entity\EntityInterface $entity
1307 * The entity object for the entity that has been deleted.
1309 * @ingroup entity_crud
1310 * @see hook_entity_delete()
1312 function hook_ENTITY_TYPE_delete(Drupal\Core\Entity\EntityInterface $entity) {
1313 // Delete the entity's entry from a fictional table of all entities.
1314 db_delete('example_entity')
1315 ->condition('type', $entity->getEntityTypeId())
1316 ->condition('id', $entity->id())
1321 * Respond to entity revision deletion.
1323 * This hook runs once the entity revision has been deleted from the storage.
1325 * @param \Drupal\Core\Entity\EntityInterface $entity
1326 * The entity object for the entity revision that has been deleted.
1328 * @ingroup entity_crud
1329 * @see hook_ENTITY_TYPE_revision_delete()
1331 function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
1332 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
1333 foreach ($referenced_files_by_field as $field => $uuids) {
1334 _editor_delete_file_usage($uuids, $entity, 1);
1339 * Respond to entity revision deletion of a particular type.
1341 * This hook runs once the entity revision has been deleted from the storage.
1343 * @param \Drupal\Core\Entity\EntityInterface $entity
1344 * The entity object for the entity revision that has been deleted.
1346 * @ingroup entity_crud
1347 * @see hook_entity_revision_delete()
1349 function hook_ENTITY_TYPE_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
1350 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
1351 foreach ($referenced_files_by_field as $field => $uuids) {
1352 _editor_delete_file_usage($uuids, $entity, 1);
1357 * Act on entities being assembled before rendering.
1360 * A renderable array representing the entity content. The module may add
1361 * elements to $build prior to rendering. The structure of $build is a
1362 * renderable array as expected by drupal_render().
1363 * @param \Drupal\Core\Entity\EntityInterface $entity
1364 * The entity object.
1365 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1366 * The entity view display holding the display options configured for the
1367 * entity components.
1369 * The view mode the entity is rendered in.
1371 * @see hook_entity_view_alter()
1372 * @see hook_ENTITY_TYPE_view()
1374 * @ingroup entity_crud
1376 function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
1377 // Only do the extra work if the component is configured to be displayed.
1378 // This assumes a 'mymodule_addition' extra field has been defined for the
1379 // entity bundle in hook_entity_extra_field_info().
1380 if ($display->getComponent('mymodule_addition')) {
1381 $build['mymodule_addition'] = [
1382 '#markup' => mymodule_addition($entity),
1383 '#theme' => 'mymodule_my_additional_field',
1389 * Act on entities of a particular type being assembled before rendering.
1392 * A renderable array representing the entity content. The module may add
1393 * elements to $build prior to rendering. The structure of $build is a
1394 * renderable array as expected by drupal_render().
1395 * @param \Drupal\Core\Entity\EntityInterface $entity
1396 * The entity object.
1397 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1398 * The entity view display holding the display options configured for the
1399 * entity components.
1401 * The view mode the entity is rendered in.
1403 * @see hook_ENTITY_TYPE_view_alter()
1404 * @see hook_entity_view()
1406 * @ingroup entity_crud
1408 function hook_ENTITY_TYPE_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
1409 // Only do the extra work if the component is configured to be displayed.
1410 // This assumes a 'mymodule_addition' extra field has been defined for the
1411 // entity bundle in hook_entity_extra_field_info().
1412 if ($display->getComponent('mymodule_addition')) {
1413 $build['mymodule_addition'] = [
1414 '#markup' => mymodule_addition($entity),
1415 '#theme' => 'mymodule_my_additional_field',
1421 * Alter the results of the entity build array.
1423 * This hook is called after the content has been assembled in a structured
1424 * array and may be used for doing processing which requires that the complete
1425 * entity content structure has been built.
1427 * If a module wishes to act on the rendered HTML of the entity rather than the
1428 * structured content array, it may use this hook to add a #post_render
1429 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
1430 * the particular entity type template, if there is one (e.g., node.html.twig).
1432 * See the @link themeable Default theme implementations topic @endlink and
1433 * drupal_render() for details.
1435 * @param array &$build
1436 * A renderable array representing the entity content.
1437 * @param \Drupal\Core\Entity\EntityInterface $entity
1438 * The entity object being rendered.
1439 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1440 * The entity view display holding the display options configured for the
1441 * entity components.
1443 * @ingroup entity_crud
1445 * @see hook_entity_view()
1446 * @see hook_ENTITY_TYPE_view_alter()
1448 function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
1449 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
1450 // Change its weight.
1451 $build['an_additional_field']['#weight'] = -10;
1453 // Add a #post_render callback to act on the rendered HTML of the entity.
1454 $build['#post_render'][] = 'my_module_node_post_render';
1459 * Alter the results of the entity build array for a particular entity type.
1461 * This hook is called after the content has been assembled in a structured
1462 * array and may be used for doing processing which requires that the complete
1463 * entity content structure has been built.
1465 * If a module wishes to act on the rendered HTML of the entity rather than the
1466 * structured content array, it may use this hook to add a #post_render
1467 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
1468 * the particular entity type template, if there is one (e.g., node.html.twig).
1470 * See the @link themeable Default theme implementations topic @endlink and
1471 * drupal_render() for details.
1473 * @param array &$build
1474 * A renderable array representing the entity content.
1475 * @param \Drupal\Core\Entity\EntityInterface $entity
1476 * The entity object being rendered.
1477 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1478 * The entity view display holding the display options configured for the
1479 * entity components.
1481 * @ingroup entity_crud
1483 * @see hook_ENTITY_TYPE_view()
1484 * @see hook_entity_view_alter()
1486 function hook_ENTITY_TYPE_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
1487 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
1488 // Change its weight.
1489 $build['an_additional_field']['#weight'] = -10;
1491 // Add a #post_render callback to act on the rendered HTML of the entity.
1492 $build['#post_render'][] = 'my_module_node_post_render';
1497 * Act on entities as they are being prepared for view.
1499 * Allows you to operate on multiple entities as they are being prepared for
1500 * view. Only use this if attaching the data during the entity loading phase
1501 * is not appropriate, for example when attaching other 'entity' style objects.
1503 * @param string $entity_type_id
1504 * The type of entities being viewed (i.e. node, user, comment).
1505 * @param array $entities
1506 * The entities keyed by entity ID.
1507 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
1508 * The array of entity view displays holding the display options configured
1509 * for the entity components, keyed by bundle name.
1510 * @param string $view_mode
1513 * @ingroup entity_crud
1515 function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
1516 // Load a specific node into the user object for later theming.
1517 if (!empty($entities) && $entity_type_id == 'user') {
1518 // Only do the extra work if the component is configured to be
1519 // displayed. This assumes a 'mymodule_addition' extra field has been
1520 // defined for the entity bundle in hook_entity_extra_field_info().
1522 foreach ($entities as $id => $entity) {
1523 if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) {
1528 $nodes = mymodule_get_user_nodes($ids);
1529 foreach ($ids as $id) {
1530 $entities[$id]->user_node = $nodes[$id];
1537 * Change the view mode of an entity that is being displayed.
1539 * @param string $view_mode
1540 * The view_mode that is to be used to display the entity.
1541 * @param \Drupal\Core\Entity\EntityInterface $entity
1542 * The entity that is being viewed.
1543 * @param array $context
1544 * Array with additional context information, currently only contains the
1545 * langcode the entity is viewed in.
1547 * @ingroup entity_crud
1549 function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
1550 // For nodes, change the view mode when it is teaser.
1551 if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
1552 $view_mode = 'my_custom_view_mode';
1557 * Alter entity renderable values before cache checking in drupal_render().
1559 * Invoked for a specific entity type.
1561 * The values in the #cache key of the renderable array are used to determine if
1562 * a cache entry exists for the entity's rendered output. Ideally only values
1563 * that pertain to caching should be altered in this hook.
1565 * @param array &$build
1566 * A renderable array containing the entity's caching and view mode values.
1567 * @param \Drupal\Core\Entity\EntityInterface $entity
1568 * The entity that is being viewed.
1569 * @param string $view_mode
1570 * The view_mode that is to be used to display the entity.
1572 * @see drupal_render()
1573 * @see \Drupal\Core\Entity\EntityViewBuilder
1574 * @see hook_entity_build_defaults_alter()
1576 * @ingroup entity_crud
1578 function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
1583 * Alter entity renderable values before cache checking in drupal_render().
1585 * The values in the #cache key of the renderable array are used to determine if
1586 * a cache entry exists for the entity's rendered output. Ideally only values
1587 * that pertain to caching should be altered in this hook.
1589 * @param array &$build
1590 * A renderable array containing the entity's caching and view mode values.
1591 * @param \Drupal\Core\Entity\EntityInterface $entity
1592 * The entity that is being viewed.
1593 * @param string $view_mode
1594 * The view_mode that is to be used to display the entity.
1596 * @see drupal_render()
1597 * @see \Drupal\Core\Entity\EntityViewBuilder
1598 * @see hook_ENTITY_TYPE_build_defaults_alter()
1600 * @ingroup entity_crud
1602 function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
1607 * Alter the settings used for displaying an entity.
1609 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
1610 * The entity view display that will be used to display the entity
1612 * @param array $context
1613 * An associative array containing:
1614 * - entity_type: The entity type, e.g., 'node' or 'user'.
1615 * - bundle: The bundle, e.g., 'page' or 'article'.
1616 * - view_mode: The view mode, e.g., 'full', 'teaser', etc.
1618 * @ingroup entity_crud
1620 function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
1621 // Leave field labels out of the search index.
1622 if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
1623 foreach ($display->getComponents() as $name => $options) {
1624 if (isset($options['label'])) {
1625 $options['label'] = 'hidden';
1626 $display->setComponent($name, $options);
1633 * Alter the render array generated by an EntityDisplay for an entity.
1635 * @param array $build
1636 * The renderable array generated by the EntityDisplay.
1637 * @param array $context
1638 * An associative array containing:
1639 * - entity: The entity being rendered.
1640 * - view_mode: The view mode; for example, 'full' or 'teaser'.
1641 * - display: The EntityDisplay holding the display options.
1643 * @ingroup entity_crud
1645 function hook_entity_display_build_alter(&$build, $context) {
1646 // Append RDF term mappings on displayed taxonomy links.
1647 foreach (Element::children($build) as $field_name) {
1648 $element = &$build[$field_name];
1649 if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') {
1650 foreach ($element['#items'] as $delta => $item) {
1651 $term = $item->entity;
1652 if (!empty($term->rdf_mapping['rdftype'])) {
1653 $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
1655 if (!empty($term->rdf_mapping['name']['predicates'])) {
1656 $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
1664 * Acts on an entity object about to be shown on an entity form.
1666 * This can be typically used to pre-fill entity values or change the form state
1667 * before the entity form is built. It is invoked just once when first building
1668 * the entity form. Rebuilds will not trigger a new invocation.
1670 * @param \Drupal\Core\Entity\EntityInterface $entity
1671 * The entity that is about to be shown on the form.
1673 * The current operation.
1674 * @param \Drupal\Core\Form\FormStateInterface $form_state
1675 * The current state of the form.
1677 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
1678 * @see hook_ENTITY_TYPE_prepare_form()
1680 * @ingroup entity_crud
1682 function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
1683 if ($operation == 'edit') {
1684 $entity->label->value = 'Altered label';
1685 $form_state->set('label_altered', TRUE);
1690 * Acts on a particular type of entity object about to be in an entity form.
1692 * This can be typically used to pre-fill entity values or change the form state
1693 * before the entity form is built. It is invoked just once when first building
1694 * the entity form. Rebuilds will not trigger a new invocation.
1696 * @param \Drupal\Core\Entity\EntityInterface $entity
1697 * The entity that is about to be shown on the form.
1699 * The current operation.
1700 * @param \Drupal\Core\Form\FormStateInterface $form_state
1701 * The current state of the form.
1703 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
1704 * @see hook_entity_prepare_form()
1706 * @ingroup entity_crud
1708 function hook_ENTITY_TYPE_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
1709 if ($operation == 'edit') {
1710 $entity->label->value = 'Altered label';
1711 $form_state->set('label_altered', TRUE);
1716 * Alter the settings used for displaying an entity form.
1718 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
1719 * The entity_form_display object that will be used to display the entity form
1721 * @param array $context
1722 * An associative array containing:
1723 * - entity_type: The entity type, e.g., 'node' or 'user'.
1724 * - bundle: The bundle, e.g., 'page' or 'article'.
1725 * - form_mode: The form mode; e.g., 'default', 'profile', 'register', etc.
1727 * @ingroup entity_crud
1729 function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
1730 // Hide the 'user_picture' field from the register form.
1731 if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
1732 $form_display->setComponent('user_picture', [
1733 'region' => 'hidden',
1739 * Provides custom base field definitions for a content entity type.
1741 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1742 * The entity type definition.
1744 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
1745 * An array of field definitions, keyed by field name.
1747 * @see hook_entity_base_field_info_alter()
1748 * @see hook_entity_bundle_field_info()
1749 * @see hook_entity_bundle_field_info_alter()
1750 * @see \Drupal\Core\Field\FieldDefinitionInterface
1751 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
1753 function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1754 if ($entity_type->id() == 'node') {
1756 $fields['mymodule_text'] = BaseFieldDefinition::create('string')
1757 ->setLabel(t('The text'))
1758 ->setDescription(t('A text property added by mymodule.'))
1760 ->setClass('\Drupal\mymodule\EntityComputedText');
1767 * Alter base field definitions for a content entity type.
1769 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
1770 * The array of base field definitions for the entity type.
1771 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1772 * The entity type definition.
1774 * @see hook_entity_base_field_info()
1775 * @see hook_entity_bundle_field_info()
1776 * @see hook_entity_bundle_field_info_alter()
1778 * @todo WARNING: This hook will be changed in
1779 * https://www.drupal.org/node/2346329.
1781 function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1782 // Alter the mymodule_text field to use a custom class.
1783 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
1784 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
1789 * Provides field definitions for a specific bundle within an entity type.
1791 * Bundle fields either have to override an existing base field, or need to
1792 * provide a field storage definition via hook_entity_field_storage_info()
1793 * unless they are computed.
1795 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1796 * The entity type definition.
1797 * @param string $bundle
1799 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
1800 * The list of base field definitions for the entity type.
1802 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
1803 * An array of bundle field definitions, keyed by field name.
1805 * @see hook_entity_base_field_info()
1806 * @see hook_entity_base_field_info_alter()
1807 * @see hook_entity_field_storage_info()
1808 * @see hook_entity_field_storage_info_alter()
1809 * @see hook_entity_bundle_field_info_alter()
1810 * @see \Drupal\Core\Field\FieldDefinitionInterface
1811 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
1813 * @todo WARNING: This hook will be changed in
1814 * https://www.drupal.org/node/2346347.
1816 function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
1817 // Add a property only to nodes of the 'article' bundle.
1818 if ($entity_type->id() == 'node' && $bundle == 'article') {
1820 $fields['mymodule_text_more'] = BaseFieldDefinition::create('string')
1821 ->setLabel(t('More text'))
1823 ->setClass('\Drupal\mymodule\EntityComputedMoreText');
1829 * Alter bundle field definitions.
1831 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
1832 * The array of bundle field definitions.
1833 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1834 * The entity type definition.
1835 * @param string $bundle
1838 * @see hook_entity_base_field_info()
1839 * @see hook_entity_base_field_info_alter()
1840 * @see hook_entity_bundle_field_info()
1842 * @todo WARNING: This hook will be changed in
1843 * https://www.drupal.org/node/2346347.
1845 function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
1846 if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
1847 // Alter the mymodule_text field to use a custom class.
1848 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
1853 * Provides field storage definitions for a content entity type.
1855 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1856 * The entity type definition.
1858 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
1859 * An array of field storage definitions, keyed by field name.
1861 * @see hook_entity_field_storage_info_alter()
1862 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface
1863 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldStorageDefinitions()
1865 function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1866 if (\Drupal::entityManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) {
1867 // Query by filtering on the ID as this is more efficient than filtering
1868 // on the entity_type property directly.
1869 $ids = \Drupal::entityQuery('field_storage_config')
1870 ->condition('id', $entity_type->id() . '.', 'STARTS_WITH')
1872 // Fetch all fields and key them by field name.
1873 $field_storages = FieldStorageConfig::loadMultiple($ids);
1875 foreach ($field_storages as $field_storage) {
1876 $result[$field_storage->getName()] = $field_storage;
1884 * Alter field storage definitions for a content entity type.
1886 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields
1887 * The array of field storage definitions for the entity type.
1888 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
1889 * The entity type definition.
1891 * @see hook_entity_field_storage_info()
1893 function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
1894 // Alter the max_length setting.
1895 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
1896 $fields['mymodule_text']->setSetting('max_length', 128);
1901 * Declares entity operations.
1903 * @param \Drupal\Core\Entity\EntityInterface $entity
1904 * The entity on which the linked operations will be performed.
1907 * An operations array as returned by
1908 * EntityListBuilderInterface::getOperations().
1910 * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
1912 function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
1914 $operations['translate'] = [
1915 'title' => t('Translate'),
1916 'url' => \Drupal\Core\Url::fromRoute('foo_module.entity.translate'),
1924 * Alter entity operations.
1926 * @param array $operations
1927 * Operations array as returned by
1928 * \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
1929 * @param \Drupal\Core\Entity\EntityInterface $entity
1930 * The entity on which the linked operations will be performed.
1932 function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) {
1933 // Alter the title and weight.
1934 $operations['translate']['title'] = t('Translate @entity_type', [
1935 '@entity_type' => $entity->getEntityTypeId(),
1937 $operations['translate']['weight'] = 99;
1941 * Control access to fields.
1943 * This hook is invoked from
1944 * \Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess() to let modules
1945 * grant or deny operations on fields.
1947 * @param string $operation
1948 * The operation to be performed. See
1949 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
1950 * for possible values.
1951 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
1952 * The field definition.
1953 * @param \Drupal\Core\Session\AccountInterface $account
1954 * The user account to check.
1955 * @param \Drupal\Core\Field\FieldItemListInterface $items
1956 * (optional) The entity field object on which the operation is to be
1959 * @return \Drupal\Core\Access\AccessResultInterface
1960 * The access result.
1962 * @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
1964 function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
1965 if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
1966 return AccessResult::allowedIfHasPermission($account, 'update field of interest');
1968 return AccessResult::neutral();
1972 * Alter the default access behavior for a given field.
1974 * Use this hook to override access grants from another module. Note that the
1975 * original default access flag is masked under the ':default' key.
1977 * @param \Drupal\Core\Access\AccessResultInterface[] $grants
1978 * An array of grants gathered by hook_entity_field_access(). The array is
1979 * keyed by the module that defines the field's access control; the values are
1980 * grant responses for each module (\Drupal\Core\Access\AccessResult).
1981 * @param array $context
1982 * Context array on the performed operation with the following keys:
1983 * - operation: The operation to be performed (string).
1984 * - field_definition: The field definition object
1985 * (\Drupal\Core\Field\FieldDefinitionInterface)
1986 * - account: The user account to check access for
1987 * (Drupal\user\Entity\User).
1988 * - items: (optional) The entity field items
1989 * (\Drupal\Core\Field\FieldItemListInterface).
1991 function hook_entity_field_access_alter(array &$grants, array $context) {
1992 /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
1993 $field_definition = $context['field_definition'];
1994 if ($field_definition->getName() == 'field_of_interest' && $grants['node']->isForbidden()) {
1995 // Override node module's restriction to no opinion (neither allowed nor
1996 // forbidden). We don't want to provide our own access hook, we only want to
1997 // take out node module's part in the access handling of this field. We also
1998 // don't want to switch node module's grant to
1999 // AccessResultInterface::isAllowed() , because the grants of other modules
2000 // should still decide on their own if this field is accessible or not
2001 $grants['node'] = AccessResult::neutral()->inheritCacheability($grants['node']);
2006 * Acts when initializing a fieldable entity object.
2008 * This hook runs after a new entity object or a new entity translation object
2009 * has just been instantiated. It can be used to set initial values, e.g. to
2012 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
2013 * The entity object.
2015 * @ingroup entity_crud
2016 * @see hook_ENTITY_TYPE_field_values_init()
2018 function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
2019 if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
2020 $entity->foo->value = 'some_initial_value';
2025 * Acts when initializing a fieldable entity object.
2027 * This hook runs after a new entity object or a new entity translation object
2028 * has just been instantiated. It can be used to set initial values, e.g. to
2031 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
2032 * The entity object.
2034 * @ingroup entity_crud
2035 * @see hook_entity_field_values_init()
2037 function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
2038 if (!$entity->foo->value) {
2039 $entity->foo->value = 'some_initial_value';
2044 * Exposes "pseudo-field" components on content entities.
2046 * Field UI's "Manage fields" and "Manage display" pages let users re-order
2047 * fields, but also non-field components. For nodes, these include elements
2048 * exposed by modules through hook_form_alter(), for instance.
2050 * Content entities or modules that want to have their components supported
2051 * should expose them using this hook. The user-defined settings (weight,
2052 * visible) are automatically applied when entities or entity forms are
2055 * @see hook_entity_extra_field_info_alter()
2058 * The array structure is identical to that of the return value of
2059 * \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields().
2061 function hook_entity_extra_field_info() {
2063 $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
2064 $description = t('Node module element');
2066 foreach (NodeType::loadMultiple() as $bundle) {
2068 // Add also the 'language' select if Language module is enabled and the
2069 // bundle has multilingual support.
2070 // Visibility of the ordering of the language selector is the same as on the
2072 if ($module_language_enabled) {
2073 $configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
2074 if ($configuration->isLanguageAlterable()) {
2075 $extra['node'][$bundle->id()]['form']['language'] = [
2076 'label' => t('Language'),
2077 'description' => $description,
2082 $extra['node'][$bundle->id()]['display']['language'] = [
2083 'label' => t('Language'),
2084 'description' => $description,
2094 * Alter "pseudo-field" components on content entities.
2096 * @param array $info
2097 * The array structure is identical to that of the return value of
2098 * \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
2100 * @see hook_entity_extra_field_info()
2102 function hook_entity_extra_field_info_alter(&$info) {
2103 // Force node title to always be at the top of the list by default.
2104 foreach (NodeType::loadMultiple() as $bundle) {
2105 if (isset($info['node'][$bundle->id()]['form']['title'])) {
2106 $info['node'][$bundle->id()]['form']['title']['weight'] = -20;
2112 * @} End of "addtogroup hooks".