So, we have our entity loaded and we can now read its data. For content entities, this is where the TypedData knowledge comes into play. Before we look at that, let's see quickly how we can get the data from configuration entities. Let's inspect the Article NodeType for this purpose:
/** @var \Drupal\node\Entity\NodeType $type */
$type = \Drupal::entityTypeManager()->getStorage('node_type')->load('article');
The first and simplest thing we can do is inspect the individual methods on the entity type class. For example, NodeType has a getDescription() method which is a handy helper to get the description field:
$description = $type->getDescription();
This is always the best way to try to get the field values of configuration entities, because you potentially get return type documentation that can come in handy with your IDE. Alternatively, the ConfigEntityBase class has the get() method that can be used to access any of the fields:
$description = $type->get('description');
This is going to do the same thing and it is the common way any field can be accessed across the different configuration entity types. The resulting value is the raw field value, in this case a string. So, this is pretty simple.
Apart from the typical field data, we have the entity keys (if you remember from the entity type plugin definitions). These are common for both configuration and content entities and the relevant accessor methods are found on the EntityInterface. Here are some of the more common ones:
$id = $type->id();
$label = $type->label();
$uuid = $type->uuid();
$bundle = $type->bundle();
$language = $type->language();
The resulting information naturally depends on the entity type. For example, configuration entities don't have bundles or some content entity types either. So, the bundle() method will return the name of the entity type if there are no bundles. By far the most important one is id() but you will often use label() as well as a shortcut to the primitive field value of the field used as the label for the entity type. There are other entity keys as well that individual entity types can declare. For example, entity types that extend the EditorialContentEntityBase, such as the Node entity, have a published entity key and a corresponding isPublished() method. So, for any other entity keys, do check the respective entity type if you can use them.
Some extra methods you can use to inspect entities of any type:
- isNew() checks whether the entity has been persisted already.
- getEntityTypeId() returns the machine name of the entity type of the entity.
- getEntityType() returns the EntityTypeInterface plugin of the given entity.
- getTypedData() returns the EntityAdapter DataType plugin instance that wraps the entity. It can be used for further inspection as well as validation;
Moreover, we can also check whether they are a content or a configuration entity:
$entity instanceof ContentEntityInterface
$entity instanceof ConfigEntityInterface
Similarly, we can also check whether they are a specific type of entity:
$entity instanceof NodeInterface
This is similar to using $entity->getEntityTypeId === 'node' but it is much more explicit and clear, plus the IDE can benefit from the information in many cases.
Now, let's turn to content entities and see how we can read their field data.
Similar to configuration entity types, many content entity types can have helper methods on their class (or parent) to make accessing certain fields easier. For example, the Node entity type has the getTitle() method which gets the first primitive value of its title field. However, let's see how we can apply what we learned in the TypedData section and navigate through the field values like a pro. To exemplify, we will examine a simple article node.
Content entities also have the get() method, but unlike configuration entities, it doesn't return the raw field value. Instead, it returns an instance of FieldItemList:
/** @var \Drupal\node\NodeInterface $node */
$node = Node::load(1);
/** @var \Drupal\Core\Field\FieldItemListInterface $title */
$title = $node->get('title');
For quick prototyping, in this example I used the static load() method on the content entity class to load an entity by ID. Under the hood, this will delegate to the relevant storage class. This is a quick alternative to using the entity manager, but you should only rely on it wherever you cannot inject dependencies.
Here are some of the things we can learn about the title FieldItemList:
$parent = $title->getParent();
This is its parent (the DataType plugin it belongs in, in this case, the EntityAdapter):
$definition = $title->getFieldDefinition();
This is the DataDefinitionInterface of the list. In this case it's a BaseFieldDefinition instance but can be a BaseFieldOverride or a FieldConfig for fully configurable fields:
$item_definition = $title->getItemDefinition();
This is the DataDefinitionInterface for the individual items in the list, typically a FieldItemDataDefinition:
$total = $title->count();
$empty = $title->isEmpty();
$exists = $title->offsetExists(1);
These are some handy methods for inspecting the list. We can see how many items there are in it, whether it's empty, and whether there are any values at a given offset. Do keep in mind that value keys start at 0, so if the cardinality of the field is 1, the value will be at the key 0.
To retrieve values from the list, we have a number of options. The most common thing you'll end up doing is the following:
$value = $title->value;
This is a magic property pointing to the first primitive value in the list. However, it's very important to note that, although most fields use the value property, some fields have a different property name. For example, entity reference fields use target_id:
$id = $field->target_id;
This returns the ID of the referenced entity. As an added bonus, if you use the magic entity property, you get the fully loaded entity object:
$entity = $field->entity;
But enough of this magic way of doing things; let's see what other options we have:
$value = $title->getValue();
The getValue() method is present on all TypedData objects and returns the raw values that it stores. In our case, it will return an array with one item (since we only have one item in the list) that contains the individual item raw values. Which in this case is an array with one element keyed value and the title string as its actual value. We will see in a moment why this is keyed value.
In some cases, we might want this to be returned and can find it useful. In other cases though, we might just want the one field value. For this, we can ask for a given item in the list:
$item = $title->get(0);
$item = $title->offsetGet(0);
Both of these do the same thing and return a FieldType plugin which, as we saw, extends FieldItemBase, which is nothing more than a fancy Map DataType plugin. Once we have this, we again have a few choices:
$value = $item->getValue();
This again returns an array of the raw values, in this case with one key called value and the string title as the actual value. So, just as we called getValue() on the list, but this time returning the raw values of only one item instead of an array of raw values of multiple items.
The reason why we have the actual title string keyed by value is because we are requesting the raw value from the StringItem field type plugin, which in this case happens to define the value columns as value. Others might differ (for example the entity reference field that stores a target_id named value).
Alternatively, again, we can navigate a bit further down:
$data = $item->get('value');
We know that this field uses the name value for its property so we can use the get() method from the Map DataType (which, if you remember, is subclassed by the StringItem field type) to retrieve its own property by name. This is exactly the same as we did with the license plate map and when we requested the number or state code. In the case of StringItem field types, this is going to be a StringData DataType plugin.
And as we did before, we can ask this final plugin for its value:
$value = $data->getValue();
Now we have the final string for the title. Of course, all the way down from the top, we have the opportunity to inspect the definitions of each of these plugins and learn more information about them.
Typically, on the day to day, you will use two methods for retrieving values from fields, depending on the cardinality. If the field has only one value, you will end up using something like this:
$title = $node->get('title')->value;
$id = $node->get('field_referencing_some_entity')->target_id;
$entity = $node->get('field_referencing_some_entity')->entity;
If the field can have multiple values, you will end up using something like this:
$names = $node->get('field_names')->getValue();
$tags = $node->get('field_tags')->referencedEntities();
The referencedEntities() method is a helper one provided by EntityReferenceFieldItemList (which is a subclass of FieldItemList) that loads all the referenced entities and returns them in an array keyed by the position in the field (the delta).