diff --git a/core/modules/block/block.api.php b/core/modules/block/block.api.php index a40b74e..f43d984 100644 --- a/core/modules/block/block.api.php +++ b/core/modules/block/block.api.php @@ -85,9 +85,9 @@ * The block plugin instance. * * @see hook_block_view_BASE_BLOCK_ID_alter() - * @ingroup block_api + * @see entity_crud * - * @ingroup entity_crud + * @ingroup block_api */ function hook_block_view_alter(array &$build, \Drupal\block\BlockPluginInterface $block) { // Remove the contextual links on all blocks that provide them. @@ -115,9 +115,9 @@ function hook_block_view_alter(array &$build, \Drupal\block\BlockPluginInterface * The block plugin instance. * * @see hook_block_view_alter() - * @ingroup block_api + * @see entity_crud * - * @ingroup entity_crud + * @ingroup block_api */ function hook_block_view_BASE_BLOCK_ID_alter(array &$build, \Drupal\block\BlockPluginInterface $block) { // Change the title of the specific block. diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index c860945..6cde557 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -255,7 +255,7 @@ function hook_field_info_max_weight($entity_type, $bundle, $context, $context_mo * @param \Drupal\field\FieldConfigInterface $prior_field * The field as it is pre-update. * - * @ingroup entity_crud + * @see entity_crud */ function hook_field_config_update_forbid(\Drupal\field\FieldConfigInterface $field, \Drupal\field\FieldConfigInterface $prior_field) { // A 'list' field stores integer keys mapped to display values. If diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 45a02bf..247c239 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -401,7 +401,6 @@ function hook_node_update_index(\Drupal\node\NodeInterface $node, $langcode) { return $text; } - /** * Perform node validation before a node is created or updated. * @@ -537,8 +536,7 @@ function hook_ranking() { * * @see \Drupal\node\NodeViewBuilder::renderLinks() * @see \Drupal\node\NodeViewBuilder::buildLinks() - * - * @ingroup entity_crud + * @see entity_crud */ function hook_node_links_alter(array &$links, NodeInterface $entity, array &$context) { $links['mymodule'] = array( diff --git a/core/modules/node/src/Tests/NodeCreationTest.php b/core/modules/node/src/Tests/NodeCreationTest.php index dfde883..2687907 100644 --- a/core/modules/node/src/Tests/NodeCreationTest.php +++ b/core/modules/node/src/Tests/NodeCreationTest.php @@ -19,7 +19,7 @@ class NodeCreationTest extends NodeTestBase { * Modules to enable. * * Enable dummy module that implements hook_ENTITY_TYPE_insert() for - * exceptions. + * exceptions (function node_test_exception_node_insert() ). * * @var array */ diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php index 16f1434..135cd4a 100644 --- a/core/modules/system/core.api.php +++ b/core/modules/system/core.api.php @@ -292,7 +292,8 @@ * - delete form: Define a class that extends * \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete * confirmation form for your entities. - * - view_builder: For content entities, define a class that implements + * - view_builder: For content entities and config entities that need to be + * viewed, define a class that implements * \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending * \Drupal\Core\Entity\EntityViewBuilder), to display a single entity. * - translation: For translatable content entities (if the 'translatable' @@ -303,7 +304,9 @@ * - access: If your configuration entity has complex permissions, you might * need an access controller, implementing * \Drupal\Core\Entity\EntityAccessControllerInterface, but most entities - * can just use the 'admin_permission' annotation instead. + * can just use the 'admin_permission' annotation instead. Note that if you + * are creating your own access controller, you should override the + * checkAccess() and checkCreateAccess() methods, not access(). * - storage: A class implementing * \Drupal\Core\Entity\EntityStorageInterface. If not specified, content * entities will use \Drupal\Core\Entity\ContentEntityDatabaseStorage, and @@ -339,16 +342,18 @@ * 'bundle_entity_type' annotation on the entity type class. For example, for * the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose * machine name is 'node_type'. This is the annotation value for - * 'bundle_entity_type' on the \Drupal\node\Entity\Node class. + * 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the + * bundle config entity type annotation must have a 'bundle_of' entry, + * giving the machine name of the entity type it is acting as a bundle for. * - Additional annotations can be seen on entity class examples such as * \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role * (configuration). These annotations are documented on * \Drupal\Core\Entity\EntityType. * * @section bundle Defining a content entity bundle - * For content entity types that use bundles, such as Node (bundles are content - * types) and Taxonomy (bundles are vocabularies), modules and install profiles - * can define bundles by supplying default configuration in their config/install + * For entity types that use bundles, such as Node (bundles are content types) + * and Taxonomy (bundles are vocabularies), modules and install profiles can + * define bundles by supplying default configuration in their config/install * directories. (See the @link config_api Configuration API topic @endlink for * general information about configuration.) * @@ -417,21 +422,23 @@ * object implementing \Drupal\Core\Entity\EntityViewBuilderInteface that * you can retrive with: * @code - * $viewer = \Drupal::entityManager()->getViewBuilder('your_entity_type'); + * $view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type'); * // Or if you have a $container variable: - * $viewer = $container->get('entity.manager')->getViewBuilder('your_entity_type'); + * $view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type'); * @endcode * - * Once you have a loaded entity and a viewer, you can render the entity - * in a particular view mode by calling: + * Once you have a loaded entity and a viewer, if for some reason you need to + * render an entity in code in a particular view mode, you can call: * @code - * // If the entity supports translation, load the translation. - * $entity = $entity->getTranslation($language->id); - * $build = $viewer->view($entity, 'view_mode_name', $language->id); + * // You can omit the language ID if the default language is being used. + * $build = $view_builder->view($entity, 'view_mode_name', $language->id); * // $build is a render array. * $rendered = drupal_render($build); * @endcode * + * The more normal way of viewing entities is by using a route, and a view + * builder, as described in the sections above. + * * @section sec_access Access checking on entities * Entity types define their access permission scheme in their annotation. * Access permissions can be quite complex, so you should not assume any @@ -448,12 +455,17 @@ * The default entity access controller invokes two hooks while checking * access on a single entity: hook_entity_access() is invoked first, and * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name - * of the entity type). - * - * The Node entity type has a complex permission system, which developers can - * interact with. This is described in the + * of the entity type). If no module returns a TRUE or FALSE value from + * either of these hooks, then the entity's default access checking takes + * place. For create operations (creating a new entity), the hooks that + * are invoked are hook_entity_create_access() and + * hook_ENTITY_TYPE_create_access() instead. + * + * The Node entity type has a complex system for determining access, which + * developers can interact with. This is described in the * @link node_access Node access topic. @endlink * + * If you are overriding * @see i18n * @see entity_crud * @} diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index c3e6669..1bab346 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -16,25 +16,27 @@ * Hooks used in various entity operations. * * Entity create, read, update, and delete (CRUD) operations are performed by - * entity storage controllers; see the + * entity storage classes; see the * @link entity_api Entity API topic @endlink for more information. Most - * entities use or extend the default storage controllers: + * entities use or extend the default classes: * \Drupal\Core\Entity\ContentEntityDatabaseStorage for content entities, and * \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities. * For these entities, there is a set of hooks that is invoked for each * CRUD operation, which module developers can implement to affect these * operations; these hooks are actually invoked from methods on - * \Drupal\Core\Entity\EntityStorageBase(). + * \Drupal\Core\Entity\EntityStorageBase. * * For content entities, viewing and rendering are handled by a view builder - * controller; see the @link entity_api Entity API topic @endlink for more + * class; see the @link entity_api Entity API topic @endlink for more * information. Most view builders extend or use the default class * \Drupal\Core\Entity\EntityViewBuilder. * * Entity editing (including adding new entities) is handled by entity form - * controllers; see the @link entity_api Entity API topic @endlink for more + * classes; see the @link entity_api Entity API topic @endlink for more * information. Most entity editing forms extend base classes * \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm. + * Note that many other operations, such as confirming deletion of entities, + * also use entity form classes. * * This topic lists all of the entity CRUD and view operations, and the hooks * and other operations that are invoked (in order) for each operation. Some @@ -42,17 +44,23 @@ * - Whenever an entity hook is invoked, there is both a type-specific entity * hook, and a generic entity hook. For instance, during a create operation on * a node, first hook_node_create() and then hook_entity_create() would be - * invoked. The entity-type-specific hooks are represented in the list below - * as hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To + * invoked. + * - The entity-type-specific hooks are represented in the list below as + * hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To * implement one of these hooks for an entity whose machine name is "foo", - * define a function called mymodule_foo_create(), for instance. + * define a function called mymodule_foo_create(), for instance. Also note + * that the entity or array of entities that are passed into a specific-type + * hook are of the specific entity class, not the generic Entity class, so in + * your implementation, you can make the $entity argument something like $node + * and give it a specific type hint (which should normally be to the specific + * interface, such as \Drupal\Node\NodeInterface for nodes). * - $storage in the code examples is assumed to be an entity storage - * controller. See the @link entity_api Entity API topic @endlink for - * information on how to instantiate the correct storage controller for an + * class. See the @link entity_api Entity API topic @endlink for + * information on how to instantiate the correct storage class for an * entity type. - * - $viewer in the code examples is assumed to be an entity view builder - * controller. See the @link entity_api Entity API topic @endlink for - * information on how to instantiate the correct view builder controller for + * - $view_builder in the code examples is assumed to be an entity view builder + * class. See the @link entity_api Entity API topic @endlink for + * information on how to instantiate the correct view builder class for * an entity type. * - During many operations, static methods are called on the entity class, * which implements \Drupal\Entity\EntityInterface. @@ -61,9 +69,14 @@ * To create an entity: * @code * $entity = $storage->create(); - * // Set up the entity properly. + * + * // Add code here to set properties on the entity. + * + * // Until you call save(), the entity is just in memory. * $entity->save(); * @endcode + * There is also a shortcut method on entity classes, which creates an entity + * with an array of provided property values: \Drupal\Core\Entity::create(). * * Hooks invoked during the create operation: * - hook_ENTITY_TYPE_create() @@ -83,23 +96,29 @@ * Since load() calls loadMultiple(), these are really the same operation. * Here is the order of hooks and other operations that take place during * entity loading: - * - Entity and fields are loaded from storage. + * - Entity is loaded from storage. * - postLoad() is called on the entity class, passing in all of the loaded * entities. * - hook_entity_load() * - hook_ENTITY_TYPE_load() * - * @section update Update operations - * To update an entity, load it (see @ref load), modify its values, and - * save it (see @ref save). + * When an entity is loaded, normally the default entity revision is loaded. + * It is also possible to load a different revision, for entities that support + * revisions, with this code: + * @code + * $entity = $storage->loadRevision($revision_id); + * @endcode + * This involves the same hooks and operations as regular entity loading. * * @section save Save operations - * Both create and update operations invoke an entity save. Here is the order - * of hooks and other events that happen during an entity save: - * - preSave() is called on the entity object. + * To update an existing entity, you will need to load it, change properties, + * and then save; as described above, when creating a new entity, you will also + * need to save it. Here is the order of hooks and other events that happen + * during an entity save: + * - preSave() is called on the entity object, and field objects. * - hook_ENTITY_TYPE_presave() * - hook_entity_presave() - * - The base entity information and fields are saved to storage. + * - Entity is saved to storage. * - For updates on content entities, if there is a translation added that * was not previously present: * - hook_ENTITY_TYPE_translation_insert() @@ -116,8 +135,8 @@ * - Field configuration preSave(): hook_field_config_update_forbid() * - Node postSave(): hook_node_access_records() and * hook_node_access_records_alter() - * - Entity bundle postSave(): hook_entity_bundle_create() or - * hook_entity_bundle_rename() as appropriate + * - Config entities that are acting as entity bundles, in postSave(): + * hook_entity_bundle_create() or hook_entity_bundle_rename() as appropriate * - Comment: hook_comment_publish() and hook_comment_unpublish() as * appropriate. * @@ -126,7 +145,7 @@ * are several hooks that are invoked: * - hook_entity_prepare_form() * - hook_ENTITY_TYPE_prepare_form() - * - hook_entity_form_display_alter() + * - hook_entity_form_display_alter() (for content entities only) * * Some specific entity types have additional hooks that are run during * various steps in the process: @@ -151,17 +170,12 @@ * Some specific entity types invoke hooks during the delete process. Examples: * - Entity bundle postDelete(): hook_entity_bundle_delete() * - * Some entities have different methods of deleting. For instance, user - * accounts can be canceled, which involves more than just deleting a user - * entity. This process invokes hook_user_cancel(). - * * Individual revisions of an entity can also be deleted: * @code * $storage->deleteRevision($revision_id); * @endcode - * * This operation invokes the following operations and hooks: - * - Revision is loaded. + * - Revision is loaded (see @ref load above). * - Revision and field information is removed from the database. * - hook_ENTITY_TYPE_revision_delete() * - hook_entity_revision_delete() @@ -169,9 +183,8 @@ * @section view View/render operations * To make a render array for a loaded entity: * @code - * // If the entity supports translation, load the translation. - * $entity = $entity->getTranslation($language->id); - * $build = $viewer->view($entity, 'view_mode_name', $language->id); + * // You can omit the language ID if the default language is being used. + * $build = $view_builder->view($entity, 'view_mode_name', $language->id); * @endcode * You can also use the viewMultiple() method to view multiple entities. * @@ -244,6 +257,8 @@ * deny access. * * @see \Drupal\Core\Entity\EntityAccessController + * @see hook_entity_create_access() + * @see hook_ENTITY_TYPE_access() * * @ingroup entity_api */ @@ -268,6 +283,8 @@ function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operat * deny access. * * @see \Drupal\Core\Entity\EntityAccessController + * @see hook_ENTITY_TYPE_create_access() + * @see hook_entity_access() * * @ingroup entity_api */ @@ -288,6 +305,10 @@ function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $o * deny access. * * @see \Drupal\Core\Entity\EntityAccessController + * @see hook_entity_access() + * @see hook_ENTITY_TYPE_create_access() + * + * @ingroup entity_api */ function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) { return NULL; @@ -306,6 +327,10 @@ function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $accoun * deny access. * * @see \Drupal\Core\Entity\EntityAccessController + * @see hook_ENTITY_TYPE_access() + * @see hook_entity_create_access() + * + * @ingroup entity_api */ function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, $langcode) { return NULL; @@ -415,7 +440,7 @@ function hook_entity_bundle_info_alter(&$bundles) { * @param string $bundle * The name of the bundle. * - * @ingroup entity_crud + * @see entity_crud */ function hook_entity_bundle_create($entity_type_id, $bundle) { // When a new bundle is created, the menu needs to be rebuilt to add the @@ -435,7 +460,7 @@ function hook_entity_bundle_create($entity_type_id, $bundle) { * @param string $bundle_new * The new name of the bundle. * - * @ingroup entity_crud + * @see entity_crud */ function hook_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) { // Update the settings associated with the bundle in my_module.settings. @@ -551,7 +576,10 @@ function hook_ENTITY_TYPE_load($entities) { * @see hook_ENTITY_TYPE_presave() */ function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) { - $entity->changed = REQUEST_TIME; + if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) { + $attributes = \Drupal::request()->attributes; + \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->id, $attributes->get('source_langcode')); + } } /** @@ -564,7 +592,10 @@ function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) { * @see hook_entity_presave() */ function hook_ENTITY_TYPE_presave(Drupal\Core\Entity\EntityInterface $entity) { - $entity->changed = REQUEST_TIME; + if ($entity->isTranslatable()) { + $attributes = \Drupal::request()->attributes; + \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->id, $attributes->get('source_langcode')); + } } /** diff --git a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php index 80154ea..80684d3 100644 --- a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php @@ -15,13 +15,11 @@ * Tests invocation of hooks when performing an action. * * Tested hooks are: - * - hook_entity_insert() - * - hook_entity_load() - * - hook_entity_update() - * - hook_entity_predelete() - * - hook_entity_delete() - * Also tested are type-specific hooks, like hook_ENTITY_TYPE_insert(), - * hook_ENTITY_TYPE_update(), etc. + * - hook_entity_insert() and hook_ENTITY_TYPE_insert() + * - hook_entity_load() and hook_ENTITY_TYPE_load() + * - hook_entity_update() and hook_ENTITY_TYPE_update() + * - hook_entity_predelete() and hook_ENTITY_TYPE_predelete() + * - hook_entity_delete() and hook_ENTITY_TYPE_delete() * * These hooks are each tested for several entity types. */ diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php index cbac697..cca3525 100644 --- a/core/modules/user/user.api.php +++ b/core/modules/user/user.api.php @@ -23,7 +23,7 @@ * * This hook is NOT invoked for the 'user_cancel_delete' account cancellation * method. To react to that method, implement hook_ENTITY_TYPE_predelete() or - * hook_ENTITY_TYPE_delete() instead. + * hook_ENTITY_TYPE_delete() for user entities instead. * * Expensive operations should be added to the global account cancellation batch * by using batch_set().