ResourceIdentifierNormalizer.php in Drupal 9
File
core/modules/jsonapi/src/Normalizer/ResourceIdentifierNormalizer.php
View source
<?php
namespace Drupal\jsonapi\Normalizer;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\jsonapi\JsonApiResource\ResourceIdentifier;
use Drupal\jsonapi\Normalizer\Value\CacheableNormalization;
use Drupal\jsonapi\ResourceType\ResourceType;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
class ResourceIdentifierNormalizer extends NormalizerBase implements DenormalizerInterface {
protected $supportedInterfaceOrClass = ResourceIdentifier::class;
protected $fieldManager;
public function __construct(EntityFieldManagerInterface $field_manager) {
$this->fieldManager = $field_manager;
}
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);
}
public function denormalize($data, $class, $format = NULL, array $context = []) {
$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.');
}
$field_definition = $field_definitions[$context['related']];
$item_definition = $field_definition
->getItemDefinition();
$property_key = $item_definition
->getMainPropertyName();
$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 ($property_key, $target_resource_type_names) {
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'], isset($value['meta']) ? $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;
}
protected function massageRelationshipInput(array $data, $is_multiple) {
if ($is_multiple) {
if (!is_array($data['data'])) {
throw new BadRequestHttpException('Invalid body payload for the relationship.');
}
$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 {
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;
}
}