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