Issue #2827218 by tedbow, damiankloip, Wim Leers, Berdir, tstoeckler: Denormalization on field items is never called: add FieldNormalizer + FieldItemNormalizer with denormalize() methods
parent
f01ac2d4d8
commit
23a9dd83fd
|
@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\rest\LinkManager\LinkManagerInterface;
|
||||
use Drupal\serialization\Normalizer\FieldableEntityNormalizerTrait;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
|
@ -14,6 +15,8 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
|||
*/
|
||||
class ContentEntityNormalizer extends NormalizerBase {
|
||||
|
||||
use FieldableEntityNormalizerTrait;
|
||||
|
||||
/**
|
||||
* The interface or class that this Normalizer supports.
|
||||
*
|
||||
|
@ -28,13 +31,6 @@ class ContentEntityNormalizer extends NormalizerBase {
|
|||
*/
|
||||
protected $linkManager;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
|
@ -42,7 +38,6 @@ class ContentEntityNormalizer extends NormalizerBase {
|
|||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an ContentEntityNormalizer object.
|
||||
*
|
||||
|
@ -128,7 +123,7 @@ class ContentEntityNormalizer extends NormalizerBase {
|
|||
|
||||
// Create the entity.
|
||||
$typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context);
|
||||
$entity_type = $this->entityManager->getDefinition($typed_data_ids['entity_type']);
|
||||
$entity_type = $this->getEntityTypeDefinition($typed_data_ids['entity_type']);
|
||||
$default_langcode_key = $entity_type->getKey('default_langcode');
|
||||
$langcode_key = $entity_type->getKey('langcode');
|
||||
$values = array();
|
||||
|
@ -174,23 +169,11 @@ class ContentEntityNormalizer extends NormalizerBase {
|
|||
}
|
||||
}
|
||||
|
||||
// Pass the names of the fields whose values can be merged.
|
||||
$entity->_restSubmittedFields = array_keys($data);
|
||||
$this->denormalizeFieldData($data, $entity, $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());
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Pass the names of the fields whose values can be merged.
|
||||
// @todo https://www.drupal.org/node/2456257 remove this.
|
||||
$entity->_restSubmittedFields = array_keys($data);
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
|
|
@ -25,13 +25,28 @@ services:
|
|||
class: Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer
|
||||
tags:
|
||||
# Set the priority lower than the hal entity reference field item
|
||||
# normalizer, so that we do not replace that for hal_json.
|
||||
# normalizer, so that we do not replace that for hal_json but higher than
|
||||
# this modules generic field item normalizer.
|
||||
# @todo Find a better way for this in https://www.drupal.org/node/2575761.
|
||||
- { name: normalizer, priority: 5 }
|
||||
- { name: normalizer, priority: 8 }
|
||||
serialization.normalizer.field_item:
|
||||
class: Drupal\serialization\Normalizer\FieldItemNormalizer
|
||||
tags:
|
||||
# Priority must be lower than serializer.normalizer.field_item.hal and any
|
||||
# field type specific normalizer such as
|
||||
# serializer.normalizer.entity_reference_field_item.
|
||||
- { name: normalizer, priority: 6 }
|
||||
serialization.normalizer.field:
|
||||
class: Drupal\serialization\Normalizer\FieldNormalizer
|
||||
tags:
|
||||
# Priority must be lower than serializer.normalizer.field.hal.
|
||||
- { name: normalizer, priority: 6 }
|
||||
serializer.normalizer.list:
|
||||
class: Drupal\serialization\Normalizer\ListNormalizer
|
||||
tags:
|
||||
- { name: normalizer }
|
||||
# Priority must be higher than serialization.normalizer.field but less
|
||||
# than hal field normalizer.
|
||||
- { name: normalizer, priority: 9 }
|
||||
serializer.normalizer.password_field_item:
|
||||
class: Drupal\serialization\Normalizer\NullNormalizer
|
||||
arguments: ['Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem']
|
||||
|
|
|
@ -8,9 +8,7 @@ namespace Drupal\serialization\Normalizer;
|
|||
class ContentEntityNormalizer extends EntityNormalizer {
|
||||
|
||||
/**
|
||||
* The interface or class that this Normalizer supports.
|
||||
*
|
||||
* @var array
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = ['Drupal\Core\Entity\ContentEntityInterface'];
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
|
@ -11,19 +12,14 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
|||
*/
|
||||
class EntityNormalizer extends ComplexDataNormalizer implements DenormalizerInterface {
|
||||
|
||||
use FieldableEntityNormalizerTrait;
|
||||
|
||||
/**
|
||||
* The interface or class that this Normalizer supports.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = array('Drupal\Core\Entity\EntityInterface');
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
protected $supportedInterfaceOrClass = [EntityInterface::class];
|
||||
|
||||
/**
|
||||
* Constructs an EntityNormalizer object.
|
||||
|
@ -39,49 +35,26 @@ class EntityNormalizer extends ComplexDataNormalizer implements DenormalizerInte
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []) {
|
||||
// Get the entity type ID while letting context override the $class param.
|
||||
$entity_type_id = !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
|
||||
$entity_type_id = $this->determineEntityTypeId($class, $context);
|
||||
$entity_type_definition = $this->getEntityTypeDefinition($entity_type_id);
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
|
||||
// Get the entity type definition.
|
||||
$entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
|
||||
// The bundle property will be required to denormalize a bundleable
|
||||
// fieldable entity.
|
||||
if ($entity_type_definition->hasKey('bundle') && $entity_type_definition->isSubclassOf(FieldableEntityInterface::class)) {
|
||||
// Get an array containing the bundle only. This also remove the bundle
|
||||
// key from the $data array.
|
||||
$bundle_data = $this->extractBundleData($data, $entity_type_definition);
|
||||
|
||||
// Don't try to create an entity without an entity type id.
|
||||
if (!$entity_type_definition) {
|
||||
throw new UnexpectedValueException(sprintf('The specified entity type "%s" does not exist. A valid etnity type is required for denormalization', $entity_type_id));
|
||||
// Create the entity from bundle data only, then apply field values after.
|
||||
$entity = $this->entityManager->getStorage($entity_type_id)->create($bundle_data);
|
||||
|
||||
$this->denormalizeFieldData($data, $entity, $format, $context);
|
||||
}
|
||||
|
||||
// The bundle property will be required to denormalize a bundleable entity.
|
||||
if ($entity_type_definition->hasKey('bundle')) {
|
||||
$bundle_key = $entity_type_definition->getKey('bundle');
|
||||
// Get the base field definitions for this entity type.
|
||||
$base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_id);
|
||||
|
||||
// Get the ID key from the base field definition for the bundle key or
|
||||
// default to 'value'.
|
||||
$key_id = isset($base_field_definitions[$bundle_key]) ? $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName() : 'value';
|
||||
|
||||
// Normalize the bundle if it is not explicitly set.
|
||||
$data[$bundle_key] = isset($data[$bundle_key][0][$key_id]) ? $data[$bundle_key][0][$key_id] : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
|
||||
|
||||
// Get the bundle entity type from the entity type definition.
|
||||
$bundle_type_id = $entity_type_definition->getBundleEntityType();
|
||||
$bundle_types = $bundle_type_id ? $this->entityManager->getStorage($bundle_type_id)->getQuery()->execute() : [];
|
||||
|
||||
// Make sure a bundle has been provided.
|
||||
if (!is_string($data[$bundle_key])) {
|
||||
throw new UnexpectedValueException('A string must be provided as a bundle value.');
|
||||
}
|
||||
|
||||
// Make sure the submitted bundle is a valid bundle for the entity type.
|
||||
if ($bundle_types && !in_array($data[$bundle_key], $bundle_types)) {
|
||||
throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $data[$bundle_key]));
|
||||
}
|
||||
else {
|
||||
// Create the entity from all data.
|
||||
$entity = $this->entityManager->getStorage($entity_type_id)->create($data);
|
||||
}
|
||||
|
||||
// Create the entity from data.
|
||||
$entity = $this->entityManager->getStorage($entity_type_id)->create($data);
|
||||
|
||||
// Pass the names of the fields whose values can be merged.
|
||||
// @todo https://www.drupal.org/node/2456257 remove this.
|
||||
$entity->_restSubmittedFields = array_keys($data);
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* Denormalizes field item object structure by updating the entity field values.
|
||||
*/
|
||||
class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = FieldItemInterface::class;
|
||||
|
||||
/**
|
||||
* {@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 ($context['target_instance']->getParent() == NULL) {
|
||||
throw new InvalidArgumentException('The field item passed in via $context[\'target_instance\'] must have a parent set.');
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Field\FieldItemInterface $field_item */
|
||||
$field_item = $context['target_instance'];
|
||||
|
||||
$field_item->setValue($this->constructValue($data, $context));
|
||||
return $field_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* The context passed into the Normalizer.
|
||||
*
|
||||
* @return mixed
|
||||
* The value to use in Entity::setValue().
|
||||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = FieldItemListInterface::class;
|
||||
|
||||
/**
|
||||
* {@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');
|
||||
}
|
||||
|
||||
/** @var FieldItemListInterface $items */
|
||||
$items = $context['target_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. All items in field should have removed before this method
|
||||
// was called.
|
||||
// @see \Drupal\serialization\Normalizer\ContentEntityNormalizer::denormalize().
|
||||
$context['target_instance'] = $items->appendItem();
|
||||
$this->serializer->denormalize($item_data, $item_class, $format, $context);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* A trait for providing fieldable entity normalization/denormalization methods.
|
||||
*
|
||||
* @todo Move this into a FieldableEntityNormalizer in Drupal 9. This is a trait
|
||||
* used in \Drupal\serialization\Normalizer\EntityNormalizer to maintain BC.
|
||||
* @see https://www.drupal.org/node/2834734
|
||||
*/
|
||||
trait FieldableEntityNormalizerTrait {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* Determines the entity type ID to denormalize as.
|
||||
*
|
||||
* @param string $class
|
||||
* The entity type class to be denormalized to.
|
||||
* @param array $context
|
||||
* The serialization context data.
|
||||
*
|
||||
* @return string
|
||||
* The entity type ID.
|
||||
*/
|
||||
protected function determineEntityTypeId($class, $context) {
|
||||
// Get the entity type ID while letting context override the $class param.
|
||||
return !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity type definition.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type ID to load the definition for.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeInterface
|
||||
* The loaded entity type definition.
|
||||
*
|
||||
* @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
|
||||
*/
|
||||
protected function getEntityTypeDefinition($entity_type_id) {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
|
||||
// Get the entity type definition.
|
||||
$entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
|
||||
|
||||
// Don't try to create an entity without an entity type id.
|
||||
if (!$entity_type_definition) {
|
||||
throw new UnexpectedValueException(sprintf('The specified entity type "%s" does not exist. A valid entity type is required for denormalization', $entity_type_id));
|
||||
}
|
||||
|
||||
return $entity_type_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalizes the bundle property so entity creation can use it.
|
||||
*
|
||||
* @param array $data
|
||||
* The data being denormalized.
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition
|
||||
* The entity type definition.
|
||||
*
|
||||
* @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
|
||||
*
|
||||
* @return string
|
||||
* The valid bundle name.
|
||||
*/
|
||||
protected function extractBundleData(array &$data, EntityTypeInterface $entity_type_definition) {
|
||||
$bundle_key = $entity_type_definition->getKey('bundle');
|
||||
// Get the base field definitions for this entity type.
|
||||
$base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_definition->id());
|
||||
|
||||
// Get the ID key from the base field definition for the bundle key or
|
||||
// default to 'value'.
|
||||
$key_id = isset($base_field_definitions[$bundle_key]) ? $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName() : 'value';
|
||||
|
||||
// Normalize the bundle if it is not explicitly set.
|
||||
$bundle_value = isset($data[$bundle_key][0][$key_id]) ? $data[$bundle_key][0][$key_id] : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
|
||||
// Unset the bundle from the data.
|
||||
unset($data[$bundle_key]);
|
||||
|
||||
// Get the bundle entity type from the entity type definition.
|
||||
$bundle_type_id = $entity_type_definition->getBundleEntityType();
|
||||
$bundle_types = $bundle_type_id ? $this->entityManager->getStorage($bundle_type_id)->getQuery()->execute() : [];
|
||||
|
||||
// Make sure a bundle has been provided.
|
||||
if (!is_string($bundle_value)) {
|
||||
throw new UnexpectedValueException('A string must be provided as a bundle value.');
|
||||
}
|
||||
|
||||
// Make sure the submitted bundle is a valid bundle for the entity type.
|
||||
if ($bundle_types && !in_array($bundle_value, $bundle_types)) {
|
||||
throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $bundle_value));
|
||||
}
|
||||
|
||||
return [$bundle_key => $bundle_value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalizes entity data by denormalizing each field individually.
|
||||
*
|
||||
* @param array $data
|
||||
* The data to denormalize.
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The fieldable entity to set field values for.
|
||||
* @param string $format
|
||||
* The serialization format.
|
||||
* @param array $context
|
||||
* The context data.
|
||||
*/
|
||||
protected function denormalizeFieldData(array $data, FieldableEntityInterface $entity, $format, array $context) {
|
||||
foreach ($data as $field_name => $field_data) {
|
||||
$field_item_list = $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.
|
||||
$field_item_list->setValue([]);
|
||||
$field_item_list_class = get_class($field_item_list);
|
||||
|
||||
if ($field_data) {
|
||||
// The field instance must be passed in the context so that the field
|
||||
// denormalizer can update field values for the parent entity.
|
||||
$context['target_instance'] = $field_item_list;
|
||||
$this->serializer->denormalize($field_data, $field_item_list_class, $format, $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
name: 'FieldItem normalization test support'
|
||||
type: module
|
||||
description: 'Provides test support for fieldItem normalization test support.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
serializer.normalizer.silly_fielditem:
|
||||
class: Drupal\field_normalization_test\Normalization\TextItemSillyNormalizer
|
||||
tags:
|
||||
# The priority must be higher than serialization.normalizer.field_item.
|
||||
- { name: normalizer , priority: 9 }
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field_normalization_test\Normalization;
|
||||
|
||||
use Drupal\serialization\Normalizer\FieldItemNormalizer;
|
||||
use Drupal\text\Plugin\Field\FieldType\TextItemBase;
|
||||
|
||||
/**
|
||||
* A test TextItem normalizer to test denormalization.
|
||||
*/
|
||||
class TextItemSillyNormalizer extends FieldItemNormalizer {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = TextItemBase::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = array()) {
|
||||
$data = parent::normalize($object, $format, $context);
|
||||
$data['value'] .= '::silly_suffix';
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
$value = parent::constructValue($data, $context);
|
||||
$value['value'] = str_replace('::silly_suffix', '', $value['value']);
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Test field level normalization process.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class FieldItemSerializationTest extends NormalizerTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user', 'field_normalization_test');
|
||||
|
||||
/**
|
||||
* The class name of the test class.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityClass = 'Drupal\entity_test\Entity\EntityTestMulRev';
|
||||
|
||||
/**
|
||||
* The test values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
/**
|
||||
* The test entity.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityBase
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The serializer service.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer.
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Auto-create a field for testing default field values.
|
||||
FieldStorageConfig::create(array(
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text_default',
|
||||
'type' => 'text',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
))->save();
|
||||
FieldConfig::create(array(
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text_default',
|
||||
'bundle' => 'entity_test_mulrev',
|
||||
'label' => 'Test text-field with default',
|
||||
'default_value' => [
|
||||
[
|
||||
'value' => 'This is the default',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
],
|
||||
'widget' => array(
|
||||
'type' => 'text_textfield',
|
||||
'weight' => 0,
|
||||
),
|
||||
))->save();
|
||||
|
||||
// Create a test entity to serialize.
|
||||
$this->values = array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'field_test_text' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'full_html',
|
||||
),
|
||||
);
|
||||
$this->entity = EntityTestMulRev::create($this->values);
|
||||
$this->entity->save();
|
||||
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
|
||||
$this->installConfig(array('field'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing and denormalizing an entity with field item normalizer.
|
||||
*/
|
||||
public function testFieldNormalizeDenormalize() {
|
||||
$normalized = $this->serializer->normalize($this->entity, 'json');
|
||||
|
||||
$expected_field_value = $this->entity->field_test_text[0]->getValue()['value'] . '::silly_suffix';
|
||||
$this->assertEquals($expected_field_value, $normalized['field_test_text'][0]['value'], 'Text field item normalized');
|
||||
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
|
||||
|
||||
$this->assertEquals($denormalized->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
|
||||
$this->assertEquals($denormalized->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
|
||||
|
||||
// Unset the values for text field that has a default value.
|
||||
unset($normalized['field_test_text_default']);
|
||||
$denormalized_without_all_fields = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
|
||||
// Check that denormalized entity is still the same even if not all fields
|
||||
// are not provided.
|
||||
$this->assertEquals($denormalized_without_all_fields->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
|
||||
// Even though field_test_text_default value was unset before
|
||||
// denormalization it should still have the default values for the field.
|
||||
$this->assertEquals($denormalized_without_all_fields->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\serialization\Normalizer\EntityNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
|
@ -104,6 +106,10 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
];
|
||||
|
||||
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('id')
|
||||
->willReturn('test');
|
||||
$entity_type->expects($this->once())
|
||||
->method('hasKey')
|
||||
->with('bundle')
|
||||
|
@ -112,6 +118,11 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
->method('getKey')
|
||||
->with('bundle')
|
||||
->will($this->returnValue('test_type'));
|
||||
$entity_type->expects($this->once())
|
||||
->method('isSubClassOf')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(TRUE);
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('getBundleEntityType')
|
||||
->will($this->returnValue('test_bundle'));
|
||||
|
@ -154,24 +165,50 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
->with('test_bundle')
|
||||
->will($this->returnValue($entity_type_storage));
|
||||
|
||||
// The expected test data should have a modified test_type property.
|
||||
$key_1 = $this->getMock(FieldItemListInterface::class);
|
||||
$key_2 = $this->getMock(FieldItemListInterface::class);
|
||||
|
||||
$entity = $this->getMock(FieldableEntityInterface::class);
|
||||
$entity->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('key_1')
|
||||
->willReturn($key_1);
|
||||
$entity->expects($this->at(1))
|
||||
->method('get')
|
||||
->with('key_2')
|
||||
->willReturn($key_2);
|
||||
|
||||
$storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
// Create should only be called with the bundle property at first.
|
||||
$expected_test_data = array(
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
'test_type' => 'test_bundle',
|
||||
);
|
||||
|
||||
$storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$storage->expects($this->once())
|
||||
->method('create')
|
||||
->with($expected_test_data)
|
||||
->will($this->returnValue($this->getMock('Drupal\Core\Entity\EntityInterface')));
|
||||
->will($this->returnValue($entity));
|
||||
|
||||
$this->entityManager->expects($this->at(3))
|
||||
->method('getStorage')
|
||||
->with('test')
|
||||
->will($this->returnValue($storage));
|
||||
|
||||
// Setup expectations for the serializer. This will be called for each field
|
||||
// item.
|
||||
$serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(array('denormalize'))
|
||||
->getMock();
|
||||
$serializer->expects($this->at(0))
|
||||
->method('denormalize')
|
||||
->with('value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test']);
|
||||
$serializer->expects($this->at(1))
|
||||
->method('denormalize')
|
||||
->with('value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test']);
|
||||
|
||||
$this->entityNormalizer->setSerializer($serializer);
|
||||
|
||||
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
|
||||
}
|
||||
|
||||
|
@ -192,6 +229,10 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
];
|
||||
|
||||
$entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('id')
|
||||
->willReturn('test');
|
||||
$entity_type->expects($this->once())
|
||||
->method('hasKey')
|
||||
->with('bundle')
|
||||
|
@ -200,6 +241,11 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
->method('getKey')
|
||||
->with('bundle')
|
||||
->will($this->returnValue('test_type'));
|
||||
$entity_type->expects($this->once())
|
||||
->method('isSubClassOf')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(TRUE);
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('getBundleEntityType')
|
||||
->will($this->returnValue('test_bundle'));
|
||||
|
@ -242,8 +288,7 @@ class EntityNormalizerTest extends UnitTestCase {
|
|||
->with('test_bundle')
|
||||
->will($this->returnValue($entity_type_storage));
|
||||
|
||||
|
||||
$this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']);
|
||||
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue