class EntityReferenceFieldNormalizer in JSON:API 8
Same name and namespace in other branches
- 8.2 src/Normalizer/EntityReferenceFieldNormalizer.php \Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer
Normalizer class specific for entity reference field objects.
@internal
Hierarchy
- class \Drupal\serialization\Normalizer\NormalizerBase implements \Symfony\Component\Serializer\SerializerAwareInterface, CacheableNormalizerInterface uses \Symfony\Component\Serializer\SerializerAwareTrait
- class \Drupal\jsonapi\Normalizer\NormalizerBase
- class \Drupal\jsonapi\Normalizer\FieldNormalizer
- class \Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface
- class \Drupal\jsonapi\Normalizer\FieldNormalizer
- class \Drupal\jsonapi\Normalizer\NormalizerBase
Expanded class hierarchy of EntityReferenceFieldNormalizer
1 file declares its use of EntityReferenceFieldNormalizer
- EntityReferenceFieldNormalizerTest.php in tests/
src/ Unit/ Normalizer/ EntityReferenceFieldNormalizerTest.php
1 string reference to 'EntityReferenceFieldNormalizer'
1 service uses EntityReferenceFieldNormalizer
File
- src/
Normalizer/ EntityReferenceFieldNormalizer.php, line 24
Namespace
Drupal\jsonapi\NormalizerView source
class EntityReferenceFieldNormalizer extends FieldNormalizer implements DenormalizerInterface {
/**
* {@inheritdoc}
*/
protected $supportedInterfaceOrClass = EntityReferenceFieldItemListInterface::class;
/**
* The link manager.
*
* @var \Drupal\jsonapi\LinkManager\LinkManager
*/
protected $linkManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $fieldManager;
/**
* The field plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $pluginManager;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* Instantiates a EntityReferenceFieldNormalizer object.
*
* @param \Drupal\jsonapi\LinkManager\LinkManager $link_manager
* The link manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager
* The entity field manager.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $plugin_manager
* The plugin manager for fields.
* @param \Drupal\jsonapi\ResourceType\ResourceTypeRepositoryInterface $resource_type_repository
* The JSON API resource type repository.
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository.
*/
public function __construct(LinkManager $link_manager, EntityFieldManagerInterface $field_manager, FieldTypePluginManagerInterface $plugin_manager, ResourceTypeRepositoryInterface $resource_type_repository, EntityRepositoryInterface $entity_repository) {
$this->linkManager = $link_manager;
$this->fieldManager = $field_manager;
$this->pluginManager = $plugin_manager;
$this->resourceTypeRepository = $resource_type_repository;
$this->entityRepository = $entity_repository;
}
/**
* {@inheritdoc}
*/
public function normalize($field, $format = NULL, array $context = []) {
/* @var \Drupal\Core\Field\FieldItemListInterface $field */
$field_access = $field
->access('view', $context['account'], TRUE);
if (!$field_access
->isAllowed()) {
return new NullFieldNormalizerValue($field_access, 'relationships');
}
// Build the relationship object based on the Entity Reference and normalize
// that object instead.
$main_property = $field
->getItemDefinition()
->getMainPropertyName();
$definition = $field
->getFieldDefinition();
$cardinality = $definition
->getFieldStorageDefinition()
->getCardinality();
$entity_list_metadata = [];
$entity_list = [];
foreach ($field as $item) {
// A non-empty entity reference field that refers to a non-existent entity
// is not a data integrity problem. For example, Term entities' "parent"
// entity reference field uses target_id zero to refer to the non-existent
// "<root>" term.
if (!$item
->isEmpty() && $item
->get('entity')
->getValue() === NULL) {
$entity_list[] = NULL;
$entity_list_metadata[] = [
'links' => [
'help' => [
'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#virtual',
'meta' => [
'about' => "Usage and meaning of the 'virtual' resource identifier.",
],
],
],
];
continue;
}
// Prepare a list of additional properties stored by the field.
$metadata = [];
/** @var \Drupal\Core\TypedData\TypedDataInterface[] $properties */
// @todo Remove this when JSON API requires Drupal 8.5 or newer.
$properties = floatval(\Drupal::VERSION) < 8.5 ? $item
->getProperties() : TypedDataInternalPropertiesHelper::getNonInternalProperties($item);
foreach ($properties as $property_key => $property) {
if ($property_key !== $main_property) {
$metadata[$property_key] = $this->serializer
->normalize($property, $format, $context);
}
}
$entity_list_metadata[] = $metadata;
// Get the referenced entity.
$entity = $item
->get('entity')
->getValue();
if ($this
->isInternalResourceType($entity)) {
continue;
}
// And get the translation in the requested language.
$entity_list[] = $this->entityRepository
->getTranslationFromContext($entity);
}
$entity_collection = new EntityCollection($entity_list);
$relationship = new Relationship($this->resourceTypeRepository, $field
->getName(), $entity_collection, $field
->getEntity(), $field_access, $cardinality, $main_property, $entity_list_metadata);
return $this->serializer
->normalize($relationship, $format, $context);
}
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = []) {
// If we get to here is through a write method on a relationship operation.
/** @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']];
// This is typically 'target_id'.
$item_definition = $field_definition
->getItemDefinition();
$property_key = $item_definition
->getMainPropertyName();
$target_resource_types = $resource_type
->getRelatableResourceTypesByField($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);
$values = array_map(function ($value) use ($property_key, $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 mach the destination resource types (%s).', $value['type'], implode(', ', $target_resource_type_names)));
}
// Load the entity by UUID.
list($entity_type_id, ) = explode('--', $value['type']);
$entity = $this->entityRepository
->loadEntityByUuid($entity_type_id, $value['id']);
$value['id'] = $entity ? $entity
->id() : NULL;
$properties = [
$property_key => $value['id'],
];
// Also take into account additional properties provided by the field
// type.
if (!empty($value['meta'])) {
foreach ($value['meta'] as $meta_key => $meta_value) {
$properties[$meta_key] = $meta_value;
}
}
return $properties;
}, $data['data']);
return $this->pluginManager
->createFieldItemList($context['target_entity'], $context['related'], $values);
}
/**
* 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;
}
/**
* Determines if the given entity is of an internal resource type.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check the internal status.
*
* @return bool
* TRUE if the entity's resource type is internal, FALSE otherwise.
*/
protected function isInternalResourceType(EntityInterface $entity) {
return ($resource_type = $this->resourceTypeRepository
->get($entity
->getEntityTypeId(), $entity
->bundle())) && $resource_type
->isInternal();
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
CacheableNormalizerInterface:: |
constant | Name of key for bubbling cacheability metadata via serialization context. | ||
EntityReferenceFieldNormalizer:: |
protected | property | The entity repository. | |
EntityReferenceFieldNormalizer:: |
protected | property | The entity field manager. | |
EntityReferenceFieldNormalizer:: |
protected | property | The link manager. | |
EntityReferenceFieldNormalizer:: |
protected | property | The field plugin manager. | |
EntityReferenceFieldNormalizer:: |
protected | property |
The interface or class that this Normalizer supports. Overrides FieldNormalizer:: |
|
EntityReferenceFieldNormalizer:: |
public | function |
Denormalizes data back into an object of the given class. Overrides FieldNormalizer:: |
|
EntityReferenceFieldNormalizer:: |
protected | function | Determines if the given entity is of an internal resource type. | |
EntityReferenceFieldNormalizer:: |
protected | function | Validates and massages the relationship input depending on the cardinality. | |
EntityReferenceFieldNormalizer:: |
public | function |
Normalizes an object into a set of arrays/scalars. Overrides FieldNormalizer:: |
|
EntityReferenceFieldNormalizer:: |
public | function | Instantiates a EntityReferenceFieldNormalizer object. | |
FieldNormalizer:: |
protected | property |
The formats that the Normalizer can handle. Overrides NormalizerBase:: |
|
FieldNormalizer:: |
protected static | function | Checks if the passed field is a relationship field. | |
FieldNormalizer:: |
protected | function | Helper function to normalize field items. | |
NormalizerBase:: |
protected | property | List of formats which supports (de-)normalization. | 3 |
NormalizerBase:: |
protected | function | Adds cacheability if applicable. | |
NormalizerBase:: |
protected | function | Checks if the provided format is supported by this normalizer. | 2 |
NormalizerBase:: |
public | function |
Implements \Symfony\Component\Serializer\Normalizer\DenormalizerInterface::supportsDenormalization() Overrides NormalizerBase:: |
|
NormalizerBase:: |
public | function |
Checks whether the given class is supported for normalization by this normalizer. Overrides NormalizerBase:: |