Typed Data API overview

Last updated on
22 February 2024

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

Overview

The Typed Data API was created to provide developers with a consistent way of interacting with data in different ways. Not only does the API allow you to interact with the actual data, it also provides means of fetching more information, or metadata, about the actual data.

The Typed Data API is a low level, generic and reusable object oriented API that appears at multiple levels of the Drupal 8+ architecture. Take for example, the EntityAdapter, which extends TypedData and acts as a wrapper for an Entity. Or FieldItemBase, which is an unwrapped extension of TypedData.

Why do we need the Typed Data API?

Unlike many other languages, PHP is a very loosely typed language. It doesn’t have a clear definition of the different types of data it deals with. Therefore Drupal used to be the same. For example, there was no consistent way of telling if a field value is a string, an integer or a timestamp. Or even, to tell if something is translatable or accessible (as in permissions). Even if you as a developer happen to know that the value of a text field happens to be a string, there’s no consistent programmatic way of fetching this information. This creates a lot of problems when building machine readable APIs on Drupal that exposes its data to other systems.

There needs to be a consistent way of typing this data, or describing the data.

The basic API

The Typed Data API mainly provides three different interfaces. Below you will find a description of these interfaces and some important methods (not a comprehensive list).

  • ComplexDataInterface

    Implementations of this interface are used for data that is composed of named properties with more pieces of data. This interface defines some basic methods, including:

    get($property_name)

    Fetch the value of a property.

    set($property_name, $value)

    Set the value of a property. The $value must be an instance of one of these three interfaces.

  • ListInterface

    Implementations of this interface are used for something that is composed of a sequential list of other things, for instance a list of other complex pieces of data. Lists are ordered and may contain duplicate items, e.g. a multiple node reference field could contain the same reference two times.
    This interface extends the ArrayAccess interface.

    offsetGet($index)

    Method inherited from ArrayAccess, so individual items may be retrieved as from arrays:

      $first_item = $items[0];
    
  • TypedDataInterface

    Implementations of this interface are used for something that represents a single piece of typed data, like a string, integer etc. This is the smallest building block in the Typed Data API, and it can definitely be used as is. The interface defines some basic methods, including:

    getValue()

    Fetches the value of the data.

    setValue($value)

    Sets the value of the data.

    getDefinition()

    Fetches information about the data telling you if it’s of a primitive type etc.

Using the API

[To be reviewed]

A Typed Data object can be created with a Data Definition for a given Data Type.

The Typed Data Manager service allows you to create Typed Data objects, and is the preferred way to instantiate primitive or Map-based Typed Data. You should use the Entity Manager or Field Plugin Manager for those respectively.

  // Create a data definition for the data type plugin.
  $definition = \Drupal::typedDataManager()->createDataDefinition('uri');
  // Create the Typed Data object for the data definition with the Typed Data Manager service. Note that the "typed_data_manager" service should be injected into your own class.
  $uri = \Drupal::typedDataManager()->create($definition);
  // Set some value on the object.
  $uri->setValue('http://www.example.com');

The Typed Data Manager service also allows to create an IteratorAggregate object of Typed Data objects using the List Data Definition class.

  $listDefinition = \Drupal::typedDataManager()->createListDataDefinition('uri');
  $urls = \Drupal::typedDataManager()->create($listDefinition, ["http://example.com", "http://drupal.org"]);

  // Get a specific index value.
  $exampleurl = $urls->get(0)->getValue();

  // Iterate over each item in the list.
  foreach ($urls as $url) {
    // Do something with the Uri Typed Data.
  }

The Data Definition class can set constraints upon the Typed Data object.

  $definition = \Drupal::typedDataManager()->createDataDefinition('uri');
  // Add a Symfony Choice constraint to this data definition.
  $definition->addConstraint('Choice', ['choices' => ['http://example.com']]);
  $uri = \Drupal::typedDataManager()->create($definition, 'http://drupal.org');
  
  $errors = $uri->validate();
  if (!empty($errors)) {
    // Iterate through \Symfony\Component\Validator\ConstraintViolationListInterface.
    foreach ($errors as $error) {
       // $error->getMessage();
    }
  }

The Typed Data API can be used with the Serialization API to serialize Drupal data types into JSON or XML easily.

  // Serialize Typed Data into JSON.
  $listDefinition = \Drupal::typedDataManager()->createListDataDefinition('uri');
  $list = \Drupal::typedDataManager()->create($listDefinition, ['http://example.com', 'http://drupal.org']);
  $serializer = \Drupal::service('serializer');
  echo $serializer->serialize($list, 'json');
  // ["http://example.com", "http://drupal.org"]

  // Deserialize JSON into Typed Data.
  $encoded = json_encode(["http://example.com", "http://drupal.org"]);
  $myList = $serializer->deserialize($encoded, 'Drupal\Core\TypedData\Plugin\DataType\Uri', 'json', ['plugin_id' => 'uri']);
  echo $myList->get(0)->getValue();
  // 'http://example.com'

What a definition looks like

[To be done]

Entity API

The Drupal Entity API is built on the Typed Data API.

Help improve this page

Page status: Needs work

You can: