Entity API implements Typed Data API

Last updated on
5 May 2022

This documentation needs review. See "Help improve this page" in the sidebar.

Understanding Drupal's data model

 

First, before we dive into the Typed Data API itself, we must understand how Drupal’s data model (Entity API) used to be perceived. This is important because this is where the Typed Data API is coming from, and the Entity API is one of the systems it was designed for.

An entity is a complex piece of data - composed of other pieces of data, like fields with a list of items. A field item is also complex - composed of more pieces of data, like text value and input format. However, the complexity only goes so far, until something can be described as some sort of primitive data type like string or integer.

A simplified example from Drupal 7 (the example is without language key because Drupal 8 handles that differently):

Figure 1

// Entities are complex, they contain other pieces of data.
$entity;

// Fields are not complex, they only contain a list of items.
$entity->image;

// Items are complex, they contain other pieces of data. They’re also translatable and accessible (has permissions).
$entity->image[0];

// The file ID is a primitive integer.
$entity->image[0][‘fid’];

// The alternative text is a primitive string.
$entity->image[0][‘alt’];

Putting it all together

Below is a simplified example of how Entity API is implementing the different interfaces from the Typed Data API. In reality, Entity API extends these interfaces adding additional methods that Entity API needs. Still, all the below statements evaluate to true:

Figure 2

// Entities are complex.
$entity instanceof ComplexDataInterface;

// Properties are not complex, they’re only a list of items.
$entity->get('image') instanceof ListInterface;

// Items are complex.
$entity->get('image')->offsetGet(0) instanceof ComplexDataInterface;

// The typed data object representing the alt value.
$entity->get('image')->offsetGet(0)->get('alt') instanceof TypedDataInterface;

// The alt value is a primitive string.
is_string($entity->get('image')->offsetGet(0)->get('alt')->getValue());

Below follows a brief overview of how Entity API extends the Typed Data API to accommodate some additional needs:

Figure 3

interface EntityInterface extends ComplexDataInterface, TranslatableInterface, AccessibleInterface {
 // ...
}

interface FielditemListInterface extends ListInterface {
 // ...
}

// Note that this extends two interfaces. Explanation below.
interface FieldItemInterface extends ComplexDataInterface, TypedDataInterface {
 // ...
}

// Below follows some actual implementations.

// Extends an abstract class with some common logic.
class ImageItem extends FieldItemBase {
 // ...
}

// Extends an abstract class with some common logic.
class String extends TypedData {
 // ...
}

[The following two paragraphs needs more work]

The two most notable things above are that:

  1. EntityInterface extends some utility interfaces for things like translation and access abilities. This should be pretty self-explanatory.
  2. FieldItemInterface extends both ComplexDataInterface and TypedDataInterface. As explained earlier, items are complex in the sense that they contain more pieces of data (like text value and format for text items). But at the same time, an item is a piece of typed data itself, thus having its own definition and data type.

So to sum this up, in addition to Figure 2, all the below statements evaluate to true as well:

Figure 4

$entity instanceof EntityInterface;

$entity->get('image') instanceof FieldItemListInterface;

$entity->get('image')->offsetGet(0) instanceof FieldItemInterface;

$entity->get('image')->offsetGet(0)->get('alt') instanceof String;

is_string($entity->get('image')->offsetGet(0)->get('alt')->getValue());

Using the API

[This section needs some more examples]

Entity API defines some magic methods, like __get(), to allow for fast and easy access to field values. So using the API is very straightforward and the syntax reminds a lot of the pre Drupal 8 era.

Fetching the actual value of the image’s alternative text will be done as below:

Figure 5

// The most verbose way.
$string = $entity->get('image')->offsetGet(0)->get('alt')->getValue();

// With magic added by the Entity API.
$string = $entity->image[0]->alt;

// With more magic added by Entity API, to fetch the first item
// in the list by default.
$string = $entity->image->alt;

The above example only adds some nice syntax to the old API. The below examples demonstrates where the real value of this API comes in - inspecting the data:

Figure 6

// Returns an array with named keys for all fields and their
// definitions. For example the ‘image’ field.
$field_definitions = $entity->getFieldDefinitions();

// Returns an array with name keys for all properties and their
// definitions. For example the ‘file_id’ and ‘alt’ properties.
$property_definitions = $entity->image
  ->getFieldDefinition()
  ->getFieldStorageDefinition()
  ->getPropertyDefinitions();

// Returns only definition for the ‘alt’ property.
$string_definition = $entity->image
  ->getFieldDefinition()
  ->getFieldStorageDefinition()
  ->getPropertyDefinition('alt');

Based on the definitions we fetched above, we can now do clever things like serialization or other data massaging. We can also expose this data over semantically rich APIs, such as an JSON-LD endpoint so that other systems can understand the essentials of our data.

See https://drupal.org/node/2078241 on more information about how to define and use field definitions of an entity type.

Help improve this page

Page status: Needs review

You can: