diff --git a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php index d9a8938..17155be 100644 --- a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php @@ -8,9 +8,7 @@ class ContentEntityNormalizer extends EntityNormalizer { /** - * The interface or class that this Normalizer supports. - * - * @var array + * {@inheritdoc} */ protected $supportedInterfaceOrClass = ['Drupal\Core\Entity\ContentEntityInterface']; @@ -36,19 +34,20 @@ public function normalize($object, $format = NULL, array $context = array()) { * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = []) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $entity = parent::denormalize($data, $class, $format, $context); - // Iterate through remaining items in data array. These should all - // correspond to fields. foreach ($data as $field_name => $field_data) { - $items = $entity->get($field_name); - // Remove any values that were set as a part of entity creation (e.g - // uuid). If the incoming field data is set to an empty array, this will - // also have the effect of emptying the field in REST module. - $items->setValue(array()); + $fieldItemList = $entity->get($field_name); + // Remove all values that were set as a part of entity creation as the + // field values were not denormalized. When passing this field item list + // to field denormalizers there should be no values in the field. + $fieldItemList->setValue(array()); if ($field_data) { - // Denormalize the field data into the FieldItemList object. - $context['target_instance'] = $items; - $this->serializer->denormalize($field_data, get_class($items), $format, $context); + // The field instance must be passed in the context so that denormalizer + // can update field values. The entity itself does not need to be passed + // because nothing except the current field needs to be set. + $context['__target_field_instance'] = $fieldItemList; + $this->serializer->denormalize($field_data, get_class($fieldItemList), $format, $context); } } return $entity; diff --git a/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php index 21b62ce..866882a 100644 --- a/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php +++ b/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php @@ -2,19 +2,16 @@ namespace Drupal\serialization\Normalizer; -use Drupal\Core\Field\FieldItemInterface; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; /** - * Converts the Drupal field item object structure to HAL array structure. + * Denormalizes field item object structure by updating the entity field values. */ class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerInterface { /** - * The interface or class that this Normalizer supports. - * - * @var string + * {@inheritdoc} */ protected $supportedInterfaceOrClass = 'Drupal\Core\Field\FieldItemInterface'; @@ -22,24 +19,15 @@ class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerI * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = array()) { - if (!isset($context['target_instance'])) { - throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldItemNormalizer'); + if (!isset($context['__target_field_item_instance'])) { + throw new InvalidArgumentException('$context[\'__target_field_item_instance\'] must be set to denormalize with the FieldItemNormalizer'); } - if ($context['target_instance']->getParent() == NULL) { - throw new InvalidArgumentException('The field item passed in via $context[\'target_instance\'] must have a parent set.'); + if ($context['__target_field_item_instance']->getParent() === NULL) { + throw new InvalidArgumentException('The field item passed in via $context[\'__target_field_item_instance\'] must have a parent set.'); } - $field_item = $context['target_instance']; - - // If this field is translatable, we need to create a translated instance. - if (isset($data['lang'])) { - $langcode = $data['lang']; - unset($data['lang']); - $field_definition = $field_item->getFieldDefinition(); - if ($field_definition->isTranslatable()) { - $field_item = $this->createTranslatedInstance($field_item, $langcode); - } - } + /** @var \Drupal\Core\Field\FieldItemInterface $field_item */ + $field_item = $context['__target_field_item_instance']; $field_item->setValue($this->constructValue($data, $context)); return $field_item; @@ -48,6 +36,10 @@ public function denormalize($data, $class, $format = NULL, array $context = arra /** * Build the field item value using the incoming data. * + * Most normalizers that extend this class can simply use this method to + * construct the denormalized value without having to override denormalize() + * and reimplementing its validation logic or its call to set the field value. + * * @param mixed $data * The incoming data for this field item. * @param array $context @@ -60,34 +52,4 @@ protected function constructValue($data, $context) { return $data; } - /** - * Get a translated version of the field item instance. - * - * To indicate that a field item applies to one translation of an entity and - * not another, the property path must originate with a translation of the - * entity. This is the reason for using target_instances, from which the - * property path can be traversed up to the root. - * - * @param \Drupal\Core\Field\FieldItemInterface $item - * The untranslated field item instance. - * @param string $langcode - * The langcode. - * - * @return \Drupal\Core\Field\FieldItemInterface - * The translated field item instance. - */ - protected function createTranslatedInstance(FieldItemInterface $item, $langcode) { - // Remove the untranslated item that was created for the default language - // by FieldNormalizer::denormalize(). - $items = $item->getParent(); - $delta = $item->getName(); - unset($items[$delta]); - - // Instead, create a new item for the entity in the requested language. - $entity = $item->getEntity(); - $entity_translation = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity->addTranslation($langcode); - $field_name = $item->getFieldDefinition()->getName(); - return $entity_translation->get($field_name)->appendItem(); - } - } diff --git a/core/modules/serialization/src/Normalizer/FieldNormalizer.php b/core/modules/serialization/src/Normalizer/FieldNormalizer.php index bcef87d..ec43550 100644 --- a/core/modules/serialization/src/Normalizer/FieldNormalizer.php +++ b/core/modules/serialization/src/Normalizer/FieldNormalizer.php @@ -7,14 +7,18 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; /** - * Converts the Drupal field structure to HAL array structure. + * Denormalizes data to Drupal field values. + * + * This class simply calls denormalize() on the individual FieldItems. The + * FieldItem normalizers are responsible for setting the field values for each + * item. + * + * @see \Drupal\serialization\Normalizer\FieldItemNormalizer. */ class FieldNormalizer extends ListNormalizer implements DenormalizerInterface { /** - * The interface or class that this Normalizer supports. - * - * @var string + * {@inheritdoc} */ protected $supportedInterfaceOrClass = FieldItemListInterface::class; @@ -22,21 +26,22 @@ class FieldNormalizer extends ListNormalizer implements DenormalizerInterface { * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = array()) { - if (!isset($context['target_instance'])) { - throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldNormalizer'); + if (!isset($context['__target_field_instance'])) { + throw new InvalidArgumentException('$context[\'__target_field_instance\'] must be set to denormalize with the FieldNormalizer'); } - if ($context['target_instance']->getParent() == NULL) { - throw new InvalidArgumentException('The field passed in via $context[\'target_instance\'] must have a parent set.'); + if ($context['__target_field_instance']->getParent() == NULL) { + throw new InvalidArgumentException('The field passed in via $context[\'__target_field_instance\'] must have a parent set.'); } - $items = $context['target_instance']; + /** @var FieldItemListInterface $items */ + $items = $context['__target_field_instance']; $item_class = $items->getItemDefinition()->getClass(); foreach ($data as $item_data) { // Create a new item and pass it as the target for the unserialization of - // $item_data. Note: if $item_data is about a different language than the - // default, FieldItemNormalizer::denormalize() will dismiss this item and - // create a new one for the right language. - $context['target_instance'] = $items->appendItem(); + // $item_data. All items in field should have removed before this method + // was called. + // @see \Drupal\serialization\Normalizer\ContentEntityNormalizer::denormalize(). + $context['__target_field_item_instance'] = $items->appendItem(); $this->serializer->denormalize($item_data, $item_class, $format, $context); } return $items;