View source
<?php
namespace Drupal\jsonapi\Normalizer;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\jsonapi\EventSubscriber\ResourceObjectNormalizationCacher;
use Drupal\jsonapi\JsonApiResource\Relationship;
use Drupal\jsonapi\JsonApiResource\ResourceObject;
use Drupal\jsonapi\Normalizer\Value\CacheableNormalization;
use Drupal\jsonapi\Normalizer\Value\CacheableOmission;
class ResourceObjectNormalizer extends NormalizerBase {
protected $supportedInterfaceOrClass = ResourceObject::class;
protected $cacher;
public function __construct(ResourceObjectNormalizationCacher $cacher) {
$this->cacher = $cacher;
}
public function supportsDenormalization($data, $type, $format = NULL) {
return FALSE;
}
public function normalize($object, $format = NULL, array $context = []) {
assert($object instanceof ResourceObject);
$context['resource_object'] = $object;
$resource_type = $object
->getResourceType();
$resource_type_name = $resource_type
->getTypeName();
$fields = $object
->getFields();
if (!empty($context['sparse_fieldset'][$resource_type_name])) {
$field_names = $context['sparse_fieldset'][$resource_type_name];
}
else {
$field_names = array_keys($fields);
}
$normalization_parts = $this
->getNormalization($field_names, $object, $format, $context);
$fields = $normalization_parts[ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_FIELDS];
$field_normalizations = array_intersect_key($fields, array_flip($field_names));
$relationship_field_names = array_keys($resource_type
->getRelatableResourceTypes());
$attributes = array_diff_key($field_normalizations, array_flip($relationship_field_names));
$relationships = array_intersect_key($field_normalizations, array_flip($relationship_field_names));
$entity_normalization = array_filter($normalization_parts[ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_BASE] + [
'attributes' => CacheableNormalization::aggregate($attributes)
->omitIfEmpty(),
'relationships' => CacheableNormalization::aggregate($relationships)
->omitIfEmpty(),
]);
return CacheableNormalization::aggregate($entity_normalization)
->withCacheableDependency($object);
}
protected function getNormalization(array $field_names, ResourceObject $object, $format = NULL, array $context = []) {
$cached_normalization_parts = $this->cacher
->get($object);
$normalizer_values = $cached_normalization_parts !== FALSE ? $cached_normalization_parts : static::buildEmptyNormalization($object);
$fields =& $normalizer_values[ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_FIELDS];
$non_cached_fields = array_diff_key($object
->getFields(), $fields);
$non_cached_requested_fields = array_intersect_key($non_cached_fields, array_flip($field_names));
foreach ($non_cached_requested_fields as $field_name => $field) {
$fields[$field_name] = $this
->serializeField($field, $context, $format);
}
$base =& $normalizer_values[ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_BASE];
$base['links'] = isset($base['links']) ? $base['links'] : $this->serializer
->normalize($object
->getLinks(), $format, $context)
->omitIfEmpty();
if (!empty($non_cached_requested_fields)) {
$this->cacher
->saveOnTerminate($object, $normalizer_values);
}
return $normalizer_values;
}
protected static function buildEmptyNormalization(ResourceObject $object) {
return [
ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_BASE => [
'type' => CacheableNormalization::permanent($object
->getResourceType()
->getTypeName()),
'id' => CacheableNormalization::permanent($object
->getId()),
],
ResourceObjectNormalizationCacher::RESOURCE_CACHE_SUBSET_FIELDS => [],
];
}
protected function serializeField($field, array $context, $format) {
if ($field instanceof FieldItemListInterface) {
$field_access_result = $field
->access('view', $context['account'], TRUE);
if (!$field_access_result
->isAllowed()) {
return new CacheableOmission(CacheableMetadata::createFromObject($field_access_result));
}
if ($field instanceof EntityReferenceFieldItemListInterface) {
assert(!empty($context['resource_object']) && $context['resource_object'] instanceof ResourceObject);
$resource_object = $context['resource_object'];
$relationship = Relationship::createFromEntityReferenceField($resource_object, $field);
$normalized_field = $this->serializer
->normalize($relationship, $format, $context);
}
else {
$normalized_field = $this->serializer
->normalize($field, $format, $context);
}
assert($normalized_field instanceof CacheableNormalization);
return $normalized_field
->withCacheableDependency(CacheableMetadata::createFromObject($field_access_result));
}
else {
if ($context['resource_object']
->getResourceType()
->getDeserializationTargetClass() === 'Drupal\\layout_builder\\Entity\\LayoutBuilderEntityViewDisplay' && $context['resource_object']
->getField('third_party_settings') === $field) {
unset($field['layout_builder']['sections']);
}
return CacheableNormalization::permanent($field);
}
}
}