annotate core/lib/Drupal/Core/Entity/entity.api.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /**
Chris@0 4 * @file
Chris@0 5 * Hooks and documentation related to entities.
Chris@0 6 */
Chris@0 7
Chris@0 8 use Drupal\Core\Access\AccessResult;
Chris@0 9 use Drupal\Core\Entity\ContentEntityInterface;
Chris@0 10 use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface;
Chris@0 11 use Drupal\Core\Field\BaseFieldDefinition;
Chris@18 12 use Drupal\Core\Field\FieldDefinition;
Chris@0 13 use Drupal\Core\Render\Element;
Chris@0 14 use Drupal\language\Entity\ContentLanguageSettings;
Chris@0 15 use Drupal\node\Entity\NodeType;
Chris@0 16
Chris@0 17 /**
Chris@0 18 * @defgroup entity_crud Entity CRUD, editing, and view hooks
Chris@0 19 * @{
Chris@0 20 * Hooks used in various entity operations.
Chris@0 21 *
Chris@0 22 * Entity create, read, update, and delete (CRUD) operations are performed by
Chris@0 23 * entity storage classes; see the
Chris@0 24 * @link entity_api Entity API topic @endlink for more information. Most
Chris@0 25 * entities use or extend the default classes:
Chris@0 26 * \Drupal\Core\Entity\Sql\SqlContentEntityStorage for content entities, and
Chris@0 27 * \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities.
Chris@0 28 * For these entities, there is a set of hooks that is invoked for each
Chris@0 29 * CRUD operation, which module developers can implement to affect these
Chris@0 30 * operations; these hooks are actually invoked from methods on
Chris@0 31 * \Drupal\Core\Entity\EntityStorageBase.
Chris@0 32 *
Chris@0 33 * For content entities, viewing and rendering are handled by a view builder
Chris@0 34 * class; see the @link entity_api Entity API topic @endlink for more
Chris@0 35 * information. Most view builders extend or use the default class
Chris@0 36 * \Drupal\Core\Entity\EntityViewBuilder.
Chris@0 37 *
Chris@0 38 * Entity editing (including adding new entities) is handled by entity form
Chris@0 39 * classes; see the @link entity_api Entity API topic @endlink for more
Chris@0 40 * information. Most entity editing forms extend base classes
Chris@0 41 * \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm.
Chris@0 42 * Note that many other operations, such as confirming deletion of entities,
Chris@0 43 * also use entity form classes.
Chris@0 44 *
Chris@0 45 * This topic lists all of the entity CRUD and view operations, and the hooks
Chris@0 46 * and other operations that are invoked (in order) for each operation. Some
Chris@0 47 * notes:
Chris@0 48 * - Whenever an entity hook is invoked, there is both a type-specific entity
Chris@0 49 * hook, and a generic entity hook. For instance, during a create operation on
Chris@0 50 * a node, first hook_node_create() and then hook_entity_create() would be
Chris@0 51 * invoked.
Chris@0 52 * - The entity-type-specific hooks are represented in the list below as
Chris@0 53 * hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To
Chris@0 54 * implement one of these hooks for an entity whose machine name is "foo",
Chris@0 55 * define a function called mymodule_foo_create(), for instance. Also note
Chris@0 56 * that the entity or array of entities that are passed into a specific-type
Chris@0 57 * hook are of the specific entity class, not the generic Entity class, so in
Chris@0 58 * your implementation, you can make the $entity argument something like $node
Chris@0 59 * and give it a specific type hint (which should normally be to the specific
Chris@0 60 * interface, such as \Drupal\Node\NodeInterface for nodes).
Chris@0 61 * - $storage in the code examples is assumed to be an entity storage
Chris@0 62 * class. See the @link entity_api Entity API topic @endlink for
Chris@0 63 * information on how to instantiate the correct storage class for an
Chris@0 64 * entity type.
Chris@0 65 * - $view_builder in the code examples is assumed to be an entity view builder
Chris@0 66 * class. See the @link entity_api Entity API topic @endlink for
Chris@0 67 * information on how to instantiate the correct view builder class for
Chris@0 68 * an entity type.
Chris@0 69 * - During many operations, static methods are called on the entity class,
Chris@0 70 * which implements \Drupal\Entity\EntityInterface.
Chris@0 71 *
Chris@14 72 * @section entities_revisions_translations Entities, revisions and translations
Chris@14 73 * A content entity can have multiple stored variants: based on its definition,
Chris@14 74 * it can be revisionable, translatable, or both.
Chris@14 75 *
Chris@14 76 * A revisionable entity can keep track of the changes that affect its data. In
Chris@14 77 * fact all previous revisions of the entity can be stored and made available as
Chris@14 78 * "historical" information. The "default" revision is the canonical variant of
Chris@14 79 * the entity, the one that is loaded when no specific revision is requested.
Chris@14 80 * Only changes to the default revision may be performed without triggering the
Chris@14 81 * creation of a new revision, in any other case revision data is not supposed
Chris@14 82 * to change. Aside from historical revisions, there can be "pending" revisions,
Chris@14 83 * that contain changes that did not make their way into the default revision.
Chris@14 84 * Typically these revisions contain data that is waiting for some form of
Chris@14 85 * approval, before being accepted as canonical.
Chris@14 86 * @see \Drupal\Core\Entity\RevisionableInterface
Chris@14 87 * @see \Drupal\Core\Entity\RevisionableStorageInterface
Chris@14 88 *
Chris@14 89 * A translatable entity can contain multiple translations of the same content.
Chris@14 90 * Content entity data is stored via fields, and each field can have one version
Chris@14 91 * for each enabled language. Some fields may be defined as untranslatable,
Chris@14 92 * which means that their values are shared among all translations. The
Chris@14 93 * "default" translation is the canonical variant of the entity, the one whose
Chris@14 94 * content will be accessible in the entity field data. Other translations
Chris@14 95 * can be instantiated from the default one. Every translation has an "active
Chris@14 96 * language" that is used to determine which field translation values should be
Chris@14 97 * handled. Typically the default translation's active language is the language
Chris@14 98 * of the content that was originally entered and served as source for the other
Chris@14 99 * translations.
Chris@14 100 * @see \Drupal\Core\Entity\TranslatableInterface
Chris@14 101 * @see \Drupal\Core\Entity\TranslatableStorageInterface
Chris@14 102 *
Chris@14 103 * An entity that is both revisionable and translatable has all the features
Chris@14 104 * described above: every revision can contain one or more translations. The
Chris@14 105 * canonical variant of the entity is the default translation of the default
Chris@14 106 * revision. Any revision will be initially loaded as the default translation,
Chris@14 107 * the other revision translations can be instantiated from this one. If a
Chris@14 108 * translation has changes in a certain revision, the translation is considered
Chris@14 109 * "affected" by that revision, and will be flagged as such via the
Chris@14 110 * "revision_translation_affected" field. With the built-in UI, every time a new
Chris@14 111 * revision is saved, the changes for the edited translations will be stored,
Chris@14 112 * while all field values for the other translations will be copied as-is.
Chris@14 113 * However, if multiple translations of the default revision are being
Chris@14 114 * subsequently modified without creating a new revision when saving, they will
Chris@14 115 * all be affected by the default revision. Additionally, all revision
Chris@14 116 * translations will be affected when saving a revision containing changes for
Chris@14 117 * untranslatable fields. On the other hand, pending revisions are not supposed
Chris@14 118 * to contain multiple affected translations, even when they are being
Chris@14 119 * manipulated via the API.
Chris@14 120 * @see \Drupal\Core\Entity\TranslatableRevisionableInterface
Chris@14 121 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface
Chris@14 122 *
Chris@0 123 * @section create Create operations
Chris@0 124 * To create an entity:
Chris@0 125 * @code
Chris@0 126 * $entity = $storage->create();
Chris@0 127 *
Chris@0 128 * // Add code here to set properties on the entity.
Chris@0 129 *
Chris@0 130 * // Until you call save(), the entity is just in memory.
Chris@0 131 * $entity->save();
Chris@0 132 * @endcode
Chris@0 133 * There is also a shortcut method on entity classes, which creates an entity
Chris@0 134 * with an array of provided property values: \Drupal\Core\Entity::create().
Chris@0 135 *
Chris@0 136 * Hooks invoked during the create operation:
Chris@0 137 * - hook_ENTITY_TYPE_create()
Chris@0 138 * - hook_entity_create()
Chris@14 139 * - When handling content entities, if a new translation is added to the entity
Chris@14 140 * object:
Chris@14 141 * - hook_ENTITY_TYPE_translation_create()
Chris@14 142 * - hook_entity_translation_create()
Chris@0 143 *
Chris@0 144 * See @ref save below for the save portion of the operation.
Chris@0 145 *
Chris@0 146 * @section load Read/Load operations
Chris@0 147 * To load (read) a single entity:
Chris@0 148 * @code
Chris@0 149 * $entity = $storage->load($id);
Chris@0 150 * @endcode
Chris@0 151 * To load multiple entities:
Chris@0 152 * @code
Chris@0 153 * $entities = $storage->loadMultiple($ids);
Chris@0 154 * @endcode
Chris@0 155 * Since load() calls loadMultiple(), these are really the same operation.
Chris@0 156 * Here is the order of hooks and other operations that take place during
Chris@0 157 * entity loading:
Chris@0 158 * - Entity is loaded from storage.
Chris@0 159 * - postLoad() is called on the entity class, passing in all of the loaded
Chris@0 160 * entities.
Chris@0 161 * - hook_entity_load()
Chris@0 162 * - hook_ENTITY_TYPE_load()
Chris@0 163 *
Chris@0 164 * When an entity is loaded, normally the default entity revision is loaded.
Chris@0 165 * It is also possible to load a different revision, for entities that support
Chris@0 166 * revisions, with this code:
Chris@0 167 * @code
Chris@0 168 * $entity = $storage->loadRevision($revision_id);
Chris@0 169 * @endcode
Chris@0 170 * This involves the same hooks and operations as regular entity loading.
Chris@0 171 *
Chris@14 172 * The "latest revision" of an entity is the most recently created one,
Chris@14 173 * regardless of it being default or pending. If the entity is translatable,
Chris@14 174 * revision translations are not taken into account either. In other words, any
Chris@14 175 * time a new revision is created, that becomes the latest revision for the
Chris@14 176 * entity overall, regardless of the affected translations. To load the latest
Chris@14 177 * revision of an entity:
Chris@14 178 * @code
Chris@14 179 * $revision_id = $storage->getLatestRevisionId($entity_id);
Chris@14 180 * $entity = $storage->loadRevision($revision_id);
Chris@14 181 * @endcode
Chris@14 182 * As usual, if the entity is translatable, this code instantiates into $entity
Chris@14 183 * the default translation of the revision, even if the latest revision contains
Chris@14 184 * only changes to a different translation:
Chris@14 185 * @code
Chris@14 186 * $is_default = $entity->isDefaultTranslation(); // returns TRUE
Chris@14 187 * @endcode
Chris@0 188 *
Chris@14 189 * The "latest translation-affected revision" is the most recently created one
Chris@14 190 * that affects the specified translation. For example, when a new revision
Chris@14 191 * introducing some changes to an English translation is saved, that becomes the
Chris@14 192 * new "latest revision". However, if an existing Italian translation was not
Chris@14 193 * affected by those changes, then the "latest translation-affected revision"
Chris@14 194 * for Italian remains what it was. To load the Italian translation at its
Chris@14 195 * latest translation-affected revision:
Chris@14 196 * @code
Chris@14 197 * $revision_id = $storage->getLatestTranslationAffectedRevisionId($entity_id, 'it');
Chris@14 198 * $it_translation = $storage->loadRevision($revision_id)->getTranslation('it');
Chris@14 199 * @endcode
Chris@0 200 *
Chris@0 201 * @section save Save operations
Chris@0 202 * To update an existing entity, you will need to load it, change properties,
Chris@0 203 * and then save; as described above, when creating a new entity, you will also
Chris@0 204 * need to save it. Here is the order of hooks and other events that happen
Chris@0 205 * during an entity save:
Chris@0 206 * - preSave() is called on the entity object, and field objects.
Chris@0 207 * - hook_ENTITY_TYPE_presave()
Chris@0 208 * - hook_entity_presave()
Chris@0 209 * - Entity is saved to storage.
Chris@0 210 * - For updates on content entities, if there is a translation added that
Chris@0 211 * was not previously present:
Chris@0 212 * - hook_ENTITY_TYPE_translation_insert()
Chris@0 213 * - hook_entity_translation_insert()
Chris@0 214 * - For updates on content entities, if there was a translation removed:
Chris@0 215 * - hook_ENTITY_TYPE_translation_delete()
Chris@0 216 * - hook_entity_translation_delete()
Chris@0 217 * - postSave() is called on the entity object.
Chris@0 218 * - hook_ENTITY_TYPE_insert() (new) or hook_ENTITY_TYPE_update() (update)
Chris@0 219 * - hook_entity_insert() (new) or hook_entity_update() (update)
Chris@0 220 *
Chris@0 221 * Some specific entity types invoke hooks during preSave() or postSave()
Chris@0 222 * operations. Examples:
Chris@0 223 * - Field configuration preSave(): hook_field_storage_config_update_forbid()
Chris@0 224 * - Node postSave(): hook_node_access_records() and
Chris@0 225 * hook_node_access_records_alter()
Chris@0 226 * - Config entities that are acting as entity bundles in postSave():
Chris@0 227 * hook_entity_bundle_create()
Chris@0 228 * - Comment: hook_comment_publish() and hook_comment_unpublish() as
Chris@0 229 * appropriate.
Chris@0 230 *
Chris@14 231 * Note that all translations available for the entity are stored during a save
Chris@14 232 * operation. When saving a new revision, a copy of every translation is stored,
Chris@14 233 * regardless of it being affected by the revision.
Chris@14 234 *
Chris@0 235 * @section edit Editing operations
Chris@0 236 * When an entity's add/edit form is used to add or edit an entity, there
Chris@0 237 * are several hooks that are invoked:
Chris@0 238 * - hook_entity_prepare_form()
Chris@0 239 * - hook_ENTITY_TYPE_prepare_form()
Chris@0 240 * - hook_entity_form_display_alter() (for content entities only)
Chris@0 241 *
Chris@0 242 * @section delete Delete operations
Chris@0 243 * To delete one or more entities, load them and then delete them:
Chris@0 244 * @code
Chris@0 245 * $entities = $storage->loadMultiple($ids);
Chris@0 246 * $storage->delete($entities);
Chris@0 247 * @endcode
Chris@0 248 *
Chris@0 249 * During the delete operation, the following hooks and other events happen:
Chris@0 250 * - preDelete() is called on the entity class.
Chris@0 251 * - hook_ENTITY_TYPE_predelete()
Chris@0 252 * - hook_entity_predelete()
Chris@0 253 * - Entity and field information is removed from storage.
Chris@0 254 * - postDelete() is called on the entity class.
Chris@0 255 * - hook_ENTITY_TYPE_delete()
Chris@0 256 * - hook_entity_delete()
Chris@0 257 *
Chris@0 258 * Some specific entity types invoke hooks during the delete process. Examples:
Chris@0 259 * - Entity bundle postDelete(): hook_entity_bundle_delete()
Chris@0 260 *
Chris@0 261 * Individual revisions of an entity can also be deleted:
Chris@0 262 * @code
Chris@0 263 * $storage->deleteRevision($revision_id);
Chris@0 264 * @endcode
Chris@0 265 * This operation invokes the following operations and hooks:
Chris@0 266 * - Revision is loaded (see @ref load above).
Chris@0 267 * - Revision and field information is removed from the database.
Chris@0 268 * - hook_ENTITY_TYPE_revision_delete()
Chris@0 269 * - hook_entity_revision_delete()
Chris@0 270 *
Chris@0 271 * @section view View/render operations
Chris@0 272 * To make a render array for a loaded entity:
Chris@0 273 * @code
Chris@0 274 * // You can omit the language ID if the default language is being used.
Chris@0 275 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
Chris@0 276 * @endcode
Chris@0 277 * You can also use the viewMultiple() method to view multiple entities.
Chris@0 278 *
Chris@0 279 * Hooks invoked during the operation of building a render array:
Chris@0 280 * - hook_entity_view_mode_alter()
Chris@0 281 * - hook_ENTITY_TYPE_build_defaults_alter()
Chris@0 282 * - hook_entity_build_defaults_alter()
Chris@0 283 *
Chris@0 284 * View builders for some types override these hooks, notably:
Chris@0 285 * - The Tour view builder does not invoke any hooks.
Chris@0 286 * - The Block view builder invokes hook_block_view_alter() and
Chris@0 287 * hook_block_view_BASE_BLOCK_ID_alter(). Note that in other view builders,
Chris@0 288 * the view alter hooks are run later in the process.
Chris@0 289 *
Chris@0 290 * During the rendering operation, the default entity viewer runs the following
Chris@0 291 * hooks and operations in the pre-render step:
Chris@0 292 * - hook_entity_view_display_alter()
Chris@0 293 * - hook_entity_prepare_view()
Chris@0 294 * - Entity fields are loaded, and render arrays are built for them using
Chris@0 295 * their formatters.
Chris@0 296 * - hook_entity_display_build_alter()
Chris@0 297 * - hook_ENTITY_TYPE_view()
Chris@0 298 * - hook_entity_view()
Chris@0 299 * - hook_ENTITY_TYPE_view_alter()
Chris@0 300 * - hook_entity_view_alter()
Chris@0 301 *
Chris@0 302 * Some specific builders have specific hooks:
Chris@0 303 * - The Node view builder invokes hook_node_links_alter().
Chris@0 304 * - The Comment view builder invokes hook_comment_links_alter().
Chris@0 305 *
Chris@0 306 * After this point in rendering, the theme system takes over. See the
Chris@0 307 * @link theme_render Theme system and render API topic @endlink for more
Chris@0 308 * information.
Chris@0 309 *
Chris@0 310 * @section misc Other entity hooks
Chris@0 311 * Some types of entities invoke hooks for specific operations:
Chris@0 312 * - Searching nodes:
Chris@0 313 * - hook_ranking()
Chris@0 314 * - Query is executed to find matching nodes
Chris@0 315 * - Resulting node is loaded
Chris@0 316 * - Node render array is built
Chris@0 317 * - comment_node_update_index() is called (this adds "N comments" text)
Chris@0 318 * - hook_node_search_result()
Chris@0 319 * - Search indexing nodes:
Chris@0 320 * - Node is loaded
Chris@0 321 * - Node render array is built
Chris@0 322 * - hook_node_update_index()
Chris@0 323 * @}
Chris@0 324 */
Chris@0 325
Chris@0 326 /**
Chris@0 327 * @defgroup entity_api Entity API
Chris@0 328 * @{
Chris@0 329 * Describes how to define and manipulate content and configuration entities.
Chris@0 330 *
Chris@0 331 * Entities, in Drupal, are objects that are used for persistent storage of
Chris@0 332 * content and configuration information. See the
Chris@0 333 * @link info_types Information types topic @endlink for an overview of the
Chris@0 334 * different types of information, and the
Chris@0 335 * @link config_api Configuration API topic @endlink for more about the
Chris@0 336 * configuration API.
Chris@0 337 *
Chris@0 338 * Each entity is an instance of a particular "entity type". Some content entity
Chris@0 339 * types have sub-types, which are known as "bundles", while for other entity
Chris@0 340 * types, there is only a single bundle. For example, the Node content entity
Chris@0 341 * type, which is used for the main content pages in Drupal, has bundles that
Chris@0 342 * are known as "content types", while the User content type, which is used for
Chris@0 343 * user accounts, has only one bundle.
Chris@0 344 *
Chris@0 345 * The sections below have more information about entities and the Entity API;
Chris@0 346 * for more detailed information, see
Chris@0 347 * https://www.drupal.org/developing/api/entity.
Chris@0 348 *
Chris@0 349 * @section define Defining an entity type
Chris@0 350 * Entity types are defined by modules, using Drupal's Plugin API (see the
Chris@0 351 * @link plugin_api Plugin API topic @endlink for more information about plugins
Chris@0 352 * in general). Here are the steps to follow to define a new entity type:
Chris@0 353 * - Choose a unique machine name, or ID, for your entity type. This normally
Chris@0 354 * starts with (or is the same as) your module's machine name. It should be
Chris@0 355 * as short as possible, and may not exceed 32 characters.
Chris@0 356 * - Define an interface for your entity's get/set methods, usually extending
Chris@0 357 * either \Drupal\Core\Config\Entity\ConfigEntityInterface or
Chris@0 358 * \Drupal\Core\Entity\ContentEntityInterface.
Chris@0 359 * - Define a class for your entity, implementing your interface and extending
Chris@0 360 * either \Drupal\Core\Config\Entity\ConfigEntityBase or
Chris@0 361 * \Drupal\Core\Entity\ContentEntityBase, with annotation for
Chris@0 362 * \@ConfigEntityType or \@ContentEntityType in its documentation block.
Chris@0 363 * If you are defining a content entity type, it is recommended to extend the
Chris@0 364 * \Drupal\Core\Entity\EditorialContentEntityBase base class in order to get
Chris@0 365 * out-of-the-box support for Entity API's revisioning and publishing
Chris@0 366 * features, which will allow your entity type to be used with Drupal's
Chris@0 367 * editorial workflow provided by the Content Moderation module.
Chris@12 368 * - In the annotation, the 'id' property gives the entity type ID, and the
Chris@12 369 * 'label' property gives the human-readable name of the entity type. If you
Chris@12 370 * are defining a content entity type that uses bundles, the 'bundle_label'
Chris@12 371 * property gives the human-readable name to use for a bundle of this entity
Chris@12 372 * type (for example, "Content type" for the Node entity).
Chris@0 373 * - The annotation will refer to several handler classes, which you will also
Chris@0 374 * need to define:
Chris@0 375 * - list_builder: Define a class that extends
Chris@0 376 * \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration
Chris@0 377 * entities) or \Drupal\Core\Entity\EntityListBuilder (for content
Chris@0 378 * entities), to provide an administrative overview for your entities.
Chris@0 379 * - add and edit forms, or default form: Define a class (or two) that
Chris@0 380 * extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms
Chris@0 381 * for your entities. For content entities, base class
Chris@0 382 * \Drupal\Core\Entity\ContentEntityForm is a better starting point.
Chris@0 383 * - delete form: Define a class that extends
Chris@0 384 * \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete
Chris@0 385 * confirmation form for your entities.
Chris@0 386 * - view_builder: For content entities and config entities that need to be
Chris@0 387 * viewed, define a class that implements
Chris@0 388 * \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
Chris@0 389 * \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
Chris@0 390 * - translation: For translatable content entities (if the 'translatable'
Chris@12 391 * annotation property has value TRUE), define a class that extends
Chris@0 392 * \Drupal\content_translation\ContentTranslationHandler, to translate
Chris@0 393 * the content. Configuration translation is handled automatically by the
Chris@0 394 * Configuration Translation module, without the need of a handler class.
Chris@0 395 * - access: If your configuration entity has complex permissions, you might
Chris@0 396 * need an access control handling, implementing
Chris@12 397 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most
Chris@12 398 * entities can just use the 'admin_permission' annotation property
Chris@12 399 * instead. Note that if you are creating your own access control handler,
Chris@12 400 * you should override the checkAccess() and checkCreateAccess() methods,
Chris@12 401 * not access().
Chris@0 402 * - storage: A class implementing
Chris@0 403 * \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
Chris@0 404 * entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
Chris@0 405 * config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage.
Chris@0 406 * You can extend one of these classes to provide custom behavior.
Chris@0 407 * - views_data: A class implementing \Drupal\views\EntityViewsDataInterface
Chris@0 408 * to provide views data for the entity type. You can autogenerate most of
Chris@0 409 * the views data by extending \Drupal\views\EntityViewsData.
Chris@0 410 * - For content entities, the annotation will refer to a number of database
Chris@0 411 * tables and their fields. These annotation properties, such as 'base_table',
Chris@0 412 * 'data_table', 'entity_keys', etc., are documented on
Chris@0 413 * \Drupal\Core\Entity\EntityType.
Chris@0 414 * - For content entities that are displayed on their own pages, the annotation
Chris@0 415 * will refer to a 'uri_callback' function, which takes an object of the
Chris@0 416 * entity interface you have defined as its parameter, and returns routing
Chris@0 417 * information for the entity page; see node_uri() for an example. You will
Chris@0 418 * also need to add a corresponding route to your module's routing.yml file;
Chris@0 419 * see the entity.node.canonical route in node.routing.yml for an example, and see
Chris@0 420 * @ref sec_routes below for some notes.
Chris@0 421 * - Optionally, instead of defining routes, routes can be auto generated by
Chris@0 422 * providing a route handler. See @ref sec_routes. Otherwise, define routes
Chris@0 423 * and links for the various URLs associated with the entity.
Chris@0 424 * These go into the 'links' annotation, with the link type as the key, and
Chris@0 425 * the path of this link template as the value. The corresponding route
Chris@0 426 * requires the following route name:
Chris@0 427 * "entity.$entity_type_id.$link_template_type". See @ref sec_routes below for
Chris@0 428 * some routing notes. Typical link types are:
Chris@0 429 * - canonical: Default link, either to view (if entities are viewed on their
Chris@0 430 * own pages) or edit the entity.
Chris@0 431 * - delete-form: Confirmation form to delete the entity.
Chris@0 432 * - edit-form: Editing form.
Chris@0 433 * - Other link types specific to your entity type can also be defined.
Chris@12 434 * - If your content entity is fieldable, provide the 'field_ui_base_route'
Chris@12 435 * annotation property, giving the name of the route that the Manage Fields,
Chris@12 436 * Manage Display, and Manage Form Display pages from the Field UI module
Chris@12 437 * will be attached to. This is usually the bundle settings edit page, or an
Chris@12 438 * entity type settings page if there are no bundles.
Chris@0 439 * - If your content entity has bundles, you will also need to define a second
Chris@0 440 * plugin to handle the bundles. This plugin is itself a configuration entity
Chris@0 441 * type, so follow the steps here to define it. The machine name ('id'
Chris@12 442 * annotation property) of this configuration entity class goes into the
Chris@12 443 * 'bundle_entity_type' annotation property on the entity type class. For
Chris@12 444 * example, for the Node entity, the bundle class is
Chris@12 445 * \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is
Chris@12 446 * the annotation property 'bundle_entity_type' on the
Chris@12 447 * \Drupal\node\Entity\Node class. Also, the
Chris@12 448 * bundle config entity type annotation must have a 'bundle_of' property,
Chris@0 449 * giving the machine name of the entity type it is acting as a bundle for.
Chris@0 450 * These machine names are considered permanent, they may not be renamed.
Chris@12 451 * - Additional annotation properties can be seen on entity class examples such
Chris@12 452 * as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
Chris@12 453 * (configuration). These annotation properties are documented on
Chris@0 454 * \Drupal\Core\Entity\EntityType.
Chris@0 455 *
Chris@0 456 * @section sec_routes Entity routes
Chris@0 457 * Entity routes can be defined in *.routing.yml files, like any other route:
Chris@0 458 * see the @link routing Routing API @endlink topic for more information.
Chris@0 459 * Another option for entity routes is to use a route provider class, and
Chris@0 460 * reference it in the annotations on the entity class: see the end of this
Chris@0 461 * section for an example.
Chris@0 462 *
Chris@0 463 * It's possible to use both a YAML file and a provider class for entity
Chris@0 464 * routes, at the same time. Avoid duplicating route names between the two:
Chris@0 465 * if a duplicate route name is found in both locations, the one in the YAML
Chris@0 466 * file takes precedence; regardless, such duplication can be confusing.
Chris@0 467 *
Chris@0 468 * Here's an example YAML route specification, for the block configure form:
Chris@0 469 * @code
Chris@0 470 * entity.block.edit_form:
Chris@0 471 * path: '/admin/structure/block/manage/{block}'
Chris@0 472 * defaults:
Chris@0 473 * _entity_form: 'block.default'
Chris@0 474 * _title: 'Configure block'
Chris@0 475 * requirements:
Chris@0 476 * _entity_access: 'block.update'
Chris@0 477 * @endcode
Chris@0 478 * Some notes on this example:
Chris@0 479 * - path: The {block} in the path is a placeholder, which (for an entity) must
Chris@0 480 * always take the form of {machine_name_of_entity_type}. In the URL, the
Chris@0 481 * placeholder value will be the ID of an entity item. When the route is used,
Chris@0 482 * the entity system will load the corresponding entity item and pass it in as
Chris@0 483 * an object to the controller for the route.
Chris@0 484 * - defaults: For entity form routes, use _entity_form rather than the generic
Chris@0 485 * _controller or _form. The value is composed of the entity type machine name
Chris@0 486 * and a form handler type from the entity annotation (see @ref define above
Chris@0 487 * more more on handlers and annotation). So, in this example, block.default
Chris@0 488 * refers to the 'default' form handler on the block entity type, whose
Chris@0 489 * annotation contains:
Chris@0 490 * @code
Chris@0 491 * handlers = {
Chris@0 492 * "form" = {
Chris@0 493 * "default" = "Drupal\block\BlockForm",
Chris@0 494 * @endcode
Chris@0 495 * If instead of YAML you want to use a route provider class:
Chris@0 496 * - \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical,
Chris@0 497 * edit-form, and delete-form routes.
Chris@0 498 * - \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same
Chris@0 499 * routes, set up to use the administrative theme for edit and delete pages.
Chris@0 500 * - You can also create your own class, extending one of these two classes if
Chris@0 501 * you only want to modify their behaviour slightly.
Chris@0 502 *
Chris@0 503 * To register any route provider class, add lines like the following to your
Chris@0 504 * entity class annotation:
Chris@0 505 * @code
Chris@0 506 * handlers = {
Chris@0 507 * "route_provider" = {
Chris@0 508 * "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
Chris@0 509 * @endcode
Chris@0 510 *
Chris@0 511 * @section bundle Defining a content entity bundle
Chris@0 512 * For entity types that use bundles, such as Node (bundles are content types)
Chris@0 513 * and Taxonomy (bundles are vocabularies), modules and install profiles can
Chris@0 514 * define bundles by supplying default configuration in their config/install
Chris@0 515 * directories. (See the @link config_api Configuration API topic @endlink for
Chris@0 516 * general information about configuration.)
Chris@0 517 *
Chris@0 518 * There are several good examples of this in Drupal Core:
Chris@0 519 * - The Forum module defines a content type in node.type.forum.yml and a
Chris@0 520 * vocabulary in taxonomy.vocabulary.forums.yml
Chris@0 521 * - The Book module defines a content type in node.type.book.yml
Chris@0 522 * - The Standard install profile defines Page and Article content types in
Chris@0 523 * node.type.page.yml and node.type.article.yml, a Tags vocabulary in
Chris@0 524 * taxonomy.vocabulary.tags.yml, and a Node comment type in
Chris@0 525 * comment.type.comment.yml. This profile's configuration is especially
Chris@0 526 * instructive, because it also adds several fields to the Article type, and
Chris@0 527 * it sets up view and form display modes for the node types.
Chris@0 528 *
Chris@0 529 * @section load_query Loading, querying, and rendering entities
Chris@0 530 * To load entities, use the entity storage manager, which is an object
Chris@0 531 * implementing \Drupal\Core\Entity\EntityStorageInterface that you can
Chris@0 532 * retrieve with:
Chris@0 533 * @code
Chris@18 534 * $storage = \Drupal::entityTypeManager()->getStorage('your_entity_type');
Chris@0 535 * // Or if you have a $container variable:
Chris@18 536 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type');
Chris@0 537 * @endcode
Chris@0 538 * Here, 'your_entity_type' is the machine name of your entity type ('id'
Chris@12 539 * annotation property on the entity class), and note that you should use
Chris@12 540 * dependency injection to retrieve this object if possible. See the
Chris@0 541 * @link container Services and Dependency Injection topic @endlink for more
Chris@0 542 * about how to properly retrieve services.
Chris@0 543 *
Chris@0 544 * To query to find entities to load, use an entity query, which is a object
Chris@0 545 * implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve
Chris@0 546 * with:
Chris@0 547 * @code
Chris@0 548 * // Simple query:
Chris@0 549 * $query = \Drupal::entityQuery('your_entity_type');
Chris@0 550 * // Or, if you have a $container variable:
Chris@0 551 * $storage = $container->get('entity_type.manager')->getStorage('your_entity_type');
Chris@0 552 * $query = $storage->getQuery();
Chris@0 553 * @endcode
Chris@0 554 * If you need aggregation, there is an aggregate query available, which
Chris@0 555 * implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
Chris@0 556 * @code
Chris@0 557 * $query \Drupal::entityQueryAggregate('your_entity_type');
Chris@0 558 * // Or:
Chris@0 559 * $query = $storage->getAggregateQuery('your_entity_type');
Chris@0 560 * @endcode
Chris@0 561 *
Chris@0 562 * In either case, you can then add conditions to your query, using methods
Chris@0 563 * like condition(), exists(), etc. on $query; add sorting, pager, and range
Chris@0 564 * if needed, and execute the query to return a list of entity IDs that match
Chris@0 565 * the query.
Chris@0 566 *
Chris@0 567 * Here is an example, using the core File entity:
Chris@0 568 * @code
Chris@0 569 * $fids = Drupal::entityQuery('file')
Chris@0 570 * ->condition('status', FILE_STATUS_PERMANENT, '<>')
Chris@0 571 * ->condition('changed', REQUEST_TIME - $age, '<')
Chris@0 572 * ->range(0, 100)
Chris@0 573 * ->execute();
Chris@0 574 * $files = $storage->loadMultiple($fids);
Chris@0 575 * @endcode
Chris@0 576 *
Chris@0 577 * The normal way of viewing entities is by using a route, as described in the
Chris@0 578 * sections above. If for some reason you need to render an entity in code in a
Chris@0 579 * particular view mode, you can use an entity view builder, which is an object
Chris@0 580 * implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can
Chris@0 581 * retrieve with:
Chris@0 582 * @code
Chris@0 583 * $view_builder = \Drupal::entityManager()->getViewBuilder('your_entity_type');
Chris@0 584 * // Or if you have a $container variable:
Chris@0 585 * $view_builder = $container->get('entity.manager')->getViewBuilder('your_entity_type');
Chris@0 586 * @endcode
Chris@0 587 * Then, to build and render the entity:
Chris@0 588 * @code
Chris@0 589 * // You can omit the language ID, by default the current content language will
Chris@0 590 * // be used. If no translation is available for the current language, fallback
Chris@0 591 * // rules will be used.
Chris@0 592 * $build = $view_builder->view($entity, 'view_mode_name', $language->getId());
Chris@0 593 * // $build is a render array.
Chris@16 594 * $rendered = \Drupal::service('renderer')->render($build);
Chris@0 595 * @endcode
Chris@0 596 *
Chris@0 597 * @section sec_access Access checking on entities
Chris@0 598 * Entity types define their access permission scheme in their annotation.
Chris@0 599 * Access permissions can be quite complex, so you should not assume any
Chris@0 600 * particular permission scheme. Instead, once you have an entity object
Chris@0 601 * loaded, you can check for permission for a particular operation (such as
Chris@0 602 * 'view') at the entity or field level by calling:
Chris@0 603 * @code
Chris@0 604 * $entity->access($operation);
Chris@0 605 * $entity->nameOfField->access($operation);
Chris@0 606 * @endcode
Chris@0 607 * The interface related to access checking in entities and fields is
Chris@0 608 * \Drupal\Core\Access\AccessibleInterface.
Chris@0 609 *
Chris@0 610 * The default entity access control handler invokes two hooks while checking
Chris@0 611 * access on a single entity: hook_entity_access() is invoked first, and
Chris@0 612 * then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name
Chris@0 613 * of the entity type). If no module returns a TRUE or FALSE value from
Chris@0 614 * either of these hooks, then the entity's default access checking takes
Chris@0 615 * place. For create operations (creating a new entity), the hooks that
Chris@0 616 * are invoked are hook_entity_create_access() and
Chris@0 617 * hook_ENTITY_TYPE_create_access() instead.
Chris@0 618 *
Chris@0 619 * The Node entity type has a complex system for determining access, which
Chris@0 620 * developers can interact with. This is described in the
Chris@0 621 * @link node_access Node access topic. @endlink
Chris@0 622 *
Chris@0 623 * @see i18n
Chris@0 624 * @see entity_crud
Chris@18 625 * @see \Drupal\Core\Entity\EntityRepositoryInterface::getTranslationFromContext()
Chris@0 626 * @}
Chris@0 627 */
Chris@0 628
Chris@0 629 /**
Chris@0 630 * @addtogroup hooks
Chris@0 631 * @{
Chris@0 632 */
Chris@0 633
Chris@0 634 /**
Chris@0 635 * Control entity operation access.
Chris@0 636 *
Chris@0 637 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 638 * The entity to check access to.
Chris@0 639 * @param string $operation
Chris@0 640 * The operation that is to be performed on $entity.
Chris@0 641 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 642 * The account trying to access the entity.
Chris@0 643 *
Chris@0 644 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 645 * The access result. The final result is calculated by using
Chris@0 646 * \Drupal\Core\Access\AccessResultInterface::orIf() on the result of every
Chris@0 647 * hook_entity_access() and hook_ENTITY_TYPE_access() implementation, and the
Chris@0 648 * result of the entity-specific checkAccess() method in the entity access
Chris@0 649 * control handler. Be careful when writing generalized access checks shared
Chris@0 650 * between routing and entity checks: routing uses the andIf() operator. So
Chris@0 651 * returning an isNeutral() does not determine entity access at all but it
Chris@0 652 * always ends up denying access while routing.
Chris@0 653 *
Chris@0 654 * @see \Drupal\Core\Entity\EntityAccessControlHandler
Chris@0 655 * @see hook_entity_create_access()
Chris@0 656 * @see hook_ENTITY_TYPE_access()
Chris@0 657 *
Chris@0 658 * @ingroup entity_api
Chris@0 659 */
Chris@0 660 function hook_entity_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
Chris@0 661 // No opinion.
Chris@0 662 return AccessResult::neutral();
Chris@0 663 }
Chris@0 664
Chris@0 665 /**
Chris@0 666 * Control entity operation access for a specific entity type.
Chris@0 667 *
Chris@0 668 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 669 * The entity to check access to.
Chris@0 670 * @param string $operation
Chris@0 671 * The operation that is to be performed on $entity.
Chris@0 672 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 673 * The account trying to access the entity.
Chris@0 674 *
Chris@0 675 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 676 * The access result. hook_entity_access() has detailed documentation.
Chris@0 677 *
Chris@0 678 * @see \Drupal\Core\Entity\EntityAccessControlHandler
Chris@0 679 * @see hook_ENTITY_TYPE_create_access()
Chris@0 680 * @see hook_entity_access()
Chris@0 681 *
Chris@0 682 * @ingroup entity_api
Chris@0 683 */
Chris@0 684 function hook_ENTITY_TYPE_access(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Session\AccountInterface $account) {
Chris@0 685 // No opinion.
Chris@0 686 return AccessResult::neutral();
Chris@0 687 }
Chris@0 688
Chris@0 689 /**
Chris@0 690 * Control entity create access.
Chris@0 691 *
Chris@0 692 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 693 * The account trying to access the entity.
Chris@0 694 * @param array $context
Chris@0 695 * An associative array of additional context values. By default it contains
Chris@0 696 * language and the entity type ID:
Chris@0 697 * - entity_type_id - the entity type ID.
Chris@0 698 * - langcode - the current language code.
Chris@0 699 * @param string $entity_bundle
Chris@0 700 * The entity bundle name.
Chris@0 701 *
Chris@0 702 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 703 * The access result.
Chris@0 704 *
Chris@0 705 * @see \Drupal\Core\Entity\EntityAccessControlHandler
Chris@0 706 * @see hook_entity_access()
Chris@0 707 * @see hook_ENTITY_TYPE_create_access()
Chris@0 708 *
Chris@0 709 * @ingroup entity_api
Chris@0 710 */
Chris@0 711 function hook_entity_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
Chris@0 712 // No opinion.
Chris@0 713 return AccessResult::neutral();
Chris@0 714 }
Chris@0 715
Chris@0 716 /**
Chris@0 717 * Control entity create access for a specific entity type.
Chris@0 718 *
Chris@0 719 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 720 * The account trying to access the entity.
Chris@0 721 * @param array $context
Chris@0 722 * An associative array of additional context values. By default it contains
Chris@0 723 * language:
Chris@0 724 * - langcode - the current language code.
Chris@0 725 * @param string $entity_bundle
Chris@0 726 * The entity bundle name.
Chris@0 727 *
Chris@0 728 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 729 * The access result.
Chris@0 730 *
Chris@0 731 * @see \Drupal\Core\Entity\EntityAccessControlHandler
Chris@0 732 * @see hook_ENTITY_TYPE_access()
Chris@0 733 * @see hook_entity_create_access()
Chris@0 734 *
Chris@0 735 * @ingroup entity_api
Chris@0 736 */
Chris@0 737 function hook_ENTITY_TYPE_create_access(\Drupal\Core\Session\AccountInterface $account, array $context, $entity_bundle) {
Chris@0 738 // No opinion.
Chris@0 739 return AccessResult::neutral();
Chris@0 740 }
Chris@0 741
Chris@0 742 /**
Chris@0 743 * Add to entity type definitions.
Chris@0 744 *
Chris@0 745 * Modules may implement this hook to add information to defined entity types,
Chris@0 746 * as defined in \Drupal\Core\Entity\EntityTypeInterface.
Chris@0 747 *
Chris@0 748 * To alter existing information or to add information dynamically, use
Chris@0 749 * hook_entity_type_alter().
Chris@0 750 *
Chris@0 751 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
Chris@0 752 * An associative array of all entity type definitions, keyed by the entity
Chris@0 753 * type name. Passed by reference.
Chris@0 754 *
Chris@0 755 * @see \Drupal\Core\Entity\Entity
Chris@0 756 * @see \Drupal\Core\Entity\EntityTypeInterface
Chris@0 757 * @see hook_entity_type_alter()
Chris@0 758 */
Chris@0 759 function hook_entity_type_build(array &$entity_types) {
Chris@0 760 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
Chris@0 761 // Add a form for a custom node form without overriding the default
Chris@0 762 // node form. To override the default node form, use hook_entity_type_alter().
Chris@0 763 $entity_types['node']->setFormClass('mymodule_foo', 'Drupal\mymodule\NodeFooForm');
Chris@0 764 }
Chris@0 765
Chris@0 766 /**
Chris@0 767 * Alter the entity type definitions.
Chris@0 768 *
Chris@0 769 * Modules may implement this hook to alter the information that defines an
Chris@0 770 * entity type. All properties that are available in
Chris@0 771 * \Drupal\Core\Entity\Annotation\EntityType and all the ones additionally
Chris@0 772 * provided by modules can be altered here.
Chris@0 773 *
Chris@0 774 * Do not use this hook to add information to entity types, unless one of the
Chris@0 775 * following is true:
Chris@0 776 * - You are filling in default values.
Chris@0 777 * - You need to dynamically add information only in certain circumstances.
Chris@0 778 * - Your hook needs to run after hook_entity_type_build() implementations.
Chris@0 779 * Use hook_entity_type_build() instead in all other cases.
Chris@0 780 *
Chris@0 781 * @param \Drupal\Core\Entity\EntityTypeInterface[] $entity_types
Chris@0 782 * An associative array of all entity type definitions, keyed by the entity
Chris@0 783 * type name. Passed by reference.
Chris@0 784 *
Chris@0 785 * @see \Drupal\Core\Entity\Entity
Chris@0 786 * @see \Drupal\Core\Entity\EntityTypeInterface
Chris@0 787 */
Chris@0 788 function hook_entity_type_alter(array &$entity_types) {
Chris@0 789 /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
Chris@0 790 // Set the controller class for nodes to an alternate implementation of the
Chris@0 791 // Drupal\Core\Entity\EntityStorageInterface interface.
Chris@0 792 $entity_types['node']->setStorageClass('Drupal\mymodule\MyCustomNodeStorage');
Chris@0 793 }
Chris@0 794
Chris@0 795 /**
Chris@0 796 * Alter the view modes for entity types.
Chris@0 797 *
Chris@0 798 * @param array $view_modes
Chris@0 799 * An array of view modes, keyed first by entity type, then by view mode name.
Chris@0 800 *
Chris@18 801 * @see \Drupal\Core\Entity\EntityDisplayRepositoryInterface::getAllViewModes()
Chris@18 802 * @see \Drupal\Core\Entity\EntityDisplayRepositoryInterface::getViewModes()
Chris@0 803 */
Chris@0 804 function hook_entity_view_mode_info_alter(&$view_modes) {
Chris@0 805 $view_modes['user']['full']['status'] = TRUE;
Chris@0 806 }
Chris@0 807
Chris@0 808 /**
Chris@0 809 * Describe the bundles for entity types.
Chris@0 810 *
Chris@0 811 * @return array
Chris@0 812 * An associative array of all entity bundles, keyed by the entity
Chris@0 813 * type name, and then the bundle name, with the following keys:
Chris@0 814 * - label: The human-readable name of the bundle.
Chris@17 815 * - uri_callback: (optional) The same as the 'uri_callback' key defined for
Chris@18 816 * the entity type in the EntityTypeManager, but for the bundle only. When
Chris@17 817 * determining the URI of an entity, if a 'uri_callback' is defined for both
Chris@17 818 * the entity type and the bundle, the one for the bundle is used.
Chris@0 819 * - translatable: (optional) A boolean value specifying whether this bundle
Chris@0 820 * has translation support enabled. Defaults to FALSE.
Chris@0 821 *
Chris@0 822 * @see \Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
Chris@0 823 * @see hook_entity_bundle_info_alter()
Chris@0 824 */
Chris@0 825 function hook_entity_bundle_info() {
Chris@0 826 $bundles['user']['user']['label'] = t('User');
Chris@0 827 return $bundles;
Chris@0 828 }
Chris@0 829
Chris@0 830 /**
Chris@0 831 * Alter the bundles for entity types.
Chris@0 832 *
Chris@0 833 * @param array $bundles
Chris@0 834 * An array of bundles, keyed first by entity type, then by bundle name.
Chris@0 835 *
Chris@0 836 * @see Drupal\Core\Entity\EntityTypeBundleInfo::getBundleInfo()
Chris@0 837 * @see hook_entity_bundle_info()
Chris@0 838 */
Chris@0 839 function hook_entity_bundle_info_alter(&$bundles) {
Chris@0 840 $bundles['user']['user']['label'] = t('Full account');
Chris@0 841 }
Chris@0 842
Chris@0 843 /**
Chris@0 844 * Act on entity_bundle_create().
Chris@0 845 *
Chris@0 846 * This hook is invoked after the operation has been performed.
Chris@0 847 *
Chris@0 848 * @param string $entity_type_id
Chris@0 849 * The type of $entity; e.g. 'node' or 'user'.
Chris@0 850 * @param string $bundle
Chris@0 851 * The name of the bundle.
Chris@0 852 *
Chris@0 853 * @see entity_crud
Chris@0 854 */
Chris@0 855 function hook_entity_bundle_create($entity_type_id, $bundle) {
Chris@0 856 // When a new bundle is created, the menu needs to be rebuilt to add the
Chris@0 857 // Field UI menu item tabs.
Chris@0 858 \Drupal::service('router.builder')->setRebuildNeeded();
Chris@0 859 }
Chris@0 860
Chris@0 861 /**
Chris@0 862 * Act on entity_bundle_delete().
Chris@0 863 *
Chris@0 864 * This hook is invoked after the operation has been performed.
Chris@0 865 *
Chris@0 866 * @param string $entity_type_id
Chris@0 867 * The type of entity; for example, 'node' or 'user'.
Chris@0 868 * @param string $bundle
Chris@0 869 * The bundle that was just deleted.
Chris@0 870 *
Chris@0 871 * @ingroup entity_crud
Chris@0 872 */
Chris@0 873 function hook_entity_bundle_delete($entity_type_id, $bundle) {
Chris@0 874 // Remove the settings associated with the bundle in my_module.settings.
Chris@0 875 $config = \Drupal::config('my_module.settings');
Chris@0 876 $bundle_settings = $config->get('bundle_settings');
Chris@0 877 if (isset($bundle_settings[$entity_type_id][$bundle])) {
Chris@0 878 unset($bundle_settings[$entity_type_id][$bundle]);
Chris@0 879 $config->set('bundle_settings', $bundle_settings);
Chris@0 880 }
Chris@0 881 }
Chris@0 882
Chris@0 883 /**
Chris@0 884 * Acts when creating a new entity.
Chris@0 885 *
Chris@0 886 * This hook runs after a new entity object has just been instantiated.
Chris@0 887 *
Chris@0 888 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 889 * The entity object.
Chris@0 890 *
Chris@0 891 * @ingroup entity_crud
Chris@0 892 * @see hook_ENTITY_TYPE_create()
Chris@0 893 */
Chris@0 894 function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 895 \Drupal::logger('example')->info('Entity created: @label', ['@label' => $entity->label()]);
Chris@0 896 }
Chris@0 897
Chris@0 898 /**
Chris@0 899 * Acts when creating a new entity of a specific type.
Chris@0 900 *
Chris@0 901 * This hook runs after a new entity object has just been instantiated.
Chris@0 902 *
Chris@0 903 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 904 * The entity object.
Chris@0 905 *
Chris@0 906 * @ingroup entity_crud
Chris@0 907 * @see hook_entity_create()
Chris@0 908 */
Chris@0 909 function hook_ENTITY_TYPE_create(\Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 910 \Drupal::logger('example')->info('ENTITY_TYPE created: @label', ['@label' => $entity->label()]);
Chris@0 911 }
Chris@0 912
Chris@0 913 /**
Chris@17 914 * Respond to entity revision creation.
Chris@17 915 *
Chris@17 916 * @param \Drupal\Core\Entity\EntityInterface $new_revision
Chris@17 917 * The new revision that was created.
Chris@17 918 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@17 919 * The original entity that was used to create the revision from.
Chris@17 920 * @param bool|null $keep_untranslatable_fields
Chris@17 921 * Whether untranslatable field values were kept (TRUE) or copied from the
Chris@17 922 * default revision (FALSE) when generating a merged revision. If no value was
Chris@17 923 * explicitly specified (NULL), a default value of TRUE should be assumed if
Chris@17 924 * the provided entity is the default translation and untranslatable fields
Chris@17 925 * should only affect the default translation, FALSE otherwise.
Chris@17 926 *
Chris@17 927 * @ingroup entity_crud
Chris@17 928 * @see \Drupal\Core\Entity\RevisionableStorageInterface::createRevision()
Chris@17 929 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface::createRevision()
Chris@17 930 */
Chris@17 931 function hook_entity_revision_create(Drupal\Core\Entity\EntityInterface $new_revision, Drupal\Core\Entity\EntityInterface $entity, $keep_untranslatable_fields) {
Chris@17 932 // Retain the value from an untranslatable field, which are by default
Chris@17 933 // synchronized from the default revision.
Chris@17 934 $new_revision->set('untranslatable_field', $entity->get('untranslatable_field'));
Chris@17 935 }
Chris@17 936
Chris@17 937 /**
Chris@17 938 * Respond to entity revision creation.
Chris@17 939 *
Chris@17 940 * @param \Drupal\Core\Entity\EntityInterface $new_revision
Chris@17 941 * The new revision that was created.
Chris@17 942 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@17 943 * The original entity that was used to create the revision from.
Chris@17 944 * @param bool|null $keep_untranslatable_fields
Chris@17 945 * Whether untranslatable field values were kept (TRUE) or copied from the
Chris@17 946 * default revision (FALSE) when generating a merged revision. If no value was
Chris@17 947 * explicitly specified (NULL), a default value of TRUE should be assumed if
Chris@17 948 * the provided entity is the default translation and untranslatable fields
Chris@17 949 * should only affect the default translation, FALSE otherwise.
Chris@17 950 *
Chris@17 951 * @ingroup entity_crud
Chris@17 952 * @see \Drupal\Core\Entity\RevisionableStorageInterface::createRevision()
Chris@17 953 * @see \Drupal\Core\Entity\TranslatableRevisionableStorageInterface::createRevision()
Chris@17 954 */
Chris@17 955 function hook_ENTITY_TYPE_revision_create(Drupal\Core\Entity\EntityInterface $new_revision, Drupal\Core\Entity\EntityInterface $entity, $keep_untranslatable_fields) {
Chris@17 956 // Retain the value from an untranslatable field, which are by default
Chris@17 957 // synchronized from the default revision.
Chris@17 958 $new_revision->set('untranslatable_field', $entity->get('untranslatable_field'));
Chris@17 959 }
Chris@17 960
Chris@17 961 /**
Chris@18 962 * Act on an array of entity IDs before they are loaded.
Chris@18 963 *
Chris@18 964 * This hook can be used by modules that need, for example, to return a
Chris@18 965 * different revision than the default one.
Chris@18 966 *
Chris@18 967 * @param array $ids
Chris@18 968 * An array of entity IDs that have to be loaded.
Chris@18 969 * @param string $entity_type_id
Chris@18 970 * The type of entities being loaded (i.e. node, user, comment).
Chris@18 971 *
Chris@18 972 * @return \Drupal\Core\Entity\EntityInterface[]
Chris@18 973 * An array of pre-loaded entity objects.
Chris@18 974 *
Chris@18 975 * @ingroup entity_crud
Chris@18 976 */
Chris@18 977 function hook_entity_preload(array $ids, $entity_type_id) {
Chris@18 978 $entities = [];
Chris@18 979
Chris@18 980 foreach ($ids as $id) {
Chris@18 981 $entities[] = mymodule_swap_revision($id);
Chris@18 982 }
Chris@18 983
Chris@18 984 return $entities;
Chris@18 985 }
Chris@18 986
Chris@18 987 /**
Chris@0 988 * Act on entities when loaded.
Chris@0 989 *
Chris@0 990 * This is a generic load hook called for all entity types loaded via the
Chris@0 991 * entity API.
Chris@0 992 *
Chris@0 993 * hook_entity_storage_load() should be used to load additional data for
Chris@0 994 * content entities.
Chris@0 995 *
Chris@0 996 * @param \Drupal\Core\Entity\EntityInterface[] $entities
Chris@0 997 * The entities keyed by entity ID.
Chris@0 998 * @param string $entity_type_id
Chris@0 999 * The type of entities being loaded (i.e. node, user, comment).
Chris@0 1000 *
Chris@0 1001 * @ingroup entity_crud
Chris@0 1002 * @see hook_ENTITY_TYPE_load()
Chris@0 1003 */
Chris@0 1004 function hook_entity_load(array $entities, $entity_type_id) {
Chris@0 1005 foreach ($entities as $entity) {
Chris@0 1006 $entity->foo = mymodule_add_something($entity);
Chris@0 1007 }
Chris@0 1008 }
Chris@0 1009
Chris@0 1010 /**
Chris@0 1011 * Act on entities of a specific type when loaded.
Chris@0 1012 *
Chris@0 1013 * @param array $entities
Chris@0 1014 * The entities keyed by entity ID.
Chris@0 1015 *
Chris@0 1016 * @ingroup entity_crud
Chris@0 1017 * @see hook_entity_load()
Chris@0 1018 */
Chris@0 1019 function hook_ENTITY_TYPE_load($entities) {
Chris@0 1020 foreach ($entities as $entity) {
Chris@0 1021 $entity->foo = mymodule_add_something($entity);
Chris@0 1022 }
Chris@0 1023 }
Chris@0 1024
Chris@0 1025 /**
Chris@0 1026 * Act on content entities when loaded from the storage.
Chris@0 1027 *
Chris@0 1028 * The results of this hook will be cached.
Chris@0 1029 *
Chris@0 1030 * @param \Drupal\Core\Entity\EntityInterface[] $entities
Chris@0 1031 * The entities keyed by entity ID.
Chris@0 1032 * @param string $entity_type
Chris@0 1033 * The type of entities being loaded (i.e. node, user, comment).
Chris@0 1034 *
Chris@0 1035 * @see hook_entity_load()
Chris@0 1036 */
Chris@0 1037 function hook_entity_storage_load(array $entities, $entity_type) {
Chris@0 1038 foreach ($entities as $entity) {
Chris@0 1039 $entity->foo = mymodule_add_something_uncached($entity);
Chris@0 1040 }
Chris@0 1041 }
Chris@0 1042
Chris@0 1043 /**
Chris@0 1044 * Act on content entities of a given type when loaded from the storage.
Chris@0 1045 *
Chris@0 1046 * The results of this hook will be cached if the entity type supports it.
Chris@0 1047 *
Chris@0 1048 * @param \Drupal\Core\Entity\EntityInterface[] $entities
Chris@0 1049 * The entities keyed by entity ID.
Chris@0 1050 *
Chris@0 1051 * @see hook_entity_storage_load()
Chris@0 1052 */
Chris@0 1053 function hook_ENTITY_TYPE_storage_load(array $entities) {
Chris@0 1054 foreach ($entities as $entity) {
Chris@0 1055 $entity->foo = mymodule_add_something_uncached($entity);
Chris@0 1056 }
Chris@0 1057 }
Chris@0 1058
Chris@0 1059 /**
Chris@0 1060 * Act on an entity before it is created or updated.
Chris@0 1061 *
Chris@0 1062 * You can get the original entity object from $entity->original when it is an
Chris@0 1063 * update of the entity.
Chris@0 1064 *
Chris@0 1065 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1066 * The entity object.
Chris@0 1067 *
Chris@0 1068 * @ingroup entity_crud
Chris@0 1069 * @see hook_ENTITY_TYPE_presave()
Chris@0 1070 */
Chris@0 1071 function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1072 if ($entity instanceof ContentEntityInterface && $entity->isTranslatable()) {
Chris@0 1073 $route_match = \Drupal::routeMatch();
Chris@0 1074 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
Chris@0 1075 }
Chris@0 1076 }
Chris@0 1077
Chris@0 1078 /**
Chris@0 1079 * Act on a specific type of entity before it is created or updated.
Chris@0 1080 *
Chris@0 1081 * You can get the original entity object from $entity->original when it is an
Chris@0 1082 * update of the entity.
Chris@0 1083 *
Chris@0 1084 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1085 * The entity object.
Chris@0 1086 *
Chris@0 1087 * @ingroup entity_crud
Chris@0 1088 * @see hook_entity_presave()
Chris@0 1089 */
Chris@0 1090 function hook_ENTITY_TYPE_presave(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1091 if ($entity->isTranslatable()) {
Chris@0 1092 $route_match = \Drupal::routeMatch();
Chris@0 1093 \Drupal::service('content_translation.synchronizer')->synchronizeFields($entity, $entity->language()->getId(), $route_match->getParameter('source_langcode'));
Chris@0 1094 }
Chris@0 1095 }
Chris@0 1096
Chris@0 1097 /**
Chris@0 1098 * Respond to creation of a new entity.
Chris@0 1099 *
Chris@0 1100 * This hook runs once the entity has been stored. Note that hook
Chris@0 1101 * implementations may not alter the stored entity data.
Chris@0 1102 *
Chris@0 1103 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1104 * The entity object.
Chris@0 1105 *
Chris@0 1106 * @ingroup entity_crud
Chris@0 1107 * @see hook_ENTITY_TYPE_insert()
Chris@0 1108 */
Chris@0 1109 function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1110 // Insert the new entity into a fictional table of all entities.
Chris@18 1111 \Drupal::database()->insert('example_entity')
Chris@0 1112 ->fields([
Chris@0 1113 'type' => $entity->getEntityTypeId(),
Chris@0 1114 'id' => $entity->id(),
Chris@0 1115 'created' => REQUEST_TIME,
Chris@0 1116 'updated' => REQUEST_TIME,
Chris@0 1117 ])
Chris@0 1118 ->execute();
Chris@0 1119 }
Chris@0 1120
Chris@0 1121 /**
Chris@0 1122 * Respond to creation of a new entity of a particular type.
Chris@0 1123 *
Chris@0 1124 * This hook runs once the entity has been stored. Note that hook
Chris@0 1125 * implementations may not alter the stored entity data.
Chris@0 1126 *
Chris@0 1127 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1128 * The entity object.
Chris@0 1129 *
Chris@0 1130 * @ingroup entity_crud
Chris@0 1131 * @see hook_entity_insert()
Chris@0 1132 */
Chris@0 1133 function hook_ENTITY_TYPE_insert(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1134 // Insert the new entity into a fictional table of this type of entity.
Chris@18 1135 \Drupal::database()->insert('example_entity')
Chris@0 1136 ->fields([
Chris@0 1137 'id' => $entity->id(),
Chris@0 1138 'created' => REQUEST_TIME,
Chris@0 1139 'updated' => REQUEST_TIME,
Chris@0 1140 ])
Chris@0 1141 ->execute();
Chris@0 1142 }
Chris@0 1143
Chris@0 1144 /**
Chris@0 1145 * Respond to updates to an entity.
Chris@0 1146 *
Chris@0 1147 * This hook runs once the entity storage has been updated. Note that hook
Chris@0 1148 * implementations may not alter the stored entity data. Get the original entity
Chris@0 1149 * object from $entity->original.
Chris@0 1150 *
Chris@0 1151 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1152 * The entity object.
Chris@0 1153 *
Chris@0 1154 * @ingroup entity_crud
Chris@0 1155 * @see hook_ENTITY_TYPE_update()
Chris@0 1156 */
Chris@0 1157 function hook_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1158 // Update the entity's entry in a fictional table of all entities.
Chris@18 1159 \Drupal::database()->update('example_entity')
Chris@0 1160 ->fields([
Chris@0 1161 'updated' => REQUEST_TIME,
Chris@0 1162 ])
Chris@0 1163 ->condition('type', $entity->getEntityTypeId())
Chris@0 1164 ->condition('id', $entity->id())
Chris@0 1165 ->execute();
Chris@0 1166 }
Chris@0 1167
Chris@0 1168 /**
Chris@0 1169 * Respond to updates to an entity of a particular type.
Chris@0 1170 *
Chris@0 1171 * This hook runs once the entity storage has been updated. Note that hook
Chris@0 1172 * implementations may not alter the stored entity data. Get the original entity
Chris@0 1173 * object from $entity->original.
Chris@0 1174 *
Chris@0 1175 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1176 * The entity object.
Chris@0 1177 *
Chris@0 1178 * @ingroup entity_crud
Chris@0 1179 * @see hook_entity_update()
Chris@0 1180 */
Chris@0 1181 function hook_ENTITY_TYPE_update(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1182 // Update the entity's entry in a fictional table of this type of entity.
Chris@18 1183 \Drupal::database()->update('example_entity')
Chris@0 1184 ->fields([
Chris@0 1185 'updated' => REQUEST_TIME,
Chris@0 1186 ])
Chris@0 1187 ->condition('id', $entity->id())
Chris@0 1188 ->execute();
Chris@0 1189 }
Chris@0 1190
Chris@0 1191 /**
Chris@0 1192 * Acts when creating a new entity translation.
Chris@0 1193 *
Chris@0 1194 * This hook runs after a new entity translation object has just been
Chris@0 1195 * instantiated.
Chris@0 1196 *
Chris@0 1197 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1198 * The entity object.
Chris@0 1199 *
Chris@0 1200 * @ingroup entity_crud
Chris@0 1201 * @see hook_ENTITY_TYPE_translation_create()
Chris@0 1202 */
Chris@0 1203 function hook_entity_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1204 \Drupal::logger('example')->info('Entity translation created: @label', ['@label' => $translation->label()]);
Chris@0 1205 }
Chris@0 1206
Chris@0 1207 /**
Chris@0 1208 * Acts when creating a new entity translation of a specific type.
Chris@0 1209 *
Chris@0 1210 * This hook runs after a new entity translation object has just been
Chris@0 1211 * instantiated.
Chris@0 1212 *
Chris@0 1213 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1214 * The entity object.
Chris@0 1215 *
Chris@0 1216 * @ingroup entity_crud
Chris@0 1217 * @see hook_entity_translation_create()
Chris@0 1218 */
Chris@0 1219 function hook_ENTITY_TYPE_translation_create(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1220 \Drupal::logger('example')->info('ENTITY_TYPE translation created: @label', ['@label' => $translation->label()]);
Chris@0 1221 }
Chris@0 1222
Chris@0 1223 /**
Chris@0 1224 * Respond to creation of a new entity translation.
Chris@0 1225 *
Chris@0 1226 * This hook runs once the entity translation has been stored. Note that hook
Chris@0 1227 * implementations may not alter the stored entity translation data.
Chris@0 1228 *
Chris@0 1229 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1230 * The entity object of the translation just stored.
Chris@0 1231 *
Chris@0 1232 * @ingroup entity_crud
Chris@0 1233 * @see hook_ENTITY_TYPE_translation_insert()
Chris@0 1234 */
Chris@0 1235 function hook_entity_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1236 $variables = [
Chris@0 1237 '@language' => $translation->language()->getName(),
Chris@0 1238 '@label' => $translation->getUntranslated()->label(),
Chris@0 1239 ];
Chris@0 1240 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
Chris@0 1241 }
Chris@0 1242
Chris@0 1243 /**
Chris@0 1244 * Respond to creation of a new entity translation of a particular type.
Chris@0 1245 *
Chris@0 1246 * This hook runs once the entity translation has been stored. Note that hook
Chris@0 1247 * implementations may not alter the stored entity translation data.
Chris@0 1248 *
Chris@0 1249 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1250 * The entity object of the translation just stored.
Chris@0 1251 *
Chris@0 1252 * @ingroup entity_crud
Chris@0 1253 * @see hook_entity_translation_insert()
Chris@0 1254 */
Chris@0 1255 function hook_ENTITY_TYPE_translation_insert(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1256 $variables = [
Chris@0 1257 '@language' => $translation->language()->getName(),
Chris@0 1258 '@label' => $translation->getUntranslated()->label(),
Chris@0 1259 ];
Chris@0 1260 \Drupal::logger('example')->notice('The @language translation of @label has just been stored.', $variables);
Chris@0 1261 }
Chris@0 1262
Chris@0 1263 /**
Chris@0 1264 * Respond to entity translation deletion.
Chris@0 1265 *
Chris@0 1266 * This hook runs once the entity translation has been deleted from storage.
Chris@0 1267 *
Chris@0 1268 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1269 * The original entity object.
Chris@0 1270 *
Chris@0 1271 * @ingroup entity_crud
Chris@0 1272 * @see hook_ENTITY_TYPE_translation_delete()
Chris@0 1273 */
Chris@0 1274 function hook_entity_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1275 $variables = [
Chris@0 1276 '@language' => $translation->language()->getName(),
Chris@0 1277 '@label' => $translation->label(),
Chris@0 1278 ];
Chris@0 1279 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
Chris@0 1280 }
Chris@0 1281
Chris@0 1282 /**
Chris@0 1283 * Respond to entity translation deletion of a particular type.
Chris@0 1284 *
Chris@0 1285 * This hook runs once the entity translation has been deleted from storage.
Chris@0 1286 *
Chris@0 1287 * @param \Drupal\Core\Entity\EntityInterface $translation
Chris@0 1288 * The original entity object.
Chris@0 1289 *
Chris@0 1290 * @ingroup entity_crud
Chris@0 1291 * @see hook_entity_translation_delete()
Chris@0 1292 */
Chris@0 1293 function hook_ENTITY_TYPE_translation_delete(\Drupal\Core\Entity\EntityInterface $translation) {
Chris@0 1294 $variables = [
Chris@0 1295 '@language' => $translation->language()->getName(),
Chris@0 1296 '@label' => $translation->label(),
Chris@0 1297 ];
Chris@0 1298 \Drupal::logger('example')->notice('The @language translation of @label has just been deleted.', $variables);
Chris@0 1299 }
Chris@0 1300
Chris@0 1301 /**
Chris@0 1302 * Act before entity deletion.
Chris@0 1303 *
Chris@0 1304 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1305 * The entity object for the entity that is about to be deleted.
Chris@0 1306 *
Chris@0 1307 * @ingroup entity_crud
Chris@0 1308 * @see hook_ENTITY_TYPE_predelete()
Chris@0 1309 */
Chris@0 1310 function hook_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@18 1311 $connection = \Drupal::database();
Chris@0 1312 // Count references to this entity in a custom table before they are removed
Chris@0 1313 // upon entity deletion.
Chris@0 1314 $id = $entity->id();
Chris@0 1315 $type = $entity->getEntityTypeId();
Chris@18 1316 $count = \Drupal::database()->select('example_entity_data')
Chris@0 1317 ->condition('type', $type)
Chris@0 1318 ->condition('id', $id)
Chris@0 1319 ->countQuery()
Chris@0 1320 ->execute()
Chris@0 1321 ->fetchField();
Chris@0 1322
Chris@0 1323 // Log the count in a table that records this statistic for deleted entities.
Chris@18 1324 $connection->merge('example_deleted_entity_statistics')
Chris@0 1325 ->key(['type' => $type, 'id' => $id])
Chris@0 1326 ->fields(['count' => $count])
Chris@0 1327 ->execute();
Chris@0 1328 }
Chris@0 1329
Chris@0 1330 /**
Chris@0 1331 * Act before entity deletion of a particular entity type.
Chris@0 1332 *
Chris@0 1333 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1334 * The entity object for the entity that is about to be deleted.
Chris@0 1335 *
Chris@0 1336 * @ingroup entity_crud
Chris@0 1337 * @see hook_entity_predelete()
Chris@0 1338 */
Chris@0 1339 function hook_ENTITY_TYPE_predelete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@18 1340 $connection = \Drupal::database();
Chris@0 1341 // Count references to this entity in a custom table before they are removed
Chris@0 1342 // upon entity deletion.
Chris@0 1343 $id = $entity->id();
Chris@0 1344 $type = $entity->getEntityTypeId();
Chris@18 1345 $count = \Drupal::database()->select('example_entity_data')
Chris@0 1346 ->condition('type', $type)
Chris@0 1347 ->condition('id', $id)
Chris@0 1348 ->countQuery()
Chris@0 1349 ->execute()
Chris@0 1350 ->fetchField();
Chris@0 1351
Chris@0 1352 // Log the count in a table that records this statistic for deleted entities.
Chris@18 1353 $connection->merge('example_deleted_entity_statistics')
Chris@0 1354 ->key(['type' => $type, 'id' => $id])
Chris@0 1355 ->fields(['count' => $count])
Chris@0 1356 ->execute();
Chris@0 1357 }
Chris@0 1358
Chris@0 1359 /**
Chris@0 1360 * Respond to entity deletion.
Chris@0 1361 *
Chris@0 1362 * This hook runs once the entity has been deleted from the storage.
Chris@0 1363 *
Chris@0 1364 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1365 * The entity object for the entity that has been deleted.
Chris@0 1366 *
Chris@0 1367 * @ingroup entity_crud
Chris@0 1368 * @see hook_ENTITY_TYPE_delete()
Chris@0 1369 */
Chris@0 1370 function hook_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1371 // Delete the entity's entry from a fictional table of all entities.
Chris@18 1372 \Drupal::database()->delete('example_entity')
Chris@0 1373 ->condition('type', $entity->getEntityTypeId())
Chris@0 1374 ->condition('id', $entity->id())
Chris@0 1375 ->execute();
Chris@0 1376 }
Chris@0 1377
Chris@0 1378 /**
Chris@0 1379 * Respond to entity deletion of a particular type.
Chris@0 1380 *
Chris@0 1381 * This hook runs once the entity has been deleted from the storage.
Chris@0 1382 *
Chris@0 1383 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1384 * The entity object for the entity that has been deleted.
Chris@0 1385 *
Chris@0 1386 * @ingroup entity_crud
Chris@0 1387 * @see hook_entity_delete()
Chris@0 1388 */
Chris@0 1389 function hook_ENTITY_TYPE_delete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1390 // Delete the entity's entry from a fictional table of all entities.
Chris@18 1391 \Drupal::database()->delete('example_entity')
Chris@0 1392 ->condition('type', $entity->getEntityTypeId())
Chris@0 1393 ->condition('id', $entity->id())
Chris@0 1394 ->execute();
Chris@0 1395 }
Chris@0 1396
Chris@0 1397 /**
Chris@0 1398 * Respond to entity revision deletion.
Chris@0 1399 *
Chris@0 1400 * This hook runs once the entity revision has been deleted from the storage.
Chris@0 1401 *
Chris@0 1402 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1403 * The entity object for the entity revision that has been deleted.
Chris@0 1404 *
Chris@0 1405 * @ingroup entity_crud
Chris@0 1406 * @see hook_ENTITY_TYPE_revision_delete()
Chris@0 1407 */
Chris@0 1408 function hook_entity_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1409 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
Chris@0 1410 foreach ($referenced_files_by_field as $field => $uuids) {
Chris@0 1411 _editor_delete_file_usage($uuids, $entity, 1);
Chris@0 1412 }
Chris@0 1413 }
Chris@0 1414
Chris@0 1415 /**
Chris@0 1416 * Respond to entity revision deletion of a particular type.
Chris@0 1417 *
Chris@0 1418 * This hook runs once the entity revision has been deleted from the storage.
Chris@0 1419 *
Chris@0 1420 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1421 * The entity object for the entity revision that has been deleted.
Chris@0 1422 *
Chris@0 1423 * @ingroup entity_crud
Chris@0 1424 * @see hook_entity_revision_delete()
Chris@0 1425 */
Chris@0 1426 function hook_ENTITY_TYPE_revision_delete(Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1427 $referenced_files_by_field = _editor_get_file_uuids_by_field($entity);
Chris@0 1428 foreach ($referenced_files_by_field as $field => $uuids) {
Chris@0 1429 _editor_delete_file_usage($uuids, $entity, 1);
Chris@0 1430 }
Chris@0 1431 }
Chris@0 1432
Chris@0 1433 /**
Chris@0 1434 * Act on entities being assembled before rendering.
Chris@0 1435 *
Chris@0 1436 * @param &$build
Chris@0 1437 * A renderable array representing the entity content. The module may add
Chris@0 1438 * elements to $build prior to rendering. The structure of $build is a
Chris@16 1439 * renderable array as expected by
Chris@16 1440 * \Drupal\Core\Render\RendererInterface::render().
Chris@0 1441 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1442 * The entity object.
Chris@0 1443 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
Chris@0 1444 * The entity view display holding the display options configured for the
Chris@0 1445 * entity components.
Chris@0 1446 * @param $view_mode
Chris@0 1447 * The view mode the entity is rendered in.
Chris@0 1448 *
Chris@0 1449 * @see hook_entity_view_alter()
Chris@0 1450 * @see hook_ENTITY_TYPE_view()
Chris@0 1451 *
Chris@0 1452 * @ingroup entity_crud
Chris@0 1453 */
Chris@0 1454 function hook_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
Chris@0 1455 // Only do the extra work if the component is configured to be displayed.
Chris@0 1456 // This assumes a 'mymodule_addition' extra field has been defined for the
Chris@0 1457 // entity bundle in hook_entity_extra_field_info().
Chris@0 1458 if ($display->getComponent('mymodule_addition')) {
Chris@0 1459 $build['mymodule_addition'] = [
Chris@0 1460 '#markup' => mymodule_addition($entity),
Chris@0 1461 '#theme' => 'mymodule_my_additional_field',
Chris@0 1462 ];
Chris@0 1463 }
Chris@0 1464 }
Chris@0 1465
Chris@0 1466 /**
Chris@0 1467 * Act on entities of a particular type being assembled before rendering.
Chris@0 1468 *
Chris@0 1469 * @param &$build
Chris@0 1470 * A renderable array representing the entity content. The module may add
Chris@0 1471 * elements to $build prior to rendering. The structure of $build is a
Chris@16 1472 * renderable array as expected by
Chris@16 1473 * \Drupal\Core\Render\RendererInterface::render().
Chris@0 1474 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1475 * The entity object.
Chris@0 1476 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
Chris@0 1477 * The entity view display holding the display options configured for the
Chris@0 1478 * entity components.
Chris@0 1479 * @param $view_mode
Chris@0 1480 * The view mode the entity is rendered in.
Chris@0 1481 *
Chris@0 1482 * @see hook_ENTITY_TYPE_view_alter()
Chris@0 1483 * @see hook_entity_view()
Chris@0 1484 *
Chris@0 1485 * @ingroup entity_crud
Chris@0 1486 */
Chris@0 1487 function hook_ENTITY_TYPE_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
Chris@0 1488 // Only do the extra work if the component is configured to be displayed.
Chris@0 1489 // This assumes a 'mymodule_addition' extra field has been defined for the
Chris@0 1490 // entity bundle in hook_entity_extra_field_info().
Chris@0 1491 if ($display->getComponent('mymodule_addition')) {
Chris@0 1492 $build['mymodule_addition'] = [
Chris@0 1493 '#markup' => mymodule_addition($entity),
Chris@0 1494 '#theme' => 'mymodule_my_additional_field',
Chris@0 1495 ];
Chris@0 1496 }
Chris@0 1497 }
Chris@0 1498
Chris@0 1499 /**
Chris@0 1500 * Alter the results of the entity build array.
Chris@0 1501 *
Chris@0 1502 * This hook is called after the content has been assembled in a structured
Chris@0 1503 * array and may be used for doing processing which requires that the complete
Chris@0 1504 * entity content structure has been built.
Chris@0 1505 *
Chris@0 1506 * If a module wishes to act on the rendered HTML of the entity rather than the
Chris@0 1507 * structured content array, it may use this hook to add a #post_render
Chris@0 1508 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
Chris@0 1509 * the particular entity type template, if there is one (e.g., node.html.twig).
Chris@0 1510 *
Chris@0 1511 * See the @link themeable Default theme implementations topic @endlink and
Chris@0 1512 * drupal_render() for details.
Chris@0 1513 *
Chris@0 1514 * @param array &$build
Chris@0 1515 * A renderable array representing the entity content.
Chris@0 1516 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1517 * The entity object being rendered.
Chris@0 1518 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
Chris@0 1519 * The entity view display holding the display options configured for the
Chris@0 1520 * entity components.
Chris@0 1521 *
Chris@0 1522 * @ingroup entity_crud
Chris@0 1523 *
Chris@0 1524 * @see hook_entity_view()
Chris@0 1525 * @see hook_ENTITY_TYPE_view_alter()
Chris@0 1526 */
Chris@0 1527 function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
Chris@0 1528 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
Chris@0 1529 // Change its weight.
Chris@0 1530 $build['an_additional_field']['#weight'] = -10;
Chris@0 1531
Chris@0 1532 // Add a #post_render callback to act on the rendered HTML of the entity.
Chris@0 1533 $build['#post_render'][] = 'my_module_node_post_render';
Chris@0 1534 }
Chris@0 1535 }
Chris@0 1536
Chris@0 1537 /**
Chris@0 1538 * Alter the results of the entity build array for a particular entity type.
Chris@0 1539 *
Chris@0 1540 * This hook is called after the content has been assembled in a structured
Chris@0 1541 * array and may be used for doing processing which requires that the complete
Chris@0 1542 * entity content structure has been built.
Chris@0 1543 *
Chris@0 1544 * If a module wishes to act on the rendered HTML of the entity rather than the
Chris@0 1545 * structured content array, it may use this hook to add a #post_render
Chris@0 1546 * callback. Alternatively, it could also implement hook_preprocess_HOOK() for
Chris@0 1547 * the particular entity type template, if there is one (e.g., node.html.twig).
Chris@0 1548 *
Chris@0 1549 * See the @link themeable Default theme implementations topic @endlink and
Chris@0 1550 * drupal_render() for details.
Chris@0 1551 *
Chris@0 1552 * @param array &$build
Chris@0 1553 * A renderable array representing the entity content.
Chris@0 1554 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1555 * The entity object being rendered.
Chris@0 1556 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
Chris@0 1557 * The entity view display holding the display options configured for the
Chris@0 1558 * entity components.
Chris@0 1559 *
Chris@0 1560 * @ingroup entity_crud
Chris@0 1561 *
Chris@0 1562 * @see hook_ENTITY_TYPE_view()
Chris@0 1563 * @see hook_entity_view_alter()
Chris@0 1564 */
Chris@0 1565 function hook_ENTITY_TYPE_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
Chris@0 1566 if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) {
Chris@0 1567 // Change its weight.
Chris@0 1568 $build['an_additional_field']['#weight'] = -10;
Chris@0 1569
Chris@0 1570 // Add a #post_render callback to act on the rendered HTML of the entity.
Chris@0 1571 $build['#post_render'][] = 'my_module_node_post_render';
Chris@0 1572 }
Chris@0 1573 }
Chris@0 1574
Chris@0 1575 /**
Chris@0 1576 * Act on entities as they are being prepared for view.
Chris@0 1577 *
Chris@0 1578 * Allows you to operate on multiple entities as they are being prepared for
Chris@0 1579 * view. Only use this if attaching the data during the entity loading phase
Chris@0 1580 * is not appropriate, for example when attaching other 'entity' style objects.
Chris@0 1581 *
Chris@0 1582 * @param string $entity_type_id
Chris@0 1583 * The type of entities being viewed (i.e. node, user, comment).
Chris@0 1584 * @param array $entities
Chris@0 1585 * The entities keyed by entity ID.
Chris@0 1586 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] $displays
Chris@0 1587 * The array of entity view displays holding the display options configured
Chris@0 1588 * for the entity components, keyed by bundle name.
Chris@0 1589 * @param string $view_mode
Chris@0 1590 * The view mode.
Chris@0 1591 *
Chris@0 1592 * @ingroup entity_crud
Chris@0 1593 */
Chris@0 1594 function hook_entity_prepare_view($entity_type_id, array $entities, array $displays, $view_mode) {
Chris@0 1595 // Load a specific node into the user object for later theming.
Chris@0 1596 if (!empty($entities) && $entity_type_id == 'user') {
Chris@0 1597 // Only do the extra work if the component is configured to be
Chris@0 1598 // displayed. This assumes a 'mymodule_addition' extra field has been
Chris@0 1599 // defined for the entity bundle in hook_entity_extra_field_info().
Chris@0 1600 $ids = [];
Chris@0 1601 foreach ($entities as $id => $entity) {
Chris@0 1602 if ($displays[$entity->bundle()]->getComponent('mymodule_addition')) {
Chris@0 1603 $ids[] = $id;
Chris@0 1604 }
Chris@0 1605 }
Chris@0 1606 if ($ids) {
Chris@0 1607 $nodes = mymodule_get_user_nodes($ids);
Chris@0 1608 foreach ($ids as $id) {
Chris@0 1609 $entities[$id]->user_node = $nodes[$id];
Chris@0 1610 }
Chris@0 1611 }
Chris@0 1612 }
Chris@0 1613 }
Chris@0 1614
Chris@0 1615 /**
Chris@0 1616 * Change the view mode of an entity that is being displayed.
Chris@0 1617 *
Chris@0 1618 * @param string $view_mode
Chris@0 1619 * The view_mode that is to be used to display the entity.
Chris@0 1620 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1621 * The entity that is being viewed.
Chris@0 1622 * @param array $context
Chris@0 1623 * Array with additional context information, currently only contains the
Chris@0 1624 * langcode the entity is viewed in.
Chris@0 1625 *
Chris@0 1626 * @ingroup entity_crud
Chris@0 1627 */
Chris@0 1628 function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
Chris@0 1629 // For nodes, change the view mode when it is teaser.
Chris@0 1630 if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
Chris@0 1631 $view_mode = 'my_custom_view_mode';
Chris@0 1632 }
Chris@0 1633 }
Chris@0 1634
Chris@0 1635 /**
Chris@0 1636 * Alter entity renderable values before cache checking in drupal_render().
Chris@0 1637 *
Chris@0 1638 * Invoked for a specific entity type.
Chris@0 1639 *
Chris@0 1640 * The values in the #cache key of the renderable array are used to determine if
Chris@0 1641 * a cache entry exists for the entity's rendered output. Ideally only values
Chris@0 1642 * that pertain to caching should be altered in this hook.
Chris@0 1643 *
Chris@0 1644 * @param array &$build
Chris@0 1645 * A renderable array containing the entity's caching and view mode values.
Chris@0 1646 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1647 * The entity that is being viewed.
Chris@0 1648 * @param string $view_mode
Chris@0 1649 * The view_mode that is to be used to display the entity.
Chris@0 1650 *
Chris@16 1651 * @see \Drupal\Core\Render\RendererInterface::render()
Chris@0 1652 * @see \Drupal\Core\Entity\EntityViewBuilder
Chris@0 1653 * @see hook_entity_build_defaults_alter()
Chris@0 1654 *
Chris@0 1655 * @ingroup entity_crud
Chris@0 1656 */
Chris@0 1657 function hook_ENTITY_TYPE_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
Chris@0 1658
Chris@0 1659 }
Chris@0 1660
Chris@0 1661 /**
Chris@0 1662 * Alter entity renderable values before cache checking in drupal_render().
Chris@0 1663 *
Chris@0 1664 * The values in the #cache key of the renderable array are used to determine if
Chris@0 1665 * a cache entry exists for the entity's rendered output. Ideally only values
Chris@0 1666 * that pertain to caching should be altered in this hook.
Chris@0 1667 *
Chris@0 1668 * @param array &$build
Chris@0 1669 * A renderable array containing the entity's caching and view mode values.
Chris@0 1670 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1671 * The entity that is being viewed.
Chris@0 1672 * @param string $view_mode
Chris@0 1673 * The view_mode that is to be used to display the entity.
Chris@0 1674 *
Chris@16 1675 * @see \Drupal\Core\Render\RendererInterface::render()
Chris@0 1676 * @see \Drupal\Core\Entity\EntityViewBuilder
Chris@0 1677 * @see hook_ENTITY_TYPE_build_defaults_alter()
Chris@0 1678 *
Chris@0 1679 * @ingroup entity_crud
Chris@0 1680 */
Chris@0 1681 function hook_entity_build_defaults_alter(array &$build, \Drupal\Core\Entity\EntityInterface $entity, $view_mode) {
Chris@0 1682
Chris@0 1683 }
Chris@0 1684
Chris@0 1685 /**
Chris@0 1686 * Alter the settings used for displaying an entity.
Chris@0 1687 *
Chris@0 1688 * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
Chris@0 1689 * The entity view display that will be used to display the entity
Chris@0 1690 * components.
Chris@0 1691 * @param array $context
Chris@0 1692 * An associative array containing:
Chris@0 1693 * - entity_type: The entity type, e.g., 'node' or 'user'.
Chris@0 1694 * - bundle: The bundle, e.g., 'page' or 'article'.
Chris@0 1695 * - view_mode: The view mode, e.g., 'full', 'teaser', etc.
Chris@0 1696 *
Chris@0 1697 * @ingroup entity_crud
Chris@0 1698 */
Chris@0 1699 function hook_entity_view_display_alter(\Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, array $context) {
Chris@0 1700 // Leave field labels out of the search index.
Chris@0 1701 if ($context['entity_type'] == 'node' && $context['view_mode'] == 'search_index') {
Chris@0 1702 foreach ($display->getComponents() as $name => $options) {
Chris@0 1703 if (isset($options['label'])) {
Chris@0 1704 $options['label'] = 'hidden';
Chris@0 1705 $display->setComponent($name, $options);
Chris@0 1706 }
Chris@0 1707 }
Chris@0 1708 }
Chris@0 1709 }
Chris@0 1710
Chris@0 1711 /**
Chris@0 1712 * Alter the render array generated by an EntityDisplay for an entity.
Chris@0 1713 *
Chris@0 1714 * @param array $build
Chris@0 1715 * The renderable array generated by the EntityDisplay.
Chris@0 1716 * @param array $context
Chris@0 1717 * An associative array containing:
Chris@0 1718 * - entity: The entity being rendered.
Chris@0 1719 * - view_mode: The view mode; for example, 'full' or 'teaser'.
Chris@0 1720 * - display: The EntityDisplay holding the display options.
Chris@0 1721 *
Chris@0 1722 * @ingroup entity_crud
Chris@0 1723 */
Chris@0 1724 function hook_entity_display_build_alter(&$build, $context) {
Chris@0 1725 // Append RDF term mappings on displayed taxonomy links.
Chris@0 1726 foreach (Element::children($build) as $field_name) {
Chris@0 1727 $element = &$build[$field_name];
Chris@0 1728 if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') {
Chris@0 1729 foreach ($element['#items'] as $delta => $item) {
Chris@0 1730 $term = $item->entity;
Chris@0 1731 if (!empty($term->rdf_mapping['rdftype'])) {
Chris@0 1732 $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
Chris@0 1733 }
Chris@0 1734 if (!empty($term->rdf_mapping['name']['predicates'])) {
Chris@0 1735 $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
Chris@0 1736 }
Chris@0 1737 }
Chris@0 1738 }
Chris@0 1739 }
Chris@0 1740 }
Chris@0 1741
Chris@0 1742 /**
Chris@0 1743 * Acts on an entity object about to be shown on an entity form.
Chris@0 1744 *
Chris@0 1745 * This can be typically used to pre-fill entity values or change the form state
Chris@0 1746 * before the entity form is built. It is invoked just once when first building
Chris@0 1747 * the entity form. Rebuilds will not trigger a new invocation.
Chris@0 1748 *
Chris@0 1749 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1750 * The entity that is about to be shown on the form.
Chris@0 1751 * @param $operation
Chris@0 1752 * The current operation.
Chris@0 1753 * @param \Drupal\Core\Form\FormStateInterface $form_state
Chris@0 1754 * The current state of the form.
Chris@0 1755 *
Chris@0 1756 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
Chris@0 1757 * @see hook_ENTITY_TYPE_prepare_form()
Chris@0 1758 *
Chris@0 1759 * @ingroup entity_crud
Chris@0 1760 */
Chris@0 1761 function hook_entity_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
Chris@0 1762 if ($operation == 'edit') {
Chris@0 1763 $entity->label->value = 'Altered label';
Chris@0 1764 $form_state->set('label_altered', TRUE);
Chris@0 1765 }
Chris@0 1766 }
Chris@0 1767
Chris@0 1768 /**
Chris@0 1769 * Acts on a particular type of entity object about to be in an entity form.
Chris@0 1770 *
Chris@0 1771 * This can be typically used to pre-fill entity values or change the form state
Chris@0 1772 * before the entity form is built. It is invoked just once when first building
Chris@0 1773 * the entity form. Rebuilds will not trigger a new invocation.
Chris@0 1774 *
Chris@0 1775 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1776 * The entity that is about to be shown on the form.
Chris@0 1777 * @param $operation
Chris@0 1778 * The current operation.
Chris@0 1779 * @param \Drupal\Core\Form\FormStateInterface $form_state
Chris@0 1780 * The current state of the form.
Chris@0 1781 *
Chris@0 1782 * @see \Drupal\Core\Entity\EntityForm::prepareEntity()
Chris@0 1783 * @see hook_entity_prepare_form()
Chris@0 1784 *
Chris@0 1785 * @ingroup entity_crud
Chris@0 1786 */
Chris@0 1787 function hook_ENTITY_TYPE_prepare_form(\Drupal\Core\Entity\EntityInterface $entity, $operation, \Drupal\Core\Form\FormStateInterface $form_state) {
Chris@0 1788 if ($operation == 'edit') {
Chris@0 1789 $entity->label->value = 'Altered label';
Chris@0 1790 $form_state->set('label_altered', TRUE);
Chris@0 1791 }
Chris@0 1792 }
Chris@0 1793
Chris@0 1794 /**
Chris@0 1795 * Alter the settings used for displaying an entity form.
Chris@0 1796 *
Chris@0 1797 * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
Chris@0 1798 * The entity_form_display object that will be used to display the entity form
Chris@0 1799 * components.
Chris@0 1800 * @param array $context
Chris@0 1801 * An associative array containing:
Chris@0 1802 * - entity_type: The entity type, e.g., 'node' or 'user'.
Chris@0 1803 * - bundle: The bundle, e.g., 'page' or 'article'.
Chris@0 1804 * - form_mode: The form mode; e.g., 'default', 'profile', 'register', etc.
Chris@0 1805 *
Chris@0 1806 * @ingroup entity_crud
Chris@0 1807 */
Chris@0 1808 function hook_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display, array $context) {
Chris@0 1809 // Hide the 'user_picture' field from the register form.
Chris@0 1810 if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
Chris@0 1811 $form_display->setComponent('user_picture', [
Chris@0 1812 'region' => 'hidden',
Chris@0 1813 ]);
Chris@0 1814 }
Chris@0 1815 }
Chris@0 1816
Chris@0 1817 /**
Chris@0 1818 * Provides custom base field definitions for a content entity type.
Chris@0 1819 *
Chris@0 1820 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1821 * The entity type definition.
Chris@0 1822 *
Chris@0 1823 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
Chris@0 1824 * An array of field definitions, keyed by field name.
Chris@0 1825 *
Chris@0 1826 * @see hook_entity_base_field_info_alter()
Chris@0 1827 * @see hook_entity_bundle_field_info()
Chris@0 1828 * @see hook_entity_bundle_field_info_alter()
Chris@0 1829 * @see \Drupal\Core\Field\FieldDefinitionInterface
Chris@18 1830 * @see \Drupal\Core\Entity\EntityFieldManagerInterface::getFieldDefinitions()
Chris@0 1831 */
Chris@0 1832 function hook_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
Chris@0 1833 if ($entity_type->id() == 'node') {
Chris@0 1834 $fields = [];
Chris@0 1835 $fields['mymodule_text'] = BaseFieldDefinition::create('string')
Chris@0 1836 ->setLabel(t('The text'))
Chris@0 1837 ->setDescription(t('A text property added by mymodule.'))
Chris@0 1838 ->setComputed(TRUE)
Chris@0 1839 ->setClass('\Drupal\mymodule\EntityComputedText');
Chris@0 1840
Chris@0 1841 return $fields;
Chris@0 1842 }
Chris@0 1843 }
Chris@0 1844
Chris@0 1845 /**
Chris@0 1846 * Alter base field definitions for a content entity type.
Chris@0 1847 *
Chris@0 1848 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
Chris@0 1849 * The array of base field definitions for the entity type.
Chris@0 1850 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1851 * The entity type definition.
Chris@0 1852 *
Chris@0 1853 * @see hook_entity_base_field_info()
Chris@0 1854 * @see hook_entity_bundle_field_info()
Chris@0 1855 * @see hook_entity_bundle_field_info_alter()
Chris@0 1856 *
Chris@0 1857 * @todo WARNING: This hook will be changed in
Chris@0 1858 * https://www.drupal.org/node/2346329.
Chris@0 1859 */
Chris@0 1860 function hook_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
Chris@0 1861 // Alter the mymodule_text field to use a custom class.
Chris@0 1862 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
Chris@0 1863 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
Chris@0 1864 }
Chris@0 1865 }
Chris@0 1866
Chris@0 1867 /**
Chris@0 1868 * Provides field definitions for a specific bundle within an entity type.
Chris@0 1869 *
Chris@0 1870 * Bundle fields either have to override an existing base field, or need to
Chris@0 1871 * provide a field storage definition via hook_entity_field_storage_info()
Chris@0 1872 * unless they are computed.
Chris@0 1873 *
Chris@0 1874 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1875 * The entity type definition.
Chris@0 1876 * @param string $bundle
Chris@0 1877 * The bundle.
Chris@0 1878 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $base_field_definitions
Chris@0 1879 * The list of base field definitions for the entity type.
Chris@0 1880 *
Chris@0 1881 * @return \Drupal\Core\Field\FieldDefinitionInterface[]
Chris@0 1882 * An array of bundle field definitions, keyed by field name.
Chris@0 1883 *
Chris@0 1884 * @see hook_entity_base_field_info()
Chris@0 1885 * @see hook_entity_base_field_info_alter()
Chris@0 1886 * @see hook_entity_field_storage_info()
Chris@0 1887 * @see hook_entity_field_storage_info_alter()
Chris@0 1888 * @see hook_entity_bundle_field_info_alter()
Chris@0 1889 * @see \Drupal\Core\Field\FieldDefinitionInterface
Chris@18 1890 * @see \Drupal\Core\Field\FieldDefinition
Chris@18 1891 * @see \Drupal\Core\Entity\EntityFieldManagerInterface::getFieldDefinitions()
Chris@0 1892 *
Chris@0 1893 * @todo WARNING: This hook will be changed in
Chris@0 1894 * https://www.drupal.org/node/2346347.
Chris@0 1895 */
Chris@0 1896 function hook_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
Chris@0 1897 // Add a property only to nodes of the 'article' bundle.
Chris@0 1898 if ($entity_type->id() == 'node' && $bundle == 'article') {
Chris@0 1899 $fields = [];
Chris@18 1900 $storage_definitions = mymodule_entity_field_storage_info($entity_type);
Chris@18 1901 $fields['mymodule_bundle_field'] = FieldDefinition::createFromFieldStorageDefinition($storage_definitions['mymodule_bundle_field'])
Chris@18 1902 ->setLabel(t('Bundle Field'));
Chris@0 1903 return $fields;
Chris@0 1904 }
Chris@18 1905
Chris@0 1906 }
Chris@0 1907
Chris@0 1908 /**
Chris@0 1909 * Alter bundle field definitions.
Chris@0 1910 *
Chris@0 1911 * @param \Drupal\Core\Field\FieldDefinitionInterface[] $fields
Chris@0 1912 * The array of bundle field definitions.
Chris@0 1913 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1914 * The entity type definition.
Chris@0 1915 * @param string $bundle
Chris@0 1916 * The bundle.
Chris@0 1917 *
Chris@0 1918 * @see hook_entity_base_field_info()
Chris@0 1919 * @see hook_entity_base_field_info_alter()
Chris@0 1920 * @see hook_entity_bundle_field_info()
Chris@0 1921 *
Chris@0 1922 * @todo WARNING: This hook will be changed in
Chris@0 1923 * https://www.drupal.org/node/2346347.
Chris@0 1924 */
Chris@0 1925 function hook_entity_bundle_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle) {
Chris@0 1926 if ($entity_type->id() == 'node' && $bundle == 'article' && !empty($fields['mymodule_text'])) {
Chris@0 1927 // Alter the mymodule_text field to use a custom class.
Chris@0 1928 $fields['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
Chris@0 1929 }
Chris@0 1930 }
Chris@0 1931
Chris@0 1932 /**
Chris@0 1933 * Provides field storage definitions for a content entity type.
Chris@0 1934 *
Chris@0 1935 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1936 * The entity type definition.
Chris@0 1937 *
Chris@0 1938 * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
Chris@0 1939 * An array of field storage definitions, keyed by field name.
Chris@0 1940 *
Chris@0 1941 * @see hook_entity_field_storage_info_alter()
Chris@0 1942 * @see \Drupal\Core\Field\FieldStorageDefinitionInterface
Chris@0 1943 * @see \Drupal\Core\Entity\EntityManagerInterface::getFieldStorageDefinitions()
Chris@0 1944 */
Chris@0 1945 function hook_entity_field_storage_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
Chris@0 1946 if (\Drupal::entityManager()->getStorage($entity_type->id()) instanceof DynamicallyFieldableEntityStorageInterface) {
Chris@0 1947 // Query by filtering on the ID as this is more efficient than filtering
Chris@0 1948 // on the entity_type property directly.
Chris@0 1949 $ids = \Drupal::entityQuery('field_storage_config')
Chris@0 1950 ->condition('id', $entity_type->id() . '.', 'STARTS_WITH')
Chris@0 1951 ->execute();
Chris@0 1952 // Fetch all fields and key them by field name.
Chris@0 1953 $field_storages = FieldStorageConfig::loadMultiple($ids);
Chris@0 1954 $result = [];
Chris@0 1955 foreach ($field_storages as $field_storage) {
Chris@0 1956 $result[$field_storage->getName()] = $field_storage;
Chris@0 1957 }
Chris@0 1958
Chris@0 1959 return $result;
Chris@0 1960 }
Chris@0 1961 }
Chris@0 1962
Chris@0 1963 /**
Chris@0 1964 * Alter field storage definitions for a content entity type.
Chris@0 1965 *
Chris@0 1966 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fields
Chris@0 1967 * The array of field storage definitions for the entity type.
Chris@0 1968 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 1969 * The entity type definition.
Chris@0 1970 *
Chris@0 1971 * @see hook_entity_field_storage_info()
Chris@0 1972 */
Chris@0 1973 function hook_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
Chris@0 1974 // Alter the max_length setting.
Chris@0 1975 if ($entity_type->id() == 'node' && !empty($fields['mymodule_text'])) {
Chris@0 1976 $fields['mymodule_text']->setSetting('max_length', 128);
Chris@0 1977 }
Chris@0 1978 }
Chris@0 1979
Chris@0 1980 /**
Chris@0 1981 * Declares entity operations.
Chris@0 1982 *
Chris@0 1983 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 1984 * The entity on which the linked operations will be performed.
Chris@0 1985 *
Chris@0 1986 * @return array
Chris@0 1987 * An operations array as returned by
Chris@0 1988 * EntityListBuilderInterface::getOperations().
Chris@0 1989 *
Chris@0 1990 * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
Chris@0 1991 */
Chris@0 1992 function hook_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 1993 $operations = [];
Chris@0 1994 $operations['translate'] = [
Chris@0 1995 'title' => t('Translate'),
Chris@0 1996 'url' => \Drupal\Core\Url::fromRoute('foo_module.entity.translate'),
Chris@0 1997 'weight' => 50,
Chris@0 1998 ];
Chris@0 1999
Chris@0 2000 return $operations;
Chris@0 2001 }
Chris@0 2002
Chris@0 2003 /**
Chris@0 2004 * Alter entity operations.
Chris@0 2005 *
Chris@0 2006 * @param array $operations
Chris@0 2007 * Operations array as returned by
Chris@0 2008 * \Drupal\Core\Entity\EntityListBuilderInterface::getOperations().
Chris@0 2009 * @param \Drupal\Core\Entity\EntityInterface $entity
Chris@0 2010 * The entity on which the linked operations will be performed.
Chris@0 2011 */
Chris@0 2012 function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\EntityInterface $entity) {
Chris@0 2013 // Alter the title and weight.
Chris@0 2014 $operations['translate']['title'] = t('Translate @entity_type', [
Chris@0 2015 '@entity_type' => $entity->getEntityTypeId(),
Chris@0 2016 ]);
Chris@0 2017 $operations['translate']['weight'] = 99;
Chris@0 2018 }
Chris@0 2019
Chris@0 2020 /**
Chris@0 2021 * Control access to fields.
Chris@0 2022 *
Chris@0 2023 * This hook is invoked from
Chris@0 2024 * \Drupal\Core\Entity\EntityAccessControlHandler::fieldAccess() to let modules
Chris@0 2025 * grant or deny operations on fields.
Chris@0 2026 *
Chris@0 2027 * @param string $operation
Chris@0 2028 * The operation to be performed. See
Chris@0 2029 * \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
Chris@0 2030 * for possible values.
Chris@0 2031 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
Chris@0 2032 * The field definition.
Chris@0 2033 * @param \Drupal\Core\Session\AccountInterface $account
Chris@0 2034 * The user account to check.
Chris@0 2035 * @param \Drupal\Core\Field\FieldItemListInterface $items
Chris@16 2036 * (optional) The entity field object for which to check access, or NULL if
Chris@16 2037 * access is checked for the field definition, without any specific value
Chris@16 2038 * available. Defaults to NULL.
Chris@0 2039 *
Chris@0 2040 * @return \Drupal\Core\Access\AccessResultInterface
Chris@0 2041 * The access result.
Chris@0 2042 *
Chris@0 2043 * @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface::fieldAccess()
Chris@0 2044 */
Chris@0 2045 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 2046 if ($field_definition->getName() == 'field_of_interest' && $operation == 'edit') {
Chris@0 2047 return AccessResult::allowedIfHasPermission($account, 'update field of interest');
Chris@0 2048 }
Chris@0 2049 return AccessResult::neutral();
Chris@0 2050 }
Chris@0 2051
Chris@0 2052 /**
Chris@0 2053 * Alter the default access behavior for a given field.
Chris@0 2054 *
Chris@0 2055 * Use this hook to override access grants from another module. Note that the
Chris@0 2056 * original default access flag is masked under the ':default' key.
Chris@0 2057 *
Chris@0 2058 * @param \Drupal\Core\Access\AccessResultInterface[] $grants
Chris@0 2059 * An array of grants gathered by hook_entity_field_access(). The array is
Chris@0 2060 * keyed by the module that defines the field's access control; the values are
Chris@0 2061 * grant responses for each module (\Drupal\Core\Access\AccessResult).
Chris@0 2062 * @param array $context
Chris@0 2063 * Context array on the performed operation with the following keys:
Chris@0 2064 * - operation: The operation to be performed (string).
Chris@0 2065 * - field_definition: The field definition object
Chris@0 2066 * (\Drupal\Core\Field\FieldDefinitionInterface)
Chris@0 2067 * - account: The user account to check access for
Chris@0 2068 * (Drupal\user\Entity\User).
Chris@0 2069 * - items: (optional) The entity field items
Chris@0 2070 * (\Drupal\Core\Field\FieldItemListInterface).
Chris@0 2071 */
Chris@0 2072 function hook_entity_field_access_alter(array &$grants, array $context) {
Chris@0 2073 /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
Chris@0 2074 $field_definition = $context['field_definition'];
Chris@0 2075 if ($field_definition->getName() == 'field_of_interest' && $grants['node']->isForbidden()) {
Chris@0 2076 // Override node module's restriction to no opinion (neither allowed nor
Chris@0 2077 // forbidden). We don't want to provide our own access hook, we only want to
Chris@0 2078 // take out node module's part in the access handling of this field. We also
Chris@0 2079 // don't want to switch node module's grant to
Chris@0 2080 // AccessResultInterface::isAllowed() , because the grants of other modules
Chris@0 2081 // should still decide on their own if this field is accessible or not
Chris@0 2082 $grants['node'] = AccessResult::neutral()->inheritCacheability($grants['node']);
Chris@0 2083 }
Chris@0 2084 }
Chris@0 2085
Chris@0 2086 /**
Chris@0 2087 * Acts when initializing a fieldable entity object.
Chris@0 2088 *
Chris@0 2089 * This hook runs after a new entity object or a new entity translation object
Chris@0 2090 * has just been instantiated. It can be used to set initial values, e.g. to
Chris@0 2091 * provide defaults.
Chris@0 2092 *
Chris@0 2093 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
Chris@0 2094 * The entity object.
Chris@0 2095 *
Chris@0 2096 * @ingroup entity_crud
Chris@0 2097 * @see hook_ENTITY_TYPE_field_values_init()
Chris@0 2098 */
Chris@0 2099 function hook_entity_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
Chris@0 2100 if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface && !$entity->foo->value) {
Chris@0 2101 $entity->foo->value = 'some_initial_value';
Chris@0 2102 }
Chris@0 2103 }
Chris@0 2104
Chris@0 2105 /**
Chris@0 2106 * Acts when initializing a fieldable entity object.
Chris@0 2107 *
Chris@0 2108 * This hook runs after a new entity object or a new entity translation object
Chris@0 2109 * has just been instantiated. It can be used to set initial values, e.g. to
Chris@0 2110 * provide defaults.
Chris@0 2111 *
Chris@0 2112 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
Chris@0 2113 * The entity object.
Chris@0 2114 *
Chris@0 2115 * @ingroup entity_crud
Chris@0 2116 * @see hook_entity_field_values_init()
Chris@0 2117 */
Chris@0 2118 function hook_ENTITY_TYPE_field_values_init(\Drupal\Core\Entity\FieldableEntityInterface $entity) {
Chris@0 2119 if (!$entity->foo->value) {
Chris@0 2120 $entity->foo->value = 'some_initial_value';
Chris@0 2121 }
Chris@0 2122 }
Chris@0 2123
Chris@0 2124 /**
Chris@0 2125 * Exposes "pseudo-field" components on content entities.
Chris@0 2126 *
Chris@0 2127 * Field UI's "Manage fields" and "Manage display" pages let users re-order
Chris@0 2128 * fields, but also non-field components. For nodes, these include elements
Chris@0 2129 * exposed by modules through hook_form_alter(), for instance.
Chris@0 2130 *
Chris@0 2131 * Content entities or modules that want to have their components supported
Chris@0 2132 * should expose them using this hook. The user-defined settings (weight,
Chris@0 2133 * visible) are automatically applied when entities or entity forms are
Chris@0 2134 * rendered.
Chris@0 2135 *
Chris@0 2136 * @see hook_entity_extra_field_info_alter()
Chris@0 2137 *
Chris@0 2138 * @return array
Chris@0 2139 * The array structure is identical to that of the return value of
Chris@0 2140 * \Drupal\Core\Entity\EntityFieldManagerInterface::getExtraFields().
Chris@0 2141 */
Chris@0 2142 function hook_entity_extra_field_info() {
Chris@0 2143 $extra = [];
Chris@0 2144 $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
Chris@0 2145 $description = t('Node module element');
Chris@0 2146
Chris@0 2147 foreach (NodeType::loadMultiple() as $bundle) {
Chris@0 2148
Chris@0 2149 // Add also the 'language' select if Language module is enabled and the
Chris@0 2150 // bundle has multilingual support.
Chris@0 2151 // Visibility of the ordering of the language selector is the same as on the
Chris@0 2152 // node/add form.
Chris@0 2153 if ($module_language_enabled) {
Chris@0 2154 $configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
Chris@0 2155 if ($configuration->isLanguageAlterable()) {
Chris@0 2156 $extra['node'][$bundle->id()]['form']['language'] = [
Chris@0 2157 'label' => t('Language'),
Chris@0 2158 'description' => $description,
Chris@0 2159 'weight' => 0,
Chris@0 2160 ];
Chris@0 2161 }
Chris@0 2162 }
Chris@0 2163 $extra['node'][$bundle->id()]['display']['language'] = [
Chris@0 2164 'label' => t('Language'),
Chris@0 2165 'description' => $description,
Chris@0 2166 'weight' => 0,
Chris@0 2167 'visible' => FALSE,
Chris@0 2168 ];
Chris@0 2169 }
Chris@0 2170
Chris@0 2171 return $extra;
Chris@0 2172 }
Chris@0 2173
Chris@0 2174 /**
Chris@0 2175 * Alter "pseudo-field" components on content entities.
Chris@0 2176 *
Chris@0 2177 * @param array $info
Chris@0 2178 * The array structure is identical to that of the return value of
Chris@0 2179 * \Drupal\Core\Entity\EntityManagerInterface::getExtraFields().
Chris@0 2180 *
Chris@0 2181 * @see hook_entity_extra_field_info()
Chris@0 2182 */
Chris@0 2183 function hook_entity_extra_field_info_alter(&$info) {
Chris@0 2184 // Force node title to always be at the top of the list by default.
Chris@0 2185 foreach (NodeType::loadMultiple() as $bundle) {
Chris@0 2186 if (isset($info['node'][$bundle->id()]['form']['title'])) {
Chris@0 2187 $info['node'][$bundle->id()]['form']['title']['weight'] = -20;
Chris@0 2188 }
Chris@0 2189 }
Chris@0 2190 }
Chris@0 2191
Chris@0 2192 /**
Chris@0 2193 * @} End of "addtogroup hooks".
Chris@0 2194 */