class ResourceIdentifierNormalizer

Same name in other branches
  1. 8.9.x core/modules/jsonapi/src/Normalizer/ResourceIdentifierNormalizer.php \Drupal\jsonapi\Normalizer\ResourceIdentifierNormalizer
  2. 10 core/modules/jsonapi/src/Normalizer/ResourceIdentifierNormalizer.php \Drupal\jsonapi\Normalizer\ResourceIdentifierNormalizer
  3. 11.x core/modules/jsonapi/src/Normalizer/ResourceIdentifierNormalizer.php \Drupal\jsonapi\Normalizer\ResourceIdentifierNormalizer

Normalizes a Relationship according to the JSON:API specification.

Normalizer class for relationship elements. A relationship can be anything that points to an entity in a JSON:API resource.

@internal JSON:API maintains no PHP API since its API is the HTTP API. This class may change at any time and this will break any dependencies on it.

Hierarchy

  • class \Drupal\serialization\Normalizer\NormalizerBase implements \Symfony\Component\Serializer\SerializerAwareInterface, \Drupal\serialization\Normalizer\CacheableNormalizerInterface, \Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface uses \Symfony\Component\Serializer\SerializerAwareTrait
    • class \Drupal\jsonapi\Normalizer\NormalizerBase extends \Drupal\serialization\Normalizer\NormalizerBase
      • class \Drupal\jsonapi\Normalizer\ResourceIdentifierNormalizer extends \Drupal\jsonapi\Normalizer\NormalizerBase implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface

Expanded class hierarchy of ResourceIdentifierNormalizer

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

1 file declares its use of ResourceIdentifierNormalizer
ResourceIdentifierNormalizerTest.php in core/modules/jsonapi/tests/src/Unit/Normalizer/ResourceIdentifierNormalizerTest.php
1 string reference to 'ResourceIdentifierNormalizer'
jsonapi.services.yml in core/modules/jsonapi/jsonapi.services.yml
core/modules/jsonapi/jsonapi.services.yml
1 service uses ResourceIdentifierNormalizer
serializer.normalizer.resource_identifier.jsonapi in core/modules/jsonapi/jsonapi.services.yml
Drupal\jsonapi\Normalizer\ResourceIdentifierNormalizer

File

core/modules/jsonapi/src/Normalizer/ResourceIdentifierNormalizer.php, line 24

Namespace

Drupal\jsonapi\Normalizer
View source
class ResourceIdentifierNormalizer extends NormalizerBase implements DenormalizerInterface {
    
    /**
     * {@inheritdoc}
     */
    protected $supportedInterfaceOrClass = ResourceIdentifier::class;
    
    /**
     * The entity field manager.
     *
     * @var \Drupal\Core\Entity\EntityFieldManagerInterface
     */
    protected $fieldManager;
    
    /**
     * RelationshipNormalizer constructor.
     *
     * @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager
     *   The entity field manager.
     */
    public function __construct(EntityFieldManagerInterface $field_manager) {
        $this->fieldManager = $field_manager;
    }
    
    /**
     * {@inheritdoc}
     */
    public function normalize($object, $format = NULL, array $context = []) {
        assert($object instanceof ResourceIdentifier);
        $normalization = [
            'type' => $object->getTypeName(),
            'id' => $object->getId(),
        ];
        if ($object->getMeta()) {
            $normalization['meta'] = $this->serializer
                ->normalize($object->getMeta(), $format, $context);
        }
        return CacheableNormalization::permanent($normalization);
    }
    
    /**
     * {@inheritdoc}
     */
    public function denormalize($data, $class, $format = NULL, array $context = []) {
        // If we get here, it's via a relationship POST/PATCH.
        
        /** @var \Drupal\jsonapi\ResourceType\ResourceType $resource_type */
        $resource_type = $context['resource_type'];
        $entity_type_id = $resource_type->getEntityTypeId();
        $field_definitions = $this->fieldManager
            ->getFieldDefinitions($entity_type_id, $resource_type->getBundle());
        if (empty($context['related']) || empty($field_definitions[$context['related']])) {
            throw new BadRequestHttpException('Invalid or missing related field.');
        }
        
        /** @var \Drupal\field\Entity\FieldConfig $field_definition */
        $field_definition = $field_definitions[$context['related']];
        $target_resource_types = $resource_type->getRelatableResourceTypesByField($resource_type->getPublicName($context['related']));
        $target_resource_type_names = array_map(function (ResourceType $resource_type) {
            return $resource_type->getTypeName();
        }, $target_resource_types);
        $is_multiple = $field_definition->getFieldStorageDefinition()
            ->isMultiple();
        $data = $this->massageRelationshipInput($data, $is_multiple);
        $resource_identifiers = array_map(function ($value) use ($target_resource_type_names) {
            // Make sure that the provided type is compatible with the targeted
            // resource.
            if (!in_array($value['type'], $target_resource_type_names)) {
                throw new BadRequestHttpException(sprintf('The provided type (%s) does not match the destination resource types (%s).', $value['type'], implode(', ', $target_resource_type_names)));
            }
            return new ResourceIdentifier($value['type'], $value['id'], $value['meta'] ?? []);
        }, $data['data']);
        if (!ResourceIdentifier::areResourceIdentifiersUnique($resource_identifiers)) {
            throw new BadRequestHttpException('Duplicate relationships are not permitted. Use `meta.arity` to distinguish resource identifiers with matching `type` and `id` values.');
        }
        return $resource_identifiers;
    }
    
    /**
     * Validates and massages the relationship input depending on the cardinality.
     *
     * @param array $data
     *   The input data from the body.
     * @param bool $is_multiple
     *   Indicates if the relationship is to-many.
     *
     * @return array
     *   The massaged data array.
     */
    protected function massageRelationshipInput(array $data, $is_multiple) {
        if ($is_multiple) {
            if (!is_array($data['data'])) {
                throw new BadRequestHttpException('Invalid body payload for the relationship.');
            }
            // Leave the invalid elements.
            $invalid_elements = array_filter($data['data'], function ($element) {
                return empty($element['type']) || empty($element['id']);
            });
            if ($invalid_elements) {
                throw new BadRequestHttpException('Invalid body payload for the relationship.');
            }
        }
        else {
            // For to-one relationships you can have a NULL value.
            if (is_null($data['data'])) {
                return [
                    'data' => [],
                ];
            }
            if (empty($data['data']['type']) || empty($data['data']['id'])) {
                throw new BadRequestHttpException('Invalid body payload for the relationship.');
            }
            $data['data'] = [
                $data['data'],
            ];
        }
        return $data;
    }
    
    /**
     * {@inheritdoc}
     */
    public function hasCacheableSupportsMethod() : bool {
        return TRUE;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY constant Name of key for bubbling cacheability metadata via serialization context.
NormalizerBase::$format protected property List of formats which supports (de-)normalization. Overrides NormalizerBase::$format
NormalizerBase::addCacheableDependency protected function Adds cacheability if applicable.
NormalizerBase::checkFormat protected function Checks if the provided format is supported by this normalizer. Overrides NormalizerBase::checkFormat
NormalizerBase::rasterizeValueRecursive protected static function Rasterizes a value recursively.
NormalizerBase::supportsDenormalization public function Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() 1
NormalizerBase::supportsNormalization public function 1
ResourceIdentifierNormalizer::$fieldManager protected property The entity field manager.
ResourceIdentifierNormalizer::$supportedInterfaceOrClass protected property The interface or class that this Normalizer supports. Overrides NormalizerBase::$supportedInterfaceOrClass
ResourceIdentifierNormalizer::denormalize public function
ResourceIdentifierNormalizer::hasCacheableSupportsMethod public function Overrides NormalizerBase::hasCacheableSupportsMethod
ResourceIdentifierNormalizer::massageRelationshipInput protected function Validates and massages the relationship input depending on the cardinality.
ResourceIdentifierNormalizer::normalize public function
ResourceIdentifierNormalizer::__construct public function RelationshipNormalizer constructor.

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