Access from where
As mentioned, it matters from where you are trying to access a field value because this will determine how you can query and load the entity it is attached to. When accessing from object oriented code, you should always try to inject a service when you can. In procedural code (a hook for instance) this is not possible and loading will have to be called statically if the entity you need was not passed in.
What type of data
Before Drupal 8 was released there was no consistent way of telling if a field value is for instance a string, an integer or a timestamp. This created a lot of problems when building machine readable APIs on Drupal that expose its data to other systems, one of the major promises of Drupal 8 and later. A solution was needed for a consistent way of typing this data, or describing the data. This is where the Typed Data API comes in. The Typed Data API for a large part determines how you can access data of a specific type, let’s see what that means.
One way to access a field value then looks like:
use Drupal\node\Entity\Node $node = Node::load($nid); $title_field = $node->get('title'); $title = $title_field->value;
The first 2 lines provide us with a loaded node object to work with in the example. To keep focus, let’s assume from her on that we already have a node loaded or passed in. As mentioned in the beginning of this article there are several ways to query and load nodes (or other entities), we will get into that in another article. After loading the needed node(s) it no longer matters from where you are trying to access the field value, the code is the same.
So what happens on line 3 and 4 of the example above? On line 3 we first use the get() method to load an instance of FieldItemList into a variable. The get() method is present on all content entities, we use the field name of the data we want to access to call this method. On line 4 we then call the value property on the loaded field object in order to load the actual title string into a variable. It is important to realise that value is a so-called magic property pointing to the first primitive value in the list that the instance of FieldItemList gave us.
Magic properties are very handy but it is important to be careful when using them because they somewhat obscure what is going on beneath the surface. On top of that, although most fields use the value property, some fields use a different property name. An example of this are entity reference fields using target_id as their property name. Line 4 of our example would then look like:
$id = $field_reference->target_id;
Instead of a string like the node title, this would load the target_id integer into a variable. Because it is quite likely that you would like to access data on the corresponding item, there is the magic entity property that directly gets you the fully loaded entity object for this item. It’s as easy as this:
$entity = $field_reference->entity;
While these magic properties are very useful, be aware that the appropriate property depends on the field to be accessed and using the wrong one will result in an error. Also when working with more complex data types (date for instance) or fields holding multiple values (cardinality larger than 1), using magic properties can lead to unexpected results.
So besides accessing data using magic properties, what other options do we have?
The getValue() method
The getValue() method is present on all TypedData objects and returns the raw values stored. Calling this method on the field in our example returns an array holding one item (since we only have one item in the list) that contains the individual item raw values. It would look like this:
$title_field = $node->get('title'); $value = $title_field->getValue();
While the list could be useful in some cases, we want to access the data of the title field like we did earlier using the value property. So to make this example get to the same value above, we need to dig deeper into the array:
$title_field = $node->get('title'); $value = $title_field->get(0)->get('value')->getValue();
What this does is get the first item in the list (use get(0) or offsetGet(0)) returning a FieldType plugin. Because we know that the title field uses the name value for its property we can use the get() method to get its property by name. Finally we use the getValue() method to get the string value from the Stringdata Datatype plugin.
You can now see how accessing the string value can lead to quite lengthy code and why using the magic value property explained earlier is so appealing.
Many content entity types have helper methods on their class to make accessing certain fields easier and more verbose. Staying with the title example, we could get the title string from the node as follows:
$value = $node->getTitle();
Helper functions can be found in the class that is responsible for instantiating the object you are working with. The notation is usually short and verbose. Besides that, it is generally advisable to use helper methods when available because in future releases these might be changed or overridden featuring additional logic that is very specific to what the helper method does.
Looking at the options laid out above, you might ask what option should I use to access a field value?
The main driver for this decision is the cardinality of the field you are dealing with. If you know that the field has only one value, you can use the following:
$title = $node->get('title')->value; $id = $node->get('field_reference')->target_id; $entity = $node->get('field_reference)->entity;
If the field has more than one value or you expect to refactor code to accommodate this in the (near) future, you can use the following:
$names = $node->get('field_names')->getValue(); $tags = $node->get('field_tags')->referencedEntities();
Hopefully this sheds some light on accessing values on fields in Drupal. While it might seem somewhat complex or even overwhelming, it leaves us with a robust way of accessing data that provides a strong foundation for working with Drupal in the future.
While this notation is verbose and very clear, you don’t have to split the get() and value calls. You can put both on one line like:
$title = $node->get('title')->value;
Extra: test if the field exists
This code shown in the examples will throw an error if the entity doesn't have a field named "title". Make sure this code only gets run on entities where you know that field exists. If you are unsure if the is the case, you can use $entity->hasField('field_name') or if (!empty($entity->field_name)) to test.
Extra: no need to explicitly call get()
You don’t need to call get(), because this is a magic method. So in our example, this line:
$title = $node->get('title')->value;
Could be written as:
$title = $node->title->value;
Calling a magic method like this provides slightly cleaner code that is even easier to read. Please be aware that magic methods can not be always used while standard methods can be.