From 23a9dd83fde030cca01e4835b3af3f0353f85fb6 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Wed, 21 Dec 2016 09:58:53 +0000 Subject: [PATCH] Issue #2827218 by tedbow, damiankloip, Wim Leers, Berdir, tstoeckler: Denormalization on field items is never called: add FieldNormalizer + FieldItemNormalizer with denormalize() methods --- .../Normalizer/ContentEntityNormalizer.php | 33 +---- .../serialization/serialization.services.yml | 21 ++- .../Normalizer/ContentEntityNormalizer.php | 4 +- .../src/Normalizer/EntityNormalizer.php | 67 +++------ .../src/Normalizer/FieldItemNormalizer.php | 57 +++++++ .../src/Normalizer/FieldNormalizer.php | 47 ++++++ .../FieldableEntityNormalizerTrait.php | 140 ++++++++++++++++++ .../field_normalization_test.info.yml | 6 + .../field_normalization_test.services.yml | 6 + .../Normalization/TextItemSillyNormalizer.php | 36 +++++ .../src/Kernel/FieldItemSerializationTest.php | 120 +++++++++++++++ .../Unit/Normalizer/EntityNormalizerTest.php | 59 +++++++- 12 files changed, 511 insertions(+), 85 deletions(-) create mode 100644 core/modules/serialization/src/Normalizer/FieldItemNormalizer.php create mode 100644 core/modules/serialization/src/Normalizer/FieldNormalizer.php create mode 100644 core/modules/serialization/src/Normalizer/FieldableEntityNormalizerTrait.php create mode 100644 core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.info.yml create mode 100644 core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml create mode 100644 core/modules/serialization/tests/modules/field_normalization_test/src/Normalization/TextItemSillyNormalizer.php create mode 100644 core/modules/serialization/tests/src/Kernel/FieldItemSerializationTest.php diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index e1efc207de9e..6a54b3c50205 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -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; } diff --git a/core/modules/serialization/serialization.services.yml b/core/modules/serialization/serialization.services.yml index 8b570c076308..cfb71adcf99e 100644 --- a/core/modules/serialization/serialization.services.yml +++ b/core/modules/serialization/serialization.services.yml @@ -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'] diff --git a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php index 0fb5300ad890..b47eeef6cc93 100644 --- a/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/serialization/src/Normalizer/ContentEntityNormalizer.php @@ -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']; diff --git a/core/modules/serialization/src/Normalizer/EntityNormalizer.php b/core/modules/serialization/src/Normalizer/EntityNormalizer.php index b800e812af35..84baec94be87 100644 --- a/core/modules/serialization/src/Normalizer/EntityNormalizer.php +++ b/core/modules/serialization/src/Normalizer/EntityNormalizer.php @@ -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); diff --git a/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php new file mode 100644 index 000000000000..a3cfcf206f5e --- /dev/null +++ b/core/modules/serialization/src/Normalizer/FieldItemNormalizer.php @@ -0,0 +1,57 @@ +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; + } + +} diff --git a/core/modules/serialization/src/Normalizer/FieldNormalizer.php b/core/modules/serialization/src/Normalizer/FieldNormalizer.php new file mode 100644 index 000000000000..1847379b23af --- /dev/null +++ b/core/modules/serialization/src/Normalizer/FieldNormalizer.php @@ -0,0 +1,47 @@ +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; + } + +} diff --git a/core/modules/serialization/src/Normalizer/FieldableEntityNormalizerTrait.php b/core/modules/serialization/src/Normalizer/FieldableEntityNormalizerTrait.php new file mode 100644 index 000000000000..d97d3a479555 --- /dev/null +++ b/core/modules/serialization/src/Normalizer/FieldableEntityNormalizerTrait.php @@ -0,0 +1,140 @@ +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); + } + } + } + +} diff --git a/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.info.yml b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.info.yml new file mode 100644 index 000000000000..4ba215ecbace --- /dev/null +++ b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.info.yml @@ -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 diff --git a/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml new file mode 100644 index 000000000000..36243e795459 --- /dev/null +++ b/core/modules/serialization/tests/modules/field_normalization_test/field_normalization_test.services.yml @@ -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 } diff --git a/core/modules/serialization/tests/modules/field_normalization_test/src/Normalization/TextItemSillyNormalizer.php b/core/modules/serialization/tests/modules/field_normalization_test/src/Normalization/TextItemSillyNormalizer.php new file mode 100644 index 000000000000..7187bdf26736 --- /dev/null +++ b/core/modules/serialization/tests/modules/field_normalization_test/src/Normalization/TextItemSillyNormalizer.php @@ -0,0 +1,36 @@ + '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.'); + } + +} diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php index 702107015501..b3b2780fd011 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityNormalizerTest.php @@ -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'])); } /**