diff core/lib/Drupal/Core/Entity/entity.api.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/lib/Drupal/Core/Entity/entity.api.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,2034 @@
+<?php
+
+/**
+ * @file
+ * Hooks and documentation related to entities.
+ */
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Render\Element;
+use Drupal\language\Entity\ContentLanguageSettings;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * @defgroup entity_crud Entity CRUD, editing, and view hooks
+ * @{
+ * Hooks used in various entity operations.
+ *
+ * Entity create, read, update, and delete (CRUD) operations are performed by
+ * entity storage classes; see the
+ * @link entity_api Entity API topic @endlink for more information. Most
+ * entities use or extend the default classes:
+ * \Drupal\Core\Entity\Sql\SqlContentEntityStorage for content entities, and
+ * \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities.
+ * For these entities, there is a set of hooks that is invoked for each
+ * CRUD operation, which module developers can implement to affect these
+ * operations; these hooks are actually invoked from methods on
+ * \Drupal\Core\Entity\EntityStorageBase.
+ *
+ * For content entities, viewing and rendering are handled by a view builder
+ * class; see the @link entity_api Entity API topic @endlink for more
+ * information. Most view builders extend or use the default class
+ * \Drupal\Core\Entity\EntityViewBuilder.
+ *
+ * Entity editing (including adding new entities) is handled by entity form
+ * classes; see the @link entity_api Entity API topic @endlink for more
+ * information. Most entity editing forms extend base classes
+ * \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm.
+ * Note that many other operations, such as confirming deletion of entities,
+ * also use entity form classes.
+ *
+ * This topic lists all of the entity CRUD and view operations, and the hooks
+ * and other operations that are invoked (in order) for each operation. Some
+ * notes:
+ * - Whenever an entity hook is invoked, there is both a type-specific entity
+ *   hook, and a generic entity hook. For instance, during a create operation on
+ *   a node, first hook_node_create() and then hook_entity_create() would be
+ *   invoked.
+ * - The entity-type-specific hooks are represented in the list below as
+ *   hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To
+ *   implement one of these hooks for an entity whose machine name is "foo",
+ *   define a function called mymodule_foo_create(), for instance. Also note
+ *   that the entity or array of entities that are passed into a specific-type
+ *   hook are of the specific entity class, not the generic Entity class, so in
+ *   your implementation, you can make the $entity argument something like $node
+ *   and give it a specific type hint (which should normally be to the specific
+ *   interface, such as \Drupal\Node\NodeInterface for nodes).
+ * - $storage in the code examples is assumed to be an entity storage
+ *   class. See the @link entity_api Entity API topic @endlink for
+ *   information on how to instantiate the correct storage class for an
+ *   entity type.
+ * - $view_builder in the code examples is assumed to be an entity view builder
+ *   class. See the @link entity_api Entity API topic @endlink for
+ *   information on how to instantiate the correct view builder class for
+ *   an entity type.
+ * - During many operations, static methods are called on the entity class,
+ *   which implements \Drupal\Entity\EntityInterface.
+ *
+ * @section create Create operations
+ * To create an entity:
+ * @code
+ * $entity = $storage->create();
+ *
+ * // Add code here to set properties on the entity.
+ *
+ * // Until you call save(), the entity is just in memory.
+ * $entity->save();
+ * @endcode
+ * There is also a shortcut method on entity classes, which creates an entity
+ * with an array of provided property values: \Drupal\Core\Entity::create().
+ *
+ * Hooks invoked during the create operation:
+ * - hook_ENTITY_TYPE_create()
+ * - hook_entity_create()
+ *
+ * See @ref save below for the save portion of the operation.
+ *
+ * @section load Read/Load operations
+ * To load (read) a single entity:
+ * @code
+ * $entity = $storage->load($id);
+ * @endcode
+ * To load multiple entities:
+ * @code
+ * $entities = $storage->loadMultiple($ids);
+ * @endcode
+ * Since load() calls loadMultiple(), these are really the same operation.
+ * Here is the order of hooks and other operations that take place during
+ * entity loading:
+ * - Entity is loaded from storage.
+ * - postLoad() is called on the entity class, passing in all of the loaded
+ *   entities.
+ * - hook_entity_load()
+ * - hook_ENTITY_TYPE_load()
+ *
+ * When an entity is loaded, normally the default entity revision is loaded.
+ * It is also possible to load a different revision, for entities that support
+ * revisions, with this code:
+ * @code
+ * $entity = $storage->loadRevision($revision_id);
+ * @endcode
+ * This involves the same hooks and operations as regular entity loading.
+ *
+ * @section entities_revisions_translations Entities, revisions and translations
+ *
+ * A translation is not a revision and a revision is not necessarily a
+ * translation. Revisions and translations are the two axes on the "spreadsheet"
+ * of an entity. If you use the built-in UI and have revisions enabled, then a
+ * new translation change would create a new revision (with a copy of all data
+ * for other languages in that revision). If an entity does not use revisions or
+ * the entity is being modified via the API, then multiple translations can be
+ * modified in a single revision. Conceptually, the revisions are columns on the
+ * spreadsheet and translations are rows.
+ *
+ * @section save Save operations
+ * To update an existing entity, you will need to load it, change properties,
+ * and then save; as described above, when creating a new entity, you will also
+ * need to save it. Here is the order of hooks and other events that happen
+ * during an entity save:
+ * - preSave() is called on the entity object, and field objects.
+ * - hook_ENTITY_TYPE_presave()
+ * - hook_entity_presave()
+ * - Entity is saved to storage.
+ * - For updates on content entities, if there is a translation added that
+ *   was not previously present:
+ *   - hook_ENTITY_TYPE_translation_insert()
+ *   - hook_entity_translation_insert()
+ * - For updates on content entities, if there was a translation removed:
+ *   - hook_ENTITY_TYPE_translation_delete()
+ *   - hook_entity_translation_delete()
+ * - postSave() is called on the entity object.
+ * - hook_ENTITY_TYPE_insert() (new) or hook_ENTITY_TYPE_update() (update)
+ * - hook_entity_insert() (new) or hook_entity_update() (update)
+ *
+ * Some specific entity types invoke hooks during preSave() or postSave()
+ * operations. Examples:
+ * - Field configuration preSave(): hook_field_storage_config_update_forbid()
+ * - Node postSave(): hook_node_access_records() and
+ *   hook_node_access_records_alter()
+ * - Config entities that are acting as entity bundles in postSave():
+ *   hook_entity_bundle_create()
+ * - Comment: hook_comment_publish() and hook_comment_unpublish() as
+ *   appropriate.
+ *
+ * @section edit Editing operations
+ * When an entity's add/edit form is used to add or edit an entity, there
+ * are several hooks that are invoked:
+ * - hook_entity_prepare_form()
+ * - hook_ENTITY_TYPE_prepare_form()
+ * - hook_entity_form_display_alter() (for content entities only)
+ *
+ * @section delete Delete operations
+ * To delete one or more entities, load them and then delete them:
+ * @code
+ * $entities = $storage->loadMultiple($ids);
+ * $storage->delete($entities);
+ * @endcode
+ *
+ * During the delete operation, the following hooks and other events happen:
+ * - preDelete() is called on the entity class.
+ * - hook_ENTITY_TYPE_predelete()
+ * - hook_entity_predelete()
+ * - Entity and field information is removed from storage.
+ * - postDelete() is called on the entity class.
+ * - hook_ENTITY_TYPE_delete()
+ * - hook_entity_delete()
+ *
+ * Some specific entity types invoke hooks during the delete process. Examples:
+ * - Entity bundle postDelete(): hook_entity_bundle_delete()
+ *
+ * Individual revisions of an entity can also be deleted:
+ * @code
+ * $storage->deleteRevision($revision_id);
+ * @endcode
+ * This operation invokes the following operations and hooks:
+ * - Revision is loaded (see @ref load above).
+ * - Revision and field information is removed from the database.
+ * - hook_ENTITY_TYPE_revision_delete()
+ * - hook_entity_revision_delete()
+ *
+ * @section view View/render operations
+ * To make a render array for a loaded entity:
+ * @code
+ * // You can omit the language ID if the default language is being used.
+ * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
+ * @endcode
+ * You can also use the viewMultiple() method to view multiple entities.
+ *
+ * Hooks invoked during the operation of building a render array:
+ * - hook_entity_view_mode_alter()
+ * - hook_ENTITY_TYPE_build_defaults_alter()
+ * - hook_entity_build_defaults_alter()
+ *
+ * View builders for some types override these hooks, notably:
+ * - The Tour view builder does not invoke any hooks.
+ * - The Block view builder invokes hook_block_view_alter() and
+ *   hook_block_view_BASE_BLOCK_ID_alter(). Note that in other view builders,
+ *   the view alter hooks are run later in the process.
+ *
+ * During the rendering operation, the default entity viewer runs the following
+ * hooks and operations in the pre-render step:
+ * - hook_entity_view_display_alter()
+ * - hook_entity_prepare_view()
+ * - Entity fields are loaded, and render arrays are built for them using
+ *   their formatters.
+ * - hook_entity_display_build_alter()
+ * - hook_ENTITY_TYPE_view()
+ * - hook_entity_view()
+ * - hook_ENTITY_TYPE_view_alter()
+ * - hook_entity_view_alter()
+ *
+ * Some specific builders have specific hooks:
+ * - The Node view builder invokes hook_node_links_alter().
+ * - The Comment view builder invokes hook_comment_links_alter().
+ *
+ * After this point in rendering, the theme system takes over. See the
+ * @link theme_render Theme system and render API topic @endlink for more
+ * information.
+ *
+ * @section misc Other entity hooks
+ * Some types of entities invoke hooks for specific operations:
+ * - Searching nodes:
+ *   - hook_ranking()
+ *   - Query is executed to find matching nodes
+ *   - Resulting node is loaded
+ *   - Node render array is built
+ *   - comment_node_update_index() is called (this adds "N comments" text)
+ *   - hook_node_search_result()
+ * - Search indexing nodes:
+ *   - Node is loaded
+ *   - Node render array is built
+ *   - hook_node_update_index()
+ * @}
+ */
+
+/**
+ * @defgroup entity_api Entity API
+ * @{
+ * Describes how to define and manipulate content and configuration entities.
+ *
+ * Entities, in Drupal, are objects that are used for persistent storage of
+ * content and configuration information. See the
+ * @link info_types Information types topic @endlink for an overview of the
+ * different types of information, and the
+ * @link config_api Configuration API topic @endlink for more about the
+ * configuration API.
+ *
+ * Each entity is an instance of a particular "entity type". Some content entity
+ * types have sub-types, which are known as "bundles", while for other entity
+ * types, there is only a single bundle. For example, the Node content entity
+ * type, which is used for the main content pages in Drupal, has bundles that
+ * are known as "content types", while the User content type, which is used for
+ * user accounts, has only one bundle.
+ *
+ * The sections below have more information about entities and the Entity API;
+ * for more detailed information, see
+ * https://www.drupal.org/developing/api/entity.
+ *
+ * @section define Defining an entity type
+ * Entity types are defined by modules, using Drupal's Plugin API (see the
+ * @link plugin_api Plugin API topic @endlink for more information about plugins
+ * in general). Here are the steps to follow to define a new entity type:
+ * - Choose a unique machine name, or ID, for your entity type. This normally
+ *   starts with (or is the same as) your module's machine name. It should be
+ *   as short as possible, and may not exceed 32 characters.
+ * - Define an interface for your entity's get/set methods, usually extending
+ *   either \Drupal\Core\Config\Entity\ConfigEntityInterface or
+ *   \Drupal\Core\Entity\ContentEntityInterface.
+ * - Define a class for your entity, implementing your interface and extending
+ *   either \Drupal\Core\Config\Entity\ConfigEntityBase or
+ *   \Drupal\Core\Entity\ContentEntityBase, with annotation for
+ *   \@ConfigEntityType or \@ContentEntityType in its documentation block.
+ *   If you are defining a content entity type, it is recommended to extend the
+ *   \Drupal\Core\Entity\EditorialContentEntityBase base class in order to get
+ *   out-of-the-box support for Entity API's revisioning and publishing
+ *   features, which will allow your entity type to be used with Drupal's
+ *   editorial workflow provided by the Content Moderation module.
+ * - The 'id' annotation gives the entity type ID, and the 'label' annotation
+ *   gives the human-readable name of the entity type. If you are defining a
+ *   content entity type that uses bundles, the 'bundle_label' annotation gives
+ *   the human-readable name to use for a bundle of this entity type (for
+ *   example, "Content type" for the Node entity).
+ * - The annotation will refer to several handler classes, which you will also
+ *   need to define:
+ *   - list_builder: Define a class that extends
+ *     \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration
+ *     entities) or \Drupal\Core\Entity\EntityListBuilder (for content
+ *     entities), to provide an administrative overview for your entities.
+ *   - add and edit forms, or default form: Define a class (or two) that
+ *     extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms
+ *     for your entities. For content entities, base class
+ *     \Drupal\Core\Entity\ContentEntityForm is a better starting point.
+ *   - delete form: Define a class that extends
+ *     \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete
+ *     confirmation form for your entities.
+ *   - view_builder: For content entities and config entities that need to be
+ *     viewed, define a class that implements
+ *     \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
+ *     \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
+ *   - translation: For translatable content entities (if the 'translatable'
+ *     annotation has value TRUE), define a class that extends
+ *     \Drupal\content_translation\ContentTranslationHandler, to translate
+ *     the content. Configuration translation is handled automatically by the
+ *     Configuration Translation module, without the need of a handler class.
+ *   - access: If your configuration entity has complex permissions, you might
+ *     need an access control handling, implementing
+ *     \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities
+ *     can just use the 'admin_permission' annotation instead. Note that if you
+ *     are creating your own access control handler, you should override the
+ *     checkAccess() and checkCreateAccess() methods, not access().
+ *   - storage: A class implementing
+ *     \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
+ *     entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
+ *     config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage.
+ *     You can extend one of these classes to provide custom behavior.
+ *   - views_data: A class implementing \Drupal\views\EntityViewsDataInterface
+ *     to provide views data for the entity type. You can autogenerate most of
+ *     the views data by extending \Drupal\views\EntityViewsData.
+ * - For content entities, the annotation will refer to a number of database
+ *   tables and their fields. These annotation properties, such as 'base_table',
+ *   'data_table', 'entity_keys', etc., are documented on
+ *   \Drupal\Core\Entity\EntityType.
+ * - For content entities that are displayed on their own pages, the annotation
+ *   will refer to a 'uri_callback' function, which takes an object of the
+ *   entity interface you have defined as its parameter, and returns routing
+ *   information for the entity page; see node_uri() for an example. You will
+ *   also need to add a corresponding route to your module's routing.yml file;
+ *   see the entity.node.canonical route in node.routing.yml for an example, and see
+ *   @ref sec_routes below for some notes.
+ * - Optionally, instead of defining routes, routes can be auto generated by
+ *   providing a route handler. See @ref sec_routes. Otherwise, define routes
+ *   and links for the various URLs associated with the entity.
+ *   These go into the 'links' annotation, with the link type as the key, and
+ *   the path of this link template as the value. The corresponding route
+ *   requires the following route name:
+ *   "entity.$entity_type_id.$link_template_type". See @ref sec_routes below for
+ *   some routing notes. Typical link types are:
+ *   - canonical: Default link, either to view (if entities are viewed on their
+ *     own pages) or edit the entity.
+ *   - delete-form: Confirmation form to delete the entity.
+ *   - edit-form: Editing form.
+ *   - Other link types specific to your entity type can also be defined.
+ * - If your content entity is fieldable, provide 'field_ui_base_route'
+ *   annotation, giving the name of the route that the Manage Fields, Manage
+ *   Display, and Manage Form Display pages from the Field UI module will be
+ *   attached to. This is usually the bundle settings edit page, or an entity
+ *   type settings page if there are no bundles.
+ * - If your content entity has bundles, you will also need to define a second
+ *   plugin to handle the bundles. This plugin is itself a configuration entity
+ *   type, so follow the steps here to define it. The machine name ('id'
+ *   annotation) of this configuration entity class goes into the
+ *   'bundle_entity_type' annotation on the entity type class. For example, for
+ *   the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose
+ *   machine name is 'node_type'. This is the annotation value for
+ *   'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the
+ *   bundle config entity type annotation must have a 'bundle_of' entry,
+ *   giving the machine name of the entity type it is acting as a bundle for.
+ *   These machine names are considered permanent, they may not be renamed.
+ * - Additional annotations can be seen on entity class examples such as
+ *   \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
+ *   (configuration). These annotations are documented on
+ *   \Drupal\Core\Entity\EntityType.
+ *
+ * @section sec_routes Entity routes
+ * Entity routes can be defined in *.routing.yml files, like any other route:
+ * see the @link routing Routing API @endlink topic for more information.
+ * Another option for entity routes is to use a route provider class, and
+ * reference it in the annotations on the entity class: see the end of this
+ * section for an example.
+ *
+ * It's possible to use both a YAML file and a provider class for entity
+ * routes, at the same time. Avoid duplicating route names between the two:
+ * if a duplicate route name is found in both locations, the one in the YAML
+ * file takes precedence; regardless, such duplication can be confusing.
+ *
+ * Here's an example YAML route specification, for the block configure form:
+ * @code
+ * entity.block.edit_form:
+ *   path: '/admin/structure/block/manage/{block}'
+ *   defaults:
+ *     _entity_form: 'block.default'
+ *     _title: 'Configure block'
+ *   requirements:
+ *     _entity_access: 'block.update'
+ * @endcode
+ * Some notes on this example:
+ * - path: The {block} in the path is a placeholder, which (for an entity) must
+ *   always take the form of {machine_name_of_entity_type}. In the URL, the
+ *   placeholder value will be the ID of an entity item. When the route is used,
+ *   the entity system will load the corresponding entity item and pass it in as
+ *   an object to the controller for the route.
+ * - defaults: For entity form routes, use _entity_form rather than the generic
+ *   _controller or _form. The value is composed of the entity type machine name
+ *   and a form handler type from the entity annotation (see @ref define above
+ *   more more on handlers and annotation). So, in this example, block.default
+ *   refers to the 'default' form handler on the block entity type, whose
+ *   annotation contains:
+ *   @code
+ *   handlers = {
+ *     "form" = {
+ *       "default" = "Drupal\block\BlockForm",
+ *   @endcode
+ * If instead of YAML you want to use a route provider class:
+ * - \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical,
+ *   edit-form, and delete-form routes.
+ * - \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same
+ *   routes, set up to use the administrative theme for edit and delete pages.
+ * - You can also create your own class, extending one of these two classes if
+ *   you only want to modify their behaviour slightly.
+ *
+ * To register any route provider class, add lines like the following to your
+ * entity class annotation:
+ * @code
+ * handlers = {
+ *   "route_provider" = {
+ *     "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ * @endcode
+ *
+ * @section bundle Defining a content entity bundle
+ * For entity types that use bundles, such as Node (bundles are content types)
+ * and Taxonomy (bundles are vocabularies), modules and install profiles can
+ * define bundles by supplying default configuration in their config/install
+ * directories. (See the @link config_api Configuration API topic @endlink for
+ * general information about configuration.)
+ *
+ * There are several good examples of this in Drupal Core:
+ * - The Forum module defines a content type in node.type.forum.yml and a
+ *   vocabulary in taxonomy.vocabulary.forums.yml
+ * - The Book module defines a content type in node.type.book.yml
+ * - The Standard install profile defines Page and Article content types in
+ *   node.type.page.yml and node.type.article.yml, a Tags vocabulary in
+ *   taxonomy.vocabulary.tags.yml, and a Node comment type in
+ *   comment.type.comment.yml. This profile's configuration is especially
+ *   instructive, because it also adds several fields to the Article type, and
+ *   it sets up view and form display modes for the node types.
+ *
+ * @section load_query Loading, querying, and rendering entities
+ * To load entities, use the entity storage manager, which is an object
+ * implementing \Drupal\Core\Entity\EntityStorageInterface that you can
+ * retrieve with:
+ * @code
+ * $storage = \Drupal::entityManager()->getStorage('your_entity_type');
+ * // Or if you have a $container variable:
+ * $storage = $container->get('entity.manager')->getStorage('your_entity_type');
+ * @endcode
+ * Here, 'your_entity_type' is the machine name of your entity type ('id'
+ * annotation on the entity class), and note that you should use dependency
+ * injection to retrieve this object if possible. See the
+ * @link container Services and Dependency Injection topic @endlink for more
+ * about how to properly retrieve services.
+ *
+ * To query to find entities to load, use an entity query, which is a object
+ * implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve
+ * with:
+ * @code
+ * // Simple query:
+ * $query = \Drupal::entityQuery('your_entity_type');
+ * // Or, if you have a $container variable:
+ * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type');
+ * $query = $storage->getQuery();
+ * @endcode
+ * If you need aggregation, there is an aggregate query available, which
+ * implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
+ * @code
+ * $query \Drupal::entityQueryAggregate('your_entity_type');
+ * // Or:
+ * $query = $storage->getAggregateQuery('your_entity_type');
+ * @endcode
+ *
+ * In either case, you can then add conditions to your query, using methods
+ * like condition(), exists(), etc. on $query; add sorting, pager, and range
+ * if needed, and execute the query to return a list of entity IDs that match
+ * the query.
+ *
+ * Here is an example, using the core File entity:
+ * @code
+ * $fids = Drupal::entityQuery('file')
+ *   ->condition('status', FILE_STATUS_PERMANENT, '<>')
+ *   ->condition('changed', REQUEST_TIME - $age, '<')
+ *   ->range(0, 100)
+ *   ->execute();
+ * $files = $storage->loadMultiple($fids);
+ * @endcode
+ *
+ * The normal way of viewing entities is by using a route, as described in the
+ * sections above. If for some reason you need to render an entity in code in a
+ * particular view mode, you can use an entity view builder, which is an object
+ * implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can
+ * retrieve with:
+ * @code
+ * $view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type');
+ * // Or if you have a $container variable:
+ * $view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type');
+ * @endcode
+ * Then, to build and render the entity:
+ * @code
+ * // You can omit the language ID, by default the current content language will
+ * // be used. If no translation is available for the current language, fallback
+ * // rules will be used.
+ * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
+ * // $build is a render array.
+ * $rendered = drupal_render($build);
+ * @endcode
+ *
+ * @section sec_access Access checking on entities
+ * Entity types define their access permission scheme in their annotation.
+ * Access permissions can be quite complex, so you should not assume any
+ * particular permission scheme. Instead, once you have an entity object
+ * loaded, you can check for permission for a particular operation (such as
+ * 'view') at the entity or field level by calling:
+ * @code
+ * $entity->access($operation);
+ * $entity->nameOfField->access($operation);
+ * @endcode
+ * The interface related to access checking in entities and fields is
+ * \Drupal\Core\Access\AccessibleInterface.
+ *
+ * The default entity access control handler invokes two hooks while checking
+ * access on a single entity: hook_entity_access() is invoked first, and
+ * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name
+ * of the entity type). If no module returns a TRUE or FALSE value from
+ * either of these hooks, then the entity's default access checking takes
+ * place. For create operations (creating a new entity), the hooks that
+ * are invoked are hook_entity_create_access() and
+ * hook_ENTITY_TYPE_create_access() instead.
+ *
+ * The Node entity type has a complex system for determining access, which
+ * developers can interact with. This is described in the
+ * @link node_access Node access topic. @endlink
+ *
+ * @see i18n
+ * @see entity_crud
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getTranslationFromContext()
+ * @}
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Control entity operation access.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity to check access to.
+ * @param string $operation
+ *   The operation that is to be performed on $entity.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ *   The account trying to access the entity.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ *   The access result. The final result is calculated by using
+ *   \Drupal\Core\Access\AccessResultInterface::orIf() on the result of every
+ *   hook_entity_access() and hook_ENTITY_TYPE_access() implementation, and the
+ *   result of the entity-specific checkAccess() method in the entity access
+ *   control handler. Be careful when writing generalized access checks shared
+ *   between routing and entity checks: routing uses the andIf() operator. So
+ *   returning an isNeutral() does not determine entity access at all but it
+ *   always ends up denying access while routing.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessControlHandler
+ * @see hook_entity_create_access()
+ * @see hook_ENTITY_TYPE_access()
+ *
+ * @ingroup entity_api
+ */
+function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
+  // No opinion.
+  return AccessResult::neutral();
+}
+
+/**
+ * Control entity operation access for a specific entity type.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity to check access to.
+ * @param string $operation
+ *   The operation that is to be performed on $entity.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ *   The account trying to access the entity.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ *   The access result. hook_entity_access() has detailed documentation.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessControlHandler
+ * @see hook_ENTITY_TYPE_create_access()
+ * @see hook_entity_access()
+ *
+ * @ingroup entity_api
+ */
+function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
+  // No opinion.
+  return AccessResult::neutral();
+}
+
+/**
+ * Control entity create access.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ *   The account trying to access the entity.
+ * @param array $context
+ *   An associative array of additional context values. By default it contains
+ *   language and the entity type ID:
+ *   - entity_type_id - the entity type ID.
+ *   - langcode - the current language code.
+ * @param string $entity_bundle
+ *   The entity bundle name.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ *   The access result.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessControlHandler
+ * @see hook_entity_access()
+ * @see hook_ENTITY_TYPE_create_access()
+ *
+ * @ingroup entity_api
+ */
+function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
+  // No opinion.
+  return AccessResult::neutral();
+}
+
+/**
+ * Control entity create access for a specific entity type.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ *   The account trying to access the entity.
+ * @param array $context
+ *   An associative array of additional context values. By default it contains
+ *   language:
+ *   - langcode - the current language code.
+ * @param string $entity_bundle
+ *   The entity bundle name.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ *   The access result.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessControlHandler
+ * @see hook_ENTITY_TYPE_access()
+ * @see hook_entity_create_access()
+ *
+ * @ingroup entity_api
+ */
+function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
+  // No opinion.
+  return AccessResult::neutral();
+}
+
+/**
+ * Add to entity type definitions.
+ *
+ * Modules may implement this hook to add information to defined entity types,
+ * as defined in \Drupal\Core\Entity\EntityTypeInterface.
+ *
+ * To alter existing information or to add information dynamically, use
+ * hook_entity_type_alter().
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
+ *   An associative array of all entity type definitions, keyed by the entity
+ *   type name. Passed by reference.
+ *
+ * @see \Drupal\Core\Entity\Entity
+ * @see \Drupal\Core\Entity\EntityTypeInterface
+ * @see hook_entity_type_alter()
+ */
+function hook_entity_type_build(array &$entity_types) {
+  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
+  // Add a form for a custom node form without overriding the default
+  // node form. To override the default node form, use hook_entity_type_alter().
+  $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
+}
+
+/**
+ * Alter the entity type definitions.
+ *
+ * Modules may implement this hook to alter the information that defines an
+ * entity type. All properties that are available in
+ * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally
+ * provided by modules can be altered here.
+ *
+ * Do not use this hook to add information to entity types, unless one of the
+ * following is true:
+ * - You are filling in default values.
+ * - You need to dynamically add information only in certain circumstances.
+ * - Your hook needs to run after hook_entity_type_build() implementations.
+ * Use hook_entity_type_build() instead in all other cases.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
+ *   An associative array of all entity type definitions, keyed by the entity
+ *   type name. Passed by reference.
+ *
+ * @see \Drupal\Core\Entity\Entity
+ * @see \Drupal\Core\Entity\EntityTypeInterface
+ */
+function hook_entity_type_alter(array &$entity_types) {
+  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
+  // Set the controller class for nodes to an alternate implementation of the
+  // Drupal\Core\Entity\EntityStorageInterface interface.
+  $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
+}
+
+/**
+ * Alter the view modes for entity types.
+ *
+ * @param array $view_modes
+ *   An array of view modes, keyed first by entity type, then by view mode name.
+ *
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getAllViewModes()
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getViewModes()
+ */
+function hook_entity_view_mode_info_alter(&$view_modes) {
+  $view_modes['user']['full']['status'] = TRUE;
+}
+
+/**
+ * Describe the bundles for entity types.
+ *
+ * @return array
+ *   An associative array of all entity bundles, keyed by the entity
+ *   type name, and then the bundle name, with the following keys:
+ *   - label: The human-readable name of the bundle.
+ *   - uri_callback: The same as the 'uri_callback' key defined for the entity
+ *     type in the EntityManager, but for the bundle only. When determining
+ *     the URI of an entity, if a 'uri_callback' is defined for both the
+ *     entity type and the bundle, the one for the bundle is used.
+ *   - translatable: (optional) A boolean value specifying whether this bundle
+ *     has translation support enabled. Defaults to FALSE.
+ *
+ * @see \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
+ * @see hook_entity_bundle_info_alter()
+ */
+function hook_entity_bundle_info() {
+  $bundles['user']['user']['label'] = t('User');
+  return $bundles;
+}
+
+/**
+ * Alter the bundles for entity types.
+ *
+ * @param array $bundles
+ *   An array of bundles, keyed first by entity type, then by bundle name.
+ *
+ * @see Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
+ * @see hook_entity_bundle_info()
+ */
+function hook_entity_bundle_info_alter(&$bundles) {
+  $bundles['user']['user']['label'] = t('Full account');
+}
+
+/**
+ * Act on entity_bundle_create().
+ *
+ * This hook is invoked after the operation has been performed.
+ *
+ * @param string $entity_type_id
+ *   The type of $entity; e.g. 'node' or 'user'.
+ * @param string $bundle
+ *   The name of the bundle.
+ *
+ * @see entity_crud
+ */
+function hook_entity_bundle_create($entity_type_id, $bundle) {
+  // When a new bundle is created, the menu needs to be rebuilt to add the
+  // Field UI menu item tabs.
+  \Drupal::service('router.builder')->setRebuildNeeded();
+}
+
+/**
+ * Act on entity_bundle_delete().
+ *
+ * This hook is invoked after the operation has been performed.
+ *
+ * @param string $entity_type_id
+ *   The type of entity; for example, 'node' or 'user'.
+ * @param string $bundle
+ *   The bundle that was just deleted.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_bundle_delete($entity_type_id, $bundle) {
+  // Remove the settings associated with the bundle in my_module.settings.
+  $config = \Drupal::config('my_module.settings');
+  $bundle_settings = $config->get('bundle_settings');
+  if (isset($bundle_settings[$entity_type_id][$bundle])) {
+    unset($bundle_settings[$entity_type_id][$bundle]);
+    $config->set('bundle_settings', $bundle_settings);
+  }
+}
+
+/**
+ * Acts when creating a new entity.
+ *
+ * This hook runs after a new entity object has just been instantiated.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_create()
+ */
+function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
+  \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
+}
+
+/**
+ * Acts when creating a new entity of a specific type.
+ *
+ * This hook runs after a new entity object has just been instantiated.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_create()
+ */
+function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
+  \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
+}
+
+/**
+ * Act on entities when loaded.
+ *
+ * This is a generic load hook called for all entity types loaded via the
+ * entity API.
+ *
+ * hook_entity_storage_load() should be used to load additional data for
+ * content entities.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities keyed by entity ID.
+ * @param string $entity_type_id
+ *   The type of entities being loaded (i.e. node, user, comment).
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_load()
+ */
+function hook_entity_load(array $entities, $entity_type_id) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something($entity);
+  }
+}
+
+/**
+ * Act on entities of a specific type when loaded.
+ *
+ * @param array $entities
+ *   The entities keyed by entity ID.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_load()
+ */
+function hook_ENTITY_TYPE_load($entities) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something($entity);
+  }
+}
+
+/**
+ * Act on content entities when loaded from the storage.
+ *
+ * The results of this hook will be cached.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities keyed by entity ID.
+ * @param string $entity_type
+ *   The type of entities being loaded (i.e. node, user, comment).
+ *
+ * @see hook_entity_load()
+ */
+function hook_entity_storage_load(array $entities, $entity_type) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something_uncached($entity);
+  }
+}
+
+/**
+ * Act on content entities of a given type when loaded from the storage.
+ *
+ * The results of this hook will be cached if the entity type supports it.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities keyed by entity ID.
+ *
+ * @see hook_entity_storage_load()
+ */
+function hook_ENTITY_TYPE_storage_load(array $entities) {
+  foreach ($entities as $entity) {
+    $entity->foo = mymodule_add_something_uncached($entity);
+  }
+}
+
+/**
+ * Act on an entity before it is created or updated.
+ *
+ * You can get the original entity object from $entity->original when it is an
+ * update of the entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_presave()
+ */
+function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
+  if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
+    $route_match = \Drupal::routeMatch();
+    \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
+  }
+}
+
+/**
+ * Act on a specific type of entity before it is created or updated.
+ *
+ * You can get the original entity object from $entity->original when it is an
+ * update of the entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_presave()
+ */
+function hook_ENTITY_TYPE_presave(Drupal\Core\Entity\EntityInterface $entity) {
+  if ($entity->isTranslatable()) {
+    $route_match = \Drupal::routeMatch();
+    \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
+  }
+}
+
+/**
+ * Respond to creation of a new entity.
+ *
+ * This hook runs once the entity has been stored. Note that hook
+ * implementations may not alter the stored entity data.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_insert()
+ */
+function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
+  // Insert the new entity into a fictional table of all entities.
+  db_insert('example_entity')
+    ->fields([
+      'type' => $entity->getEntityTypeId(),
+      'id' => $entity->id(),
+      'created' => REQUEST_TIME,
+      'updated' => REQUEST_TIME,
+    ])
+    ->execute();
+}
+
+/**
+ * Respond to creation of a new entity of a particular type.
+ *
+ * This hook runs once the entity has been stored. Note that hook
+ * implementations may not alter the stored entity data.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_insert()
+ */
+function hook_ENTITY_TYPE_insert(Drupal\Core\Entity\EntityInterface $entity) {
+  // Insert the new entity into a fictional table of this type of entity.
+  db_insert('example_entity')
+    ->fields([
+      'id' => $entity->id(),
+      'created' => REQUEST_TIME,
+      'updated' => REQUEST_TIME,
+    ])
+    ->execute();
+}
+
+/**
+ * Respond to updates to an entity.
+ *
+ * This hook runs once the entity storage has been updated. Note that hook
+ * implementations may not alter the stored entity data. Get the original entity
+ * object from $entity->original.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_update()
+ */
+function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
+  // Update the entity's entry in a fictional table of all entities.
+  db_update('example_entity')
+    ->fields([
+      'updated' => REQUEST_TIME,
+    ])
+    ->condition('type', $entity->getEntityTypeId())
+    ->condition('id', $entity->id())
+    ->execute();
+}
+
+/**
+ * Respond to updates to an entity of a particular type.
+ *
+ * This hook runs once the entity storage has been updated. Note that hook
+ * implementations may not alter the stored entity data. Get the original entity
+ * object from $entity->original.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_update()
+ */
+function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
+  // Update the entity's entry in a fictional table of this type of entity.
+  db_update('example_entity')
+    ->fields([
+      'updated' => REQUEST_TIME,
+    ])
+    ->condition('id', $entity->id())
+    ->execute();
+}
+
+/**
+ * Acts when creating a new entity translation.
+ *
+ * This hook runs after a new entity translation object has just been
+ * instantiated.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_translation_create()
+ */
+function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
+  \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
+}
+
+/**
+ * Acts when creating a new entity translation of a specific type.
+ *
+ * This hook runs after a new entity translation object has just been
+ * instantiated.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_translation_create()
+ */
+function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
+  \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
+}
+
+/**
+ * Respond to creation of a new entity translation.
+ *
+ * This hook runs once the entity translation has been stored. Note that hook
+ * implementations may not alter the stored entity translation data.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The entity object of the translation just stored.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_translation_insert()
+ */
+function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
+  $variables = [
+    '@language' => $translation->language()->getName(),
+    '@label' => $translation->getUntranslated()->label(),
+  ];
+  \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
+}
+
+/**
+ * Respond to creation of a new entity translation of a particular type.
+ *
+ * This hook runs once the entity translation has been stored. Note that hook
+ * implementations may not alter the stored entity translation data.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The entity object of the translation just stored.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_translation_insert()
+ */
+function hook_ENTITY_TYPE_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
+  $variables = [
+    '@language' => $translation->language()->getName(),
+    '@label' => $translation->getUntranslated()->label(),
+  ];
+  \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
+}
+
+/**
+ * Respond to entity translation deletion.
+ *
+ * This hook runs once the entity translation has been deleted from storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The original entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_translation_delete()
+ */
+function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
+  $variables = [
+    '@language' => $translation->language()->getName(),
+    '@label' => $translation->label(),
+  ];
+  \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
+}
+
+/**
+ * Respond to entity translation deletion of a particular type.
+ *
+ * This hook runs once the entity translation has been deleted from storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $translation
+ *   The original entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_translation_delete()
+ */
+function hook_ENTITY_TYPE_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
+  $variables = [
+    '@language' => $translation->language()->getName(),
+    '@label' => $translation->label(),
+  ];
+  \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
+}
+
+/**
+ * Act before entity deletion.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity that is about to be deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_predelete()
+ */
+function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
+  // Count references to this entity in a custom table before they are removed
+  // upon entity deletion.
+  $id = $entity->id();
+  $type = $entity->getEntityTypeId();
+  $count = db_select('example_entity_data')
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+
+  // Log the count in a table that records this statistic for deleted entities.
+  db_merge('example_deleted_entity_statistics')
+    ->key(['type' => $type, 'id' => $id])
+    ->fields(['count' => $count])
+    ->execute();
+}
+
+/**
+ * Act before entity deletion of a particular entity type.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity that is about to be deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_predelete()
+ */
+function hook_ENTITY_TYPE_predelete(Drupal\Core\Entity\EntityInterface $entity) {
+  // Count references to this entity in a custom table before they are removed
+  // upon entity deletion.
+  $id = $entity->id();
+  $type = $entity->getEntityTypeId();
+  $count = db_select('example_entity_data')
+    ->condition('type', $type)
+    ->condition('id', $id)
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+
+  // Log the count in a table that records this statistic for deleted entities.
+  db_merge('example_deleted_entity_statistics')
+    ->key(['type' => $type, 'id' => $id])
+    ->fields(['count' => $count])
+    ->execute();
+}
+
+/**
+ * Respond to entity deletion.
+ *
+ * This hook runs once the entity has been deleted from the storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity that has been deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_delete()
+ */
+function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
+  // Delete the entity's entry from a fictional table of all entities.
+  db_delete('example_entity')
+    ->condition('type', $entity->getEntityTypeId())
+    ->condition('id', $entity->id())
+    ->execute();
+}
+
+/**
+ * Respond to entity deletion of a particular type.
+ *
+ * This hook runs once the entity has been deleted from the storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity that has been deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_delete()
+ */
+function hook_ENTITY_TYPE_delete(Drupal\Core\Entity\EntityInterface $entity) {
+  // Delete the entity's entry from a fictional table of all entities.
+  db_delete('example_entity')
+    ->condition('type', $entity->getEntityTypeId())
+    ->condition('id', $entity->id())
+    ->execute();
+}
+
+/**
+ * Respond to entity revision deletion.
+ *
+ * This hook runs once the entity revision has been deleted from the storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity revision that has been deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_revision_delete()
+ */
+function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
+  $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
+  foreach ($referenced_files_by_field as $field => $uuids) {
+    _editor_delete_file_usage($uuids, $entity, 1);
+  }
+}
+
+/**
+ * Respond to entity revision deletion of a particular type.
+ *
+ * This hook runs once the entity revision has been deleted from the storage.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object for the entity revision that has been deleted.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_revision_delete()
+ */
+function hook_ENTITY_TYPE_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
+  $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
+  foreach ($referenced_files_by_field as $field => $uuids) {
+    _editor_delete_file_usage($uuids, $entity, 1);
+  }
+}
+
+/**
+ * Act on entities being assembled before rendering.
+ *
+ * @param &$build
+ *   A renderable array representing the entity content. The module may add
+ *   elements to $build prior to rendering. The structure of $build is a
+ *   renderable array as expected by drupal_render().
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ *   The entity view display holding the display options configured for the
+ *   entity components.
+ * @param $view_mode
+ *   The view mode the entity is rendered in.
+ *
+ * @see hook_entity_view_alter()
+ * @see hook_ENTITY_TYPE_view()
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
+  // Only do the extra work if the component is configured to be displayed.
+  // This assumes a 'mymodule_addition' extra field has been defined for the
+  // entity bundle in hook_entity_extra_field_info().
+  if ($display->getComponent('mymodule_addition')) {
+    $build['mymodule_addition'] = [
+      '#markup' => mymodule_addition($entity),
+      '#theme' => 'mymodule_my_additional_field',
+    ];
+  }
+}
+
+/**
+ * Act on entities of a particular type being assembled before rendering.
+ *
+ * @param &$build
+ *   A renderable array representing the entity content. The module may add
+ *   elements to $build prior to rendering. The structure of $build is a
+ *   renderable array as expected by drupal_render().
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ *   The entity view display holding the display options configured for the
+ *   entity components.
+ * @param $view_mode
+ *   The view mode the entity is rendered in.
+ *
+ * @see hook_ENTITY_TYPE_view_alter()
+ * @see hook_entity_view()
+ *
+ * @ingroup entity_crud
+ */
+function hook_ENTITY_TYPE_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
+  // Only do the extra work if the component is configured to be displayed.
+  // This assumes a 'mymodule_addition' extra field has been defined for the
+  // entity bundle in hook_entity_extra_field_info().
+  if ($display->getComponent('mymodule_addition')) {
+    $build['mymodule_addition'] = [
+      '#markup' => mymodule_addition($entity),
+      '#theme' => 'mymodule_my_additional_field',
+    ];
+  }
+}
+
+/**
+ * Alter the results of the entity build array.
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * entity content structure has been built.
+ *
+ * If a module wishes to act on the rendered HTML of the entity rather than the
+ * structured content array, it may use this hook to add a #post_render
+ * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
+ * the particular entity type template, if there is one (e.g., node.html.twig).
+ *
+ * See the @link themeable Default theme implementations topic @endlink and
+ * drupal_render() for details.
+ *
+ * @param array &$build
+ *   A renderable array representing the entity content.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object being rendered.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ *   The entity view display holding the display options configured for the
+ *   entity components.
+ *
+ * @ingroup entity_crud
+ *
+ * @see hook_entity_view()
+ * @see hook_ENTITY_TYPE_view_alter()
+ */
+function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
+  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+
+    // Add a #post_render callback to act on the rendered HTML of the entity.
+    $build['#post_render'][] = 'my_module_node_post_render';
+  }
+}
+
+/**
+ * Alter the results of the entity build array for a particular entity type.
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * entity content structure has been built.
+ *
+ * If a module wishes to act on the rendered HTML of the entity rather than the
+ * structured content array, it may use this hook to add a #post_render
+ * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
+ * the particular entity type template, if there is one (e.g., node.html.twig).
+ *
+ * See the @link themeable Default theme implementations topic @endlink and
+ * drupal_render() for details.
+ *
+ * @param array &$build
+ *   A renderable array representing the entity content.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object being rendered.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ *   The entity view display holding the display options configured for the
+ *   entity components.
+ *
+ * @ingroup entity_crud
+ *
+ * @see hook_ENTITY_TYPE_view()
+ * @see hook_entity_view_alter()
+ */
+function hook_ENTITY_TYPE_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
+  if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+
+    // Add a #post_render callback to act on the rendered HTML of the entity.
+    $build['#post_render'][] = 'my_module_node_post_render';
+  }
+}
+
+/**
+ * Act on entities as they are being prepared for view.
+ *
+ * Allows you to operate on multiple entities as they are being prepared for
+ * view. Only use this if attaching the data during the entity loading phase
+ * is not appropriate, for example when attaching other 'entity' style objects.
+ *
+ * @param string $entity_type_id
+ *   The type of entities being viewed (i.e. node, user, comment).
+ * @param array $entities
+ *   The entities keyed by entity ID.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
+ *   The array of entity view displays holding the display options configured
+ *   for the entity components, keyed by bundle name.
+ * @param string $view_mode
+ *   The view mode.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
+  // Load a specific node into the user object for later theming.
+  if (!empty($entities) && $entity_type_id == 'user') {
+    // Only do the extra work if the component is configured to be
+    // displayed. This assumes a 'mymodule_addition' extra field has been
+    // defined for the entity bundle in hook_entity_extra_field_info().
+    $ids = [];
+    foreach ($entities as $id => $entity) {
+      if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) {
+        $ids[] = $id;
+      }
+    }
+    if ($ids) {
+      $nodes = mymodule_get_user_nodes($ids);
+      foreach ($ids as $id) {
+        $entities[$id]->user_node = $nodes[$id];
+      }
+    }
+  }
+}
+
+/**
+ * Change the view mode of an entity that is being displayed.
+ *
+ * @param string $view_mode
+ *   The view_mode that is to be used to display the entity.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity that is being viewed.
+ * @param array $context
+ *   Array with additional context information, currently only contains the
+ *   langcode the entity is viewed in.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
+  // For nodes, change the view mode when it is teaser.
+  if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
+    $view_mode = 'my_custom_view_mode';
+  }
+}
+
+/**
+ * Alter entity renderable values before cache checking in drupal_render().
+ *
+ * Invoked for a specific entity type.
+ *
+ * The values in the #cache key of the renderable array are used to determine if
+ * a cache entry exists for the entity's rendered output. Ideally only values
+ * that pertain to caching should be altered in this hook.
+ *
+ * @param array &$build
+ *   A renderable array containing the entity's caching and view mode values.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity that is being viewed.
+ * @param string $view_mode
+ *   The view_mode that is to be used to display the entity.
+ *
+ * @see drupal_render()
+ * @see \Drupal\Core\Entity\EntityViewBuilder
+ * @see hook_entity_build_defaults_alter()
+ *
+ * @ingroup entity_crud
+ */
+function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
+
+}
+
+/**
+ * Alter entity renderable values before cache checking in drupal_render().
+ *
+ * The values in the #cache key of the renderable array are used to determine if
+ * a cache entry exists for the entity's rendered output. Ideally only values
+ * that pertain to caching should be altered in this hook.
+ *
+ * @param array &$build
+ *   A renderable array containing the entity's caching and view mode values.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity that is being viewed.
+ * @param string $view_mode
+ *   The view_mode that is to be used to display the entity.
+ *
+ * @see drupal_render()
+ * @see \Drupal\Core\Entity\EntityViewBuilder
+ * @see hook_ENTITY_TYPE_build_defaults_alter()
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
+
+}
+
+/**
+ * Alter the settings used for displaying an entity.
+ *
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ *   The entity view display that will be used to display the entity
+ *   components.
+ * @param array $context
+ *   An associative array containing:
+ *   - entity_type: The entity type, e.g., 'node' or 'user'.
+ *   - bundle: The bundle, e.g., 'page' or 'article'.
+ *   - view_mode: The view mode, e.g., 'full', 'teaser', etc.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
+  // Leave field labels out of the search index.
+  if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
+    foreach ($display->getComponents() as $name => $options) {
+      if (isset($options['label'])) {
+        $options['label'] = 'hidden';
+        $display->setComponent($name, $options);
+      }
+    }
+  }
+}
+
+/**
+ * Alter the render array generated by an EntityDisplay for an entity.
+ *
+ * @param array $build
+ *   The renderable array generated by the EntityDisplay.
+ * @param array $context
+ *   An associative array containing:
+ *   - entity: The entity being rendered.
+ *   - view_mode: The view mode; for example, 'full' or 'teaser'.
+ *   - display: The EntityDisplay holding the display options.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_display_build_alter(&$build, $context) {
+  // Append RDF term mappings on displayed taxonomy links.
+  foreach (Element::children($build) as $field_name) {
+    $element = &$build[$field_name];
+    if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') {
+      foreach ($element['#items'] as $delta => $item) {
+        $term = $item->entity;
+        if (!empty($term->rdf_mapping['rdftype'])) {
+          $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
+        }
+        if (!empty($term->rdf_mapping['name']['predicates'])) {
+          $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Acts on an entity object about to be shown on an entity form.
+ *
+ * This can be typically used to pre-fill entity values or change the form state
+ * before the entity form is built. It is invoked just once when first building
+ * the entity form. Rebuilds will not trigger a new invocation.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity that is about to be shown on the form.
+ * @param $operation
+ *   The current operation.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   The current state of the form.
+ *
+ * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
+ * @see hook_ENTITY_TYPE_prepare_form()
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
+  if ($operation == 'edit') {
+    $entity->label->value = 'Altered label';
+    $form_state->set('label_altered', TRUE);
+  }
+}
+
+/**
+ * Acts on a particular type of entity object about to be in an entity form.
+ *
+ * This can be typically used to pre-fill entity values or change the form state
+ * before the entity form is built. It is invoked just once when first building
+ * the entity form. Rebuilds will not trigger a new invocation.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity that is about to be shown on the form.
+ * @param $operation
+ *   The current operation.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   The current state of the form.
+ *
+ * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
+ * @see hook_entity_prepare_form()
+ *
+ * @ingroup entity_crud
+ */
+function hook_ENTITY_TYPE_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
+  if ($operation == 'edit') {
+    $entity->label->value = 'Altered label';
+    $form_state->set('label_altered', TRUE);
+  }
+}
+
+/**
+ * Alter the settings used for displaying an entity form.
+ *
+ * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
+ *   The entity_form_display object that will be used to display the entity form
+ *   components.
+ * @param array $context
+ *   An associative array containing:
+ *   - entity_type: The entity type, e.g., 'node' or 'user'.
+ *   - bundle: The bundle, e.g., 'page' or 'article'.
+ *   - form_mode: The form mode; e.g., 'default', 'profile', 'register', etc.
+ *
+ * @ingroup entity_crud
+ */
+function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
+  // Hide the 'user_picture' field from the register form.
+  if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
+    $form_display->setComponent('user_picture', [
+      'region' => 'hidden',
+    ]);
+  }
+}
+
+/**
+ * Provides custom base field definitions for a content entity type.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ *
+ * @return \Drupal\Core\Field\FieldDefinitionInterface[]
+ *   An array of field definitions, keyed by field name.
+ *
+ * @see hook_entity_base_field_info_alter()
+ * @see hook_entity_bundle_field_info()
+ * @see hook_entity_bundle_field_info_alter()
+ * @see \Drupal\Core\Field\FieldDefinitionInterface
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
+ */
+function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
+  if ($entity_type->id() == 'node') {
+    $fields = [];
+    $fields['mymodule_text'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('The text'))
+      ->setDescription(t('A text property added by mymodule.'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\mymodule\EntityComputedText');
+
+    return $fields;
+  }
+}
+
+/**
+ * Alter base field definitions for a content entity type.
+ *
+ * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
+ *   The array of base field definitions for the entity type.
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ *
+ * @see hook_entity_base_field_info()
+ * @see hook_entity_bundle_field_info()
+ * @see hook_entity_bundle_field_info_alter()
+ *
+ * @todo WARNING: This hook will be changed in
+ * https://www.drupal.org/node/2346329.
+ */
+function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
+  // Alter the mymodule_text field to use a custom class.
+  if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
+    $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
+  }
+}
+
+/**
+ * Provides field definitions for a specific bundle within an entity type.
+ *
+ * Bundle fields either have to override an existing base field, or need to
+ * provide a field storage definition via hook_entity_field_storage_info()
+ * unless they are computed.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ * @param string $bundle
+ *   The bundle.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
+ *   The list of base field definitions for the entity type.
+ *
+ * @return \Drupal\Core\Field\FieldDefinitionInterface[]
+ *   An array of bundle field definitions, keyed by field name.
+ *
+ * @see hook_entity_base_field_info()
+ * @see hook_entity_base_field_info_alter()
+ * @see hook_entity_field_storage_info()
+ * @see hook_entity_field_storage_info_alter()
+ * @see hook_entity_bundle_field_info_alter()
+ * @see \Drupal\Core\Field\FieldDefinitionInterface
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
+ *
+ * @todo WARNING: This hook will be changed in
+ * https://www.drupal.org/node/2346347.
+ */
+function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
+  // Add a property only to nodes of the 'article' bundle.
+  if ($entity_type->id() == 'node' && $bundle == 'article') {
+    $fields = [];
+    $fields['mymodule_text_more'] = BaseFieldDefinition::create('string')
+      ->setLabel(t('More text'))
+      ->setComputed(TRUE)
+      ->setClass('\Drupal\mymodule\EntityComputedMoreText');
+    return $fields;
+  }
+}
+
+/**
+ * Alter bundle field definitions.
+ *
+ * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
+ *   The array of bundle field definitions.
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ * @param string $bundle
+ *   The bundle.
+ *
+ * @see hook_entity_base_field_info()
+ * @see hook_entity_base_field_info_alter()
+ * @see hook_entity_bundle_field_info()
+ *
+ * @todo WARNING: This hook will be changed in
+ * https://www.drupal.org/node/2346347.
+ */
+function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
+  if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
+    // Alter the mymodule_text field to use a custom class.
+    $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
+  }
+}
+
+/**
+ * Provides field storage definitions for a content entity type.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ *
+ * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+ *   An array of field storage definitions, keyed by field name.
+ *
+ * @see hook_entity_field_storage_info_alter()
+ * @see \Drupal\Core\Field\FieldStorageDefinitionInterface
+ * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldStorageDefinitions()
+ */
+function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
+  if (\Drupal::entityManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) {
+    // Query by filtering on the ID as this is more efficient than filtering
+    // on the entity_type property directly.
+    $ids = \Drupal::entityQuery('field_storage_config')
+      ->condition('id', $entity_type->id() . '.', 'STARTS_WITH')
+      ->execute();
+    // Fetch all fields and key them by field name.
+    $field_storages = FieldStorageConfig::loadMultiple($ids);
+    $result = [];
+    foreach ($field_storages as $field_storage) {
+      $result[$field_storage->getName()] = $field_storage;
+    }
+
+    return $result;
+  }
+}
+
+/**
+ * Alter field storage definitions for a content entity type.
+ *
+ * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields
+ *   The array of field storage definitions for the entity type.
+ * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+ *   The entity type definition.
+ *
+ * @see hook_entity_field_storage_info()
+ */
+function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
+  // Alter the max_length setting.
+  if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
+    $fields['mymodule_text']->setSetting('max_length', 128);
+  }
+}
+
+/**
+ * Declares entity operations.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity on which the linked operations will be performed.
+ *
+ * @return array
+ *   An operations array as returned by
+ *   EntityListBuilderInterface::getOperations().
+ *
+ * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
+ */
+function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
+  $operations = [];
+  $operations['translate'] = [
+    'title' => t('Translate'),
+    'url' => \Drupal\Core\Url::fromRoute('foo_module.entity.translate'),
+    'weight' => 50,
+  ];
+
+  return $operations;
+}
+
+/**
+ * Alter entity operations.
+ *
+ * @param array $operations
+ *   Operations array as returned by
+ *   \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity on which the linked operations will be performed.
+ */
+function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) {
+  // Alter the title and weight.
+  $operations['translate']['title'] = t('Translate @entity_type', [
+    '@entity_type' => $entity->getEntityTypeId(),
+  ]);
+  $operations['translate']['weight'] = 99;
+}
+
+/**
+ * Control access to fields.
+ *
+ * This hook is invoked from
+ * \Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess() to let modules
+ * grant or deny operations on fields.
+ *
+ * @param string $operation
+ *   The operation to be performed. See
+ *   \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
+ *   for possible values.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ *   The field definition.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ *   The user account to check.
+ * @param \Drupal\Core\Field\FieldItemListInterface $items
+ *   (optional) The entity field object on which the operation is to be
+ *   performed.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ *   The access result.
+ *
+ * @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
+ */
+function hook_entity_field_access($operation, \Drupal\Core\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Field\FieldItemListInterface $items = NULL) {
+  if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
+    return AccessResult::allowedIfHasPermission($account, 'update field of interest');
+  }
+  return AccessResult::neutral();
+}
+
+/**
+ * Alter the default access behavior for a given field.
+ *
+ * Use this hook to override access grants from another module. Note that the
+ * original default access flag is masked under the ':default' key.
+ *
+ * @param \Drupal\Core\Access\AccessResultInterface[] $grants
+ *   An array of grants gathered by hook_entity_field_access(). The array is
+ *   keyed by the module that defines the field's access control; the values are
+ *   grant responses for each module (\Drupal\Core\Access\AccessResult).
+ * @param array $context
+ *   Context array on the performed operation with the following keys:
+ *   - operation: The operation to be performed (string).
+ *   - field_definition: The field definition object
+ *     (\Drupal\Core\Field\FieldDefinitionInterface)
+ *   - account: The user account to check access for
+ *     (Drupal\user\Entity\User).
+ *   - items: (optional) The entity field items
+ *     (\Drupal\Core\Field\FieldItemListInterface).
+ */
+function hook_entity_field_access_alter(array &$grants, array $context) {
+  /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
+  $field_definition = $context['field_definition'];
+  if ($field_definition->getName() == 'field_of_interest' && $grants['node']->isForbidden()) {
+    // Override node module's restriction to no opinion (neither allowed nor
+    // forbidden). We don't want to provide our own access hook, we only want to
+    // take out node module's part in the access handling of this field. We also
+    // don't want to switch node module's grant to
+    // AccessResultInterface::isAllowed() , because the grants of other modules
+    // should still decide on their own if this field is accessible or not
+    $grants['node'] = AccessResult::neutral()->inheritCacheability($grants['node']);
+  }
+}
+
+/**
+ * Acts when initializing a fieldable entity object.
+ *
+ * This hook runs after a new entity object or a new entity translation object
+ * has just been instantiated. It can be used to set initial values, e.g. to
+ * provide defaults.
+ *
+ * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_ENTITY_TYPE_field_values_init()
+ */
+function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
+  if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
+    $entity->foo->value = 'some_initial_value';
+  }
+}
+
+/**
+ * Acts when initializing a fieldable entity object.
+ *
+ * This hook runs after a new entity object or a new entity translation object
+ * has just been instantiated. It can be used to set initial values, e.g. to
+ * provide defaults.
+ *
+ * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+ *   The entity object.
+ *
+ * @ingroup entity_crud
+ * @see hook_entity_field_values_init()
+ */
+function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
+  if (!$entity->foo->value) {
+    $entity->foo->value = 'some_initial_value';
+  }
+}
+
+/**
+ * Exposes "pseudo-field" components on content entities.
+ *
+ * Field UI's "Manage fields" and "Manage display" pages let users re-order
+ * fields, but also non-field components. For nodes, these include elements
+ * exposed by modules through hook_form_alter(), for instance.
+ *
+ * Content entities or modules that want to have their components supported
+ * should expose them using this hook. The user-defined settings (weight,
+ * visible) are automatically applied when entities or entity forms are
+ * rendered.
+ *
+ * @see hook_entity_extra_field_info_alter()
+ *
+ * @return array
+ *   The array structure is identical to that of the return value of
+ *   \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields().
+ */
+function hook_entity_extra_field_info() {
+  $extra = [];
+  $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
+  $description = t('Node module element');
+
+  foreach (NodeType::loadMultiple() as $bundle) {
+
+    // Add also the 'language' select if Language module is enabled and the
+    // bundle has multilingual support.
+    // Visibility of the ordering of the language selector is the same as on the
+    // node/add form.
+    if ($module_language_enabled) {
+      $configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
+      if ($configuration->isLanguageAlterable()) {
+        $extra['node'][$bundle->id()]['form']['language'] = [
+          'label' => t('Language'),
+          'description' => $description,
+          'weight' => 0,
+        ];
+      }
+    }
+    $extra['node'][$bundle->id()]['display']['language'] = [
+      'label' => t('Language'),
+      'description' => $description,
+      'weight' => 0,
+      'visible' => FALSE,
+    ];
+  }
+
+  return $extra;
+}
+
+/**
+ * Alter "pseudo-field" components on content entities.
+ *
+ * @param array $info
+ *   The array structure is identical to that of the return value of
+ *   \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
+ *
+ * @see hook_entity_extra_field_info()
+ */
+function hook_entity_extra_field_info_alter(&$info) {
+  // Force node title to always be at the top of the list by default.
+  foreach (NodeType::loadMultiple() as $bundle) {
+    if (isset($info['node'][$bundle->id()]['form']['title'])) {
+      $info['node'][$bundle->id()]['form']['title']['weight'] = -20;
+    }
+  }
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */