Skip to content
This repository has been archived by the owner on Dec 1, 2021. It is now read-only.

Latest commit

 

History

History
236 lines (200 loc) · 9.12 KB

4.4-essential-apis-entity.md

File metadata and controls

236 lines (200 loc) · 9.12 KB

Entity API

Entities are the fundamental building blocks of the Drupal system. Almost every component is an entity or relies on entities to work. Examples of entities are Nodes, Users, Blocks, Menus, etc...

In Drupal 8, there are two entity variants:

  • Configuration entities: Used by the Configuration System. Supports translations and can provide custom defaults for installations.
  • Content entities: Consist of configurable and base fields, can have revisions and support translations.

Entities can also have subtypes, known as bundles.

Entity Types

Entity classes live in \Drupal\[module_name]\Entity, meaning they should be found in src/Entity. The class docblock must use an EntityType annotation (including ContentEntityType and ConfigEntityType).

For example, core/modules/node/src/Entity/Node.php is responsible for creating nodes:

<?php
/**
 * Defines the node entity class.
 *
 * @ContentEntityType(
 *   id = "node",
 *   label = @Translation("Content"),
 *   label_singular = @Translation("content item"),
 *   label_plural = @Translation("content items"),
 *   label_count = @PluralTranslation(
 *     singular = "@count content item",
 *     plural = "@count content items"
 *   ),
 *   bundle_label = @Translation("Content type"),
 *   handlers = {
 *     "storage" = "Drupal\node\NodeStorage",
 *     "storage_schema" = "Drupal\node\NodeStorageSchema",
 *     "view_builder" = "Drupal\node\NodeViewBuilder",
 *     "access" = "Drupal\node\NodeAccessControlHandler",
 *     "views_data" = "Drupal\node\NodeViewsData",
 *     "form" = {
 *       "default" = "Drupal\node\NodeForm",
 *       "delete" = "Drupal\node\Form\NodeDeleteForm",
 *       "edit" = "Drupal\node\NodeForm"
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\node\Entity\NodeRouteProvider",
 *     },
 *     "list_builder" = "Drupal\node\NodeListBuilder",
 *     "translation" = "Drupal\node\NodeTranslationHandler"
 *   },
 *   base_table = "node",
 *   data_table = "node_field_data",
 *   revision_table = "node_revision",
 *   revision_data_table = "node_field_revision",
 *   translatable = TRUE,
 *   list_cache_contexts = { "user.node_grants:view" },
 *   entity_keys = {
 *     "id" = "nid",
 *     "revision" = "vid",
 *     "bundle" = "type",
 *     "label" = "title",
 *     "langcode" = "langcode",
 *     "uuid" = "uuid",
 *     "status" = "status",
 *     "uid" = "uid",
 *   },
 *   bundle_entity_type = "node_type",
 *   field_ui_base_route = "entity.node_type.edit_form",
 *   common_reference_target = TRUE,
 *   permission_granularity = "bundle",
 *   links = {
 *     "canonical" = "/node/{node}",
 *     "delete-form" = "/node/{node}/delete",
 *     "edit-form" = "/node/{node}/edit",
 *     "version-history" = "/node/{node}/revisions",
 *     "revision" = "/node/{node}/revisions/{node_revision}/view",
 *   }
 * )
 */
class Node extends ContentEntityBase implements NodeInterface {
  // ...
}
?>

From Entity Types:

Entity type names should be prefixed with the module name if the entity type and module name aren't the same.

Drupal 8 recommends you type hint functions and methods with interfaces instead of classes. For example, the generic entity storage hooks type hint with EntityInterface as in hook_entity_insert(EntityInterface $entity) and the node specific storage hooks type hint with NodeInterface as in hook_node_insert(NodeInterface $node).

Entity fields / properties are often very short, storage-centric and not very self-descriptive. Additionally, content entities don't use defined properties for their fields (including base fields like the node title) at all.

  • Methods usually have a get/set/is or similar prefix: getSomething(), setSomething($value), isSomething()
  • Only add methods for things that other code is supposed to change. The last changed date of nodes ($node->changed) isn't supposed to be changed, so there is $node->getChangedTime() but no $node->setChangedTime() method.
  • Use self-descriptive method names, for example, the method to access $node->status is called $node->isPublished().

Working with Entity API

Following examples from Entity API:

<?php
// Make sure that an object is an entity.
if ($object instanceof \Drupal\Core\Entity\EntityInterface) {
}

// Make sure it's a content entity.
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
}

// Get the entity type.
$entity->getEntityTypeId();

// Make sure it's a node.
if ($entity instanceof \Drupal\node\NodeInterface) {
}

// Using entityType() works better when the needed entity type is dynamic.
$needed_type = 'node';
if ($entity->getEntityTypeId() == $needed_type) {
}

// Get the ID.
$entity->id();

// Get the bundle.
$entity->bundle();

// Check if the entity is new.
$entity->isNew();

// Get the label of an entity. Replacement for entity_label().
$entity->label().

// Get the URI for an entity.
// @todo: This might still change with the new URI template API.
$entity->uri();

// Create a duplicate that can be saved as a new entity.
$duplicate = $entity->createDuplicate();

// Use the procedural wrapper.
$node = entity_create('node', [
  'title' => 'My node',
  'body' => 'The body content. This just works like this due to the new Entity Field
          API. It will be assigned as the value of the first field item in the
          default language.',
]);

// Or you can use the static create() method if you know
// the entity class::
$node = Node::create(['title' => 'The node title']);

// Use the entity manager.
$node = \Drupal::entityTypeManager()->getStorage('node')->create(['type' => 'article', 'title' => 'Another node']);

// To save an entity.
$entity->save();

// The following will attempt to insert a new node with the ID 5, this will fail if that node already exists.
$node->nid->value = 5;
$node->enforceIsNew(TRUE);
$node->save();

// Delete a single entity.
$entity = \Drupal::entityTypeManager()->getStorage('node')->load(1);
$entity->delete();

// Delete multiple entities at once.
\Drupal::entityTypeManager()->getStorage($entity_type)->delete([$id1 => $entity1, $id2 => $entity2]);

// Check view access of an entity.
// This defaults to check access for the currently logged in user.
if ($entity->access('view')) {

}

// Check if a given user can delete an entity.
if ($entity->access('delete', $account)) {

}

// Update a field value in a ContentEntity.
$custom_field_value = $Custom_Entity->custom_field->value;
// Perform some kind of data manipulation
$Custom_Entity->custom_field->value = $custom_field_value;
$Custom_Entity->save();
?>

Entity Validation

From drupal.org - Entity Validation API overview:

In Drupal 8 Entity validation is moved to a separate Entity validation API and decoupled from form validation. Decoupling entity validation from forms allows validation entities to be independent of form submissions, such as when changed via the RESTful web service. This new validation API has been implemented based on the Symfony validator.

Overview

Validation is controlled by a set of constraints, such as the maximum length of some text or restriction to a set of allowed values. Symfony comes with many useful constraints, which Drupal extends with more Drupal-specific constraints. Drupal Symfony validator has been integrated with the Typed Data API, such that validation constraints can be used and specified as part of Entity field definitions and, generally, any typed data definition.

Using the API

Validation can be invoked by calling the validate() method on any typed data object as in the following example:

    $definition = DataDefinition::create('integer')
      ->addConstraint('Range', ['min' => 5]);
    $typed_data = \Drupal::typedDataManager()->create($definition, 10);
    $violations = $typed_data->validate();

This returns a list of violations if passed an empty validation:

  if ($violations->count() > 0) {
    // Validation failed.
  }

Validating entities

Entity fields and field items are typed data objects, and can be validated as in this example:

$violations = $entity->field_text->validate();
Here is an example of validating an entity as a whole:

$violations = $entity->validate();

Putting constraints on field item properties:

Entity field definitions ease putting constraints on individual field item properties via the setPropertyConstraints() method. In the following example, the field definition puts a maximum length constraint on the field item's value property ($field[0]->value):

$fields['name'] = BaseFieldDefinition::create('string')
  ->setLabel(t('Name'))
  ->setPropertyConstraints('value', ['Length' => ['max' => 32]]);

Additional Resources