Creating a custom field with dynamic/virtual computed property value I’m working on a custom field (Drupal 8) where the author selects a taxonomy term from defined list, but the display is of, or includes, a node that references said term. I already have a service that can retrieve the desired node based on the term id. I started with a custom FieldFormatter for entity references, and that does a reasonable job of displaying the node information as desired.

However, what I really need is to get that node information included in a jsonapi response. I’ve made a custom FieldType extending EntityReferenceItem figuring all I would need is to add a computed property value (See https://www.drupal.org/node/2112677, e.g.). The examples keep pointing to the use of a specialized class extending TypedData, but the EntityReferenceItem treats the main property as computed, and doesn’t use that. 

I also read How to properly define and return simple array computed field property but I'm not sure it's addressing the same kind of thing nor is there quite enough detail there to help me implement a solution.

class MyField extends EntityReferenceItem { ...
    public function getValue() {
        $values = parent::getValue();
        $values['computed'] = 'foobar';
        return $values;
    }
}


my_module/config/schema/my_module.data_types.schema.yml - 

# Schema copied and modified from the configuration of the Entity reference field type
# in docroot/core/config/schema/core.data_types.schema.yml
field.storage_settings.my_field:
  type: mapping
  label: 'My field storage settings'
  mapping:
    target_type:
      type: string
      label: 'Blah blah blah'
field.field_settings.my_field:
  type: mapping
  label: 'Another label'
  mapping:
    handler:
      type: string
      label: 'Reference method'
    handler_settings:
      type: entity_reference_selection.[%parent.handler]
      label: 'Yet another label'
field.value.my_field:
  type: mapping
  label: 'Default value'
  mapping:
    target_id:
      type: string
      label: 'Value'
    target_uuid:
      type: uuid
# This part is new:
field.computed.my_field:
  type: mapping
  label: 'Hello World'
  mapping:
    target_id:
      type: string
      label: 'Computed Value'
    target_uuid:
      type: uuid


Any pointers out there? 

My understanding is that I can set things up so that the computed value shows up in the $items parameter passed into my custom Formatter's viewElements() method, and, at least as important, will be part of a JSONAPI response when my_field is part of the include query parameter. As of now, neither of those things occurs.
UDPATE

I think the new part of your configuration is not necessary. I recall that there is a module called entity_reference_uuid which does something like you want: extends from core ER field type, adds an extra column target_uuid and uses the entity property which is computed from the reference.

In their schema they only have these keys definition:


field.storage_settings.entity_reference_uuid
field.field_settings.entity_reference_uuid
field.value.entity_reference_uuid


If you look here you will see the entity is computed (like the one you want to add). Further down you can see they kept sync between the entity property during ::setValue  ::getValue methods and implement ::onChange to notify any changes on the property. This all are entity lifecycle logic, nothing to do with rendering.

Until here everything is more like your code.

Later on the formmatter they just iterate over the referenced entities which are obtained from the $items. The thing is I think that your problem comes from not implementing the \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase::prepareView:

/**
 * {@inheritdoc}
 *
 * Loads the entities referenced in that field across all the entities being
 * viewed.
 */
public function prepareView(array $entities_items) {
  ...
  // Fragment fromt line 142 and on.
  // For each item, pre-populate the loaded entity in $item-&gt;entity, and set
  // the 'loaded' flag.
  foreach ($entities_items as $items) {
    foreach ($items as $item) {
      if (isset($target_entities[$item-&gt;target_id])) {
        $item-&gt;entity = $target_entities[$item-&gt;target_id];
        $item-&gt;_loaded = TRUE;
      }
      elseif ($item-&gt;hasNewEntity()) {
        $item-&gt;_loaded = TRUE;
      }
    }
  }
}


As you can see there is where the entity key is populated for the formatter to use.



ORIGINAL ANSWER

By default all (most) pieces of data in Drupal 8 are typed data or get composed by it; means that fields are complex typed data which are like maps or mappings. EntityReferenceItem extends from \Drupal\Core\Field\FieldItemBase which extends from \Drupal\Core\TypedData\Plugin\DataType\Map. As explained in both of your renferences you should override the ::getValue method from EntityReferenceItem. Here is an example

/**
 * {@inheritdoc}
 */
public function getValue() {
  $values = parent::getValue();
  $values['computed_value'] = $this-&gt;myAwesomeService-&gt;getJSONApiData();      
  return $values;
}


There on your formatters (and view's field) can use the computed_value key from your field.