function JsonApiDocumentTopLevelNormalizer::denormalize

Same name and namespace in other branches
  1. 8.9.x core/modules/jsonapi/src/Normalizer/JsonApiDocumentTopLevelNormalizer.php \Drupal\jsonapi\Normalizer\JsonApiDocumentTopLevelNormalizer::denormalize()
  2. 10 core/modules/jsonapi/src/Normalizer/JsonApiDocumentTopLevelNormalizer.php \Drupal\jsonapi\Normalizer\JsonApiDocumentTopLevelNormalizer::denormalize()
  3. 11.x core/modules/jsonapi/src/Normalizer/JsonApiDocumentTopLevelNormalizer.php \Drupal\jsonapi\Normalizer\JsonApiDocumentTopLevelNormalizer::denormalize()

File

core/modules/jsonapi/src/Normalizer/JsonApiDocumentTopLevelNormalizer.php, line 73

Class

JsonApiDocumentTopLevelNormalizer
Normalizes the top-level document according to the JSON:API specification.

Namespace

Drupal\jsonapi\Normalizer

Code

public function denormalize($data, $class, $format = NULL, array $context = []) {
    $resource_type = $context['resource_type'];
    // Validate a few common errors in document formatting.
    static::validateRequestBody($data, $resource_type);
    $normalized = [];
    if (!empty($data['data']['attributes'])) {
        $normalized = $data['data']['attributes'];
    }
    if (!empty($data['data']['id'])) {
        $uuid_key = $this->entityTypeManager
            ->getDefinition($resource_type->getEntityTypeId())
            ->getKey('uuid');
        $normalized[$uuid_key] = $data['data']['id'];
    }
    if (!empty($data['data']['relationships'])) {
        // Turn all single object relationship data fields into an array of
        // objects.
        $relationships = array_map(function ($relationship) {
            if (isset($relationship['data']['type']) && isset($relationship['data']['id'])) {
                return [
                    'data' => [
                        $relationship['data'],
                    ],
                ];
            }
            else {
                return $relationship;
            }
        }, $data['data']['relationships']);
        // Get an array of ids for every relationship.
        $relationships = array_map(function ($relationship) {
            if (empty($relationship['data'])) {
                return [];
            }
            if (empty($relationship['data'][0]['id'])) {
                throw new BadRequestHttpException("No ID specified for related resource");
            }
            $id_list = array_column($relationship['data'], 'id');
            if (empty($relationship['data'][0]['type'])) {
                throw new BadRequestHttpException("No type specified for related resource");
            }
            if (!($resource_type = $this->resourceTypeRepository
                ->getByTypeName($relationship['data'][0]['type']))) {
                throw new BadRequestHttpException("Invalid type specified for related resource: '" . $relationship['data'][0]['type'] . "'");
            }
            $entity_type_id = $resource_type->getEntityTypeId();
            try {
                $entity_storage = $this->entityTypeManager
                    ->getStorage($entity_type_id);
            } catch (PluginNotFoundException $e) {
                throw new BadRequestHttpException("Invalid type specified for related resource: '" . $relationship['data'][0]['type'] . "'");
            }
            // In order to maintain the order ($delta) of the relationships, we need
            // to load the entities and create a mapping between id and uuid.
            $uuid_key = $this->entityTypeManager
                ->getDefinition($entity_type_id)
                ->getKey('uuid');
            $related_entities = array_values($entity_storage->loadByProperties([
                $uuid_key => $id_list,
            ]));
            $map = [];
            foreach ($related_entities as $related_entity) {
                $map[$related_entity->uuid()] = $related_entity->id();
            }
            // $id_list has the correct order of uuids. We stitch this together with
            // $map which contains loaded entities, and then bring in the correct
            // meta values from the relationship, whose deltas match with $id_list.
            $canonical_ids = [];
            foreach ($id_list as $delta => $uuid) {
                if (!isset($map[$uuid])) {
                    // @see \Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer::normalize()
                    if ($uuid === 'virtual') {
                        continue;
                    }
                    throw new NotFoundHttpException(sprintf('The resource identified by `%s:%s` (given as a relationship item) could not be found.', $relationship['data'][$delta]['type'], $uuid));
                }
                $reference_item = [
                    'target_id' => $map[$uuid],
                ];
                if (isset($relationship['data'][$delta]['meta'])) {
                    $reference_item += $relationship['data'][$delta]['meta'];
                }
                $canonical_ids[] = array_filter($reference_item, function ($key) {
                    return substr($key, 0, strlen('drupal_internal__')) !== 'drupal_internal__';
                }, ARRAY_FILTER_USE_KEY);
            }
            return array_filter($canonical_ids);
        }, $relationships);
        // Add the relationship ids.
        $normalized = array_merge($normalized, $relationships);
    }
    // Override deserialization target class with the one in the ResourceType.
    $class = $context['resource_type']->getDeserializationTargetClass();
    return $this->serializer
        ->denormalize($normalized, $class, $format, $context);
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.