3 namespace Drupal\serialization\Normalizer;
5 use Drupal\Core\Entity\EntityTypeInterface;
6 use Drupal\Core\Entity\FieldableEntityInterface;
7 use Symfony\Component\Serializer\Exception\UnexpectedValueException;
10 * A trait for providing fieldable entity normalization/denormalization methods.
12 * @todo Move this into a FieldableEntityNormalizer in Drupal 9. This is a trait
13 * used in \Drupal\serialization\Normalizer\EntityNormalizer to maintain BC.
14 * @see https://www.drupal.org/node/2834734
16 trait FieldableEntityNormalizerTrait {
21 * @var \Drupal\Core\Entity\EntityManagerInterface
23 protected $entityManager;
26 * Determines the entity type ID to denormalize as.
28 * @param string $class
29 * The entity type class to be denormalized to.
30 * @param array $context
31 * The serialization context data.
36 protected function determineEntityTypeId($class, $context) {
37 // Get the entity type ID while letting context override the $class param.
38 return !empty($context['entity_type']) ? $context['entity_type'] : $this->entityManager->getEntityTypeFromClass($class);
42 * Gets the entity type definition.
44 * @param string $entity_type_id
45 * The entity type ID to load the definition for.
47 * @return \Drupal\Core\Entity\EntityTypeInterface
48 * The loaded entity type definition.
50 * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
52 protected function getEntityTypeDefinition($entity_type_id) {
53 /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition */
54 // Get the entity type definition.
55 $entity_type_definition = $this->entityManager->getDefinition($entity_type_id, FALSE);
57 // Don't try to create an entity without an entity type id.
58 if (!$entity_type_definition) {
59 throw new UnexpectedValueException(sprintf('The specified entity type "%s" does not exist. A valid entity type is required for denormalization', $entity_type_id));
62 return $entity_type_definition;
66 * Denormalizes the bundle property so entity creation can use it.
69 * The data being denormalized.
70 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition
71 * The entity type definition.
73 * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
76 * The valid bundle name.
78 protected function extractBundleData(array &$data, EntityTypeInterface $entity_type_definition) {
79 $bundle_key = $entity_type_definition->getKey('bundle');
80 // Get the base field definitions for this entity type.
81 $base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity_type_definition->id());
83 // Get the ID key from the base field definition for the bundle key or
84 // default to 'value'.
85 $key_id = isset($base_field_definitions[$bundle_key]) ? $base_field_definitions[$bundle_key]->getFieldStorageDefinition()->getMainPropertyName() : 'value';
87 // Normalize the bundle if it is not explicitly set.
88 $bundle_value = isset($data[$bundle_key][0][$key_id]) ? $data[$bundle_key][0][$key_id] : (isset($data[$bundle_key]) ? $data[$bundle_key] : NULL);
89 // Unset the bundle from the data.
90 unset($data[$bundle_key]);
92 // Get the bundle entity type from the entity type definition.
93 $bundle_type_id = $entity_type_definition->getBundleEntityType();
94 $bundle_types = $bundle_type_id ? $this->entityManager->getStorage($bundle_type_id)->getQuery()->execute() : [];
96 // Make sure a bundle has been provided.
97 if (!is_string($bundle_value)) {
98 throw new UnexpectedValueException(sprintf('Could not determine entity type bundle: "%s" field is missing.', $bundle_key));
101 // Make sure the submitted bundle is a valid bundle for the entity type.
102 if ($bundle_types && !in_array($bundle_value, $bundle_types)) {
103 throw new UnexpectedValueException(sprintf('"%s" is not a valid bundle type for denormalization.', $bundle_value));
106 return [$bundle_key => $bundle_value];
110 * Denormalizes entity data by denormalizing each field individually.
113 * The data to denormalize.
114 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
115 * The fieldable entity to set field values for.
116 * @param string $format
117 * The serialization format.
118 * @param array $context
121 protected function denormalizeFieldData(array $data, FieldableEntityInterface $entity, $format, array $context) {
122 foreach ($data as $field_name => $field_data) {
123 $field_item_list = $entity->get($field_name);
125 // Remove any values that were set as a part of entity creation (e.g
126 // uuid). If the incoming field data is set to an empty array, this will
127 // also have the effect of emptying the field in REST module.
128 $field_item_list->setValue([]);
129 $field_item_list_class = get_class($field_item_list);
132 // The field instance must be passed in the context so that the field
133 // denormalizer can update field values for the parent entity.
134 $context['target_instance'] = $field_item_list;
135 $this->serializer->denormalize($field_data, $field_item_list_class, $format, $context);