You are here

class FieldItemNormalizer in Drupal 9

Same name in this branch
  1. 9 core/modules/jsonapi/src/Normalizer/FieldItemNormalizer.php \Drupal\jsonapi\Normalizer\FieldItemNormalizer
  2. 9 core/modules/hal/src/Normalizer/FieldItemNormalizer.php \Drupal\hal\Normalizer\FieldItemNormalizer
  3. 9 core/modules/serialization/src/Normalizer/FieldItemNormalizer.php \Drupal\serialization\Normalizer\FieldItemNormalizer
Same name and namespace in other branches
  1. 8 core/modules/jsonapi/src/Normalizer/FieldItemNormalizer.php \Drupal\jsonapi\Normalizer\FieldItemNormalizer

Converts the Drupal field item object to a JSON:API array structure.

@internal JSON:API maintains no PHP API since its API is the HTTP API. This class may change at any time and this will break any dependencies on it.

Hierarchy

Expanded class hierarchy of FieldItemNormalizer

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

1 file declares its use of FieldItemNormalizer
FieldItemNormalizerTest.php in core/modules/jsonapi/tests/src/Kernel/Normalizer/FieldItemNormalizerTest.php
1 string reference to 'FieldItemNormalizer'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses FieldItemNormalizer
serializer.normalizer.field_item.jsonapi in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\Normalizer\FieldItemNormalizer

File

core/modules/jsonapi/src/Normalizer/FieldItemNormalizer.php, line 26

Namespace

Drupal\jsonapi\Normalizer
View source
class FieldItemNormalizer extends NormalizerBase implements DenormalizerInterface {
  use SerializedColumnNormalizerTrait;

  /**
   * The interface or class that this Normalizer supports.
   *
   * @var string
   */
  protected $supportedInterfaceOrClass = FieldItemInterface::class;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * FieldItemNormalizer constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   *
   * This normalizer leaves JSON:API normalizer land and enters the land of
   * Drupal core's serialization system. That system was never designed with
   * cacheability in mind, and hence bubbles cacheability out of band. This must
   * catch it, and pass it to the value object that JSON:API uses.
   */
  public function normalize($field_item, $format = NULL, array $context = []) {
    assert($field_item instanceof FieldItemInterface);

    /** @var \Drupal\Core\TypedData\TypedDataInterface $property */
    $values = [];
    $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = new CacheableMetadata();
    if (!empty($field_item
      ->getProperties(TRUE))) {

      // We normalize each individual value, so each can do their own casting,
      // if needed.
      $field_properties = TypedDataInternalPropertiesHelper::getNonInternalProperties($field_item);
      foreach ($field_properties as $property_name => $property) {
        $values[$property_name] = $this->serializer
          ->normalize($property, $format, $context);
      }

      // Flatten if there is only a single property to normalize.
      $flatten = count($field_properties) === 1 && $field_item::mainPropertyName() !== NULL;
      $values = static::rasterizeValueRecursive($flatten ? reset($values) : $values);
    }
    else {
      $values = $field_item
        ->getValue();
    }
    $normalization = new CacheableNormalization($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY], $values);
    unset($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]);
    return $normalization;
  }

  /**
   * {@inheritdoc}
   */
  public function denormalize($data, $class, $format = NULL, array $context = []) {
    $item_definition = $context['field_definition']
      ->getItemDefinition();
    assert($item_definition instanceof FieldItemDataDefinitionInterface);
    $field_item = $this
      ->getFieldItemInstance($context['resource_type'], $item_definition);
    $this
      ->checkForSerializedStrings($data, $class, $field_item);
    $property_definitions = $item_definition
      ->getPropertyDefinitions();
    $serialized_property_names = $this
      ->getCustomSerializedPropertyNames($field_item);
    $denormalize_property = function ($property_name, $property_value, $property_value_class, $format, $context) use ($serialized_property_names) {
      if ($this->serializer
        ->supportsDenormalization($property_value, $property_value_class, $format, $context)) {
        return $this->serializer
          ->denormalize($property_value, $property_value_class, $format, $context);
      }
      else {
        if (in_array($property_name, $serialized_property_names, TRUE)) {
          $property_value = serialize($property_value);
        }
        return $property_value;
      }
    };

    // Because e.g. the 'bundle' entity key field requires field values to not
    // be expanded to an array of all properties, we special-case single-value
    // properties.
    if (!is_array($data)) {

      // The NULL normalization means there is no value, hence we can return
      // early. Note that this is not just an optimization but a necessity for
      // field types without main properties (such as the "map" field type).
      if ($data === NULL) {
        return $data;
      }
      $property_value = $data;
      $property_name = $item_definition
        ->getMainPropertyName();
      $property_value_class = $property_definitions[$property_name]
        ->getClass();
      return $denormalize_property($property_name, $property_value, $property_value_class, $format, $context);
    }
    $data_internal = [];
    if (!empty($property_definitions)) {
      foreach ($data as $property_name => $property_value) {
        $property_value_class = $property_definitions[$property_name]
          ->getClass();
        $data_internal[$property_name] = $denormalize_property($property_name, $property_value, $property_value_class, $format, $context);
      }
    }
    else {
      $data_internal = $data;
    }
    return $data_internal;
  }

  /**
   * Gets a field item instance for use with SerializedColumnNormalizerTrait.
   *
   * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type
   *   The JSON:API resource type of the entity being denormalized.
   * @param \Drupal\Core\Field\TypedData\FieldItemDataDefinitionInterface $item_definition
   *   The field item definition of the instance to get.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getFieldItemInstance(ResourceType $resource_type, FieldItemDataDefinitionInterface $item_definition) {
    if ($bundle_key = $this->entityTypeManager
      ->getDefinition($resource_type
      ->getEntityTypeId())
      ->getKey('bundle')) {
      $create_values = [
        $bundle_key => $resource_type
          ->getBundle(),
      ];
    }
    else {
      $create_values = [];
    }
    $entity = $this->entityTypeManager
      ->getStorage($resource_type
      ->getEntityTypeId())
      ->create($create_values);
    $field = $entity
      ->get($item_definition
      ->getFieldDefinition()
      ->getName());
    assert($field instanceof FieldItemListInterface);
    $field_item = $field
      ->appendItem();
    assert($field_item instanceof FieldItemInterface);
    return $field_item;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY constant Name of key for bubbling cacheability metadata via serialization context.
FieldItemNormalizer::$entityTypeManager protected property The entity type manager.
FieldItemNormalizer::$supportedInterfaceOrClass protected property The interface or class that this Normalizer supports. Overrides NormalizerBase::$supportedInterfaceOrClass
FieldItemNormalizer::denormalize public function
FieldItemNormalizer::getFieldItemInstance protected function Gets a field item instance for use with SerializedColumnNormalizerTrait.
FieldItemNormalizer::normalize public function This normalizer leaves JSON:API normalizer land and enters the land of Drupal core's serialization system. That system was never designed with cacheability in mind, and hence bubbles cacheability out of band. This must catch it, and pass it to…
FieldItemNormalizer::__construct public function FieldItemNormalizer constructor.
NormalizerBase::$format protected property List of formats which supports (de-)normalization. Overrides NormalizerBase::$format
NormalizerBase::addCacheableDependency protected function Adds cacheability if applicable.
NormalizerBase::checkFormat protected function Checks if the provided format is supported by this normalizer. Overrides NormalizerBase::checkFormat
NormalizerBase::rasterizeValueRecursive protected static function Rasterizes a value recursively.
NormalizerBase::supportsDenormalization public function Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() 1
NormalizerBase::supportsNormalization public function 1
SerializedColumnNormalizerTrait::checkForSerializedStrings protected function Checks if there is a serialized string for a column.
SerializedColumnNormalizerTrait::dataHasStringForSerializeColumn protected function Checks if the data contains string value for serialize column.
SerializedColumnNormalizerTrait::getCustomSerializedPropertyNames protected function Gets the names of all properties the plugin treats as serialized data.
SerializedColumnNormalizerTrait::getSerializedPropertyNames protected function Gets the names of all serialized properties.