src/Normalizer/EntityNormalizer.php | 39 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Normalizer/EntityNormalizer.php b/src/Normalizer/EntityNormalizer.php index 0f3e99d..5b3167f 100644 --- a/src/Normalizer/EntityNormalizer.php +++ b/src/Normalizer/EntityNormalizer.php @@ -121,21 +121,37 @@ class EntityNormalizer extends NormalizerBase implements DenormalizerInterface { $entity->bundle() ); $resource_type_name = $resource_type->getTypeName(); + // Get the bundle ID of the requested resource. This is used to determine if + // this is a bundle level resource or an entity level resource. + $bundle = $resource_type->getBundle(); + if (!empty($context['sparse_fieldset'][$resource_type_name])) { + $field_names = $context['sparse_fieldset'][$resource_type_name]; + } + else { + $field_names = $this->getFieldNames($entity, $bundle, $resource_type); + } $cached_normalization_parts = $this->cacheGet($resource_type, $entity); if ($cached_normalization_parts !== FALSE) { $normalization_parts = $cached_normalization_parts; + // Check if some of the currently requested sparse fieldset has not yet + // been normalized in the cache item. If there are any missing fields, + // normalize those too, and update the cache. + $missing = array_diff($field_names, array_keys($normalization_parts['fields'])); + if ($missing) { + $missing_normalization_parts = $this->getPartialNormalization($missing, $entity, $resource_type, $format, $context); + $normalization_parts['fields'] += $missing_normalization_parts['fields']; + $this->cacheSet($resource_type, $entity, $normalization_parts); + } } else { - $normalization_parts = $this->getPartialNormalization($entity, $resource_type, $format, $context); + $normalization_parts = $this->getPartialNormalization($field_names, $entity, $resource_type, $format, $context); $this->cacheSet($resource_type, $entity, $normalization_parts); } - // If a sparse fieldset is active during this normalization, keep only the - // requested fields. - $field_normalizations = (!empty($context['sparse_fieldset'][$resource_type_name])) - ? array_intersect_key($normalization_parts['fields'], array_flip($context['sparse_fieldset'][$resource_type_name])) - : $normalization_parts['fields']; + // Keep only the requested fields (the cached normalization gradually grows + // to the complete set of fields). + $field_normalizations = array_intersect_key($normalization_parts['fields'], array_flip($field_names)); $entity_normalizaton = $normalization_parts['base']; $relationship_field_names = array_keys($resource_type->getRelatableResourceTypes()); @@ -149,6 +165,8 @@ class EntityNormalizer extends NormalizerBase implements DenormalizerInterface { /** * Normalizes an entity into a partial normalization. * + * @param string[] $field_names + * The field names to normalize (the sparse fieldset, if any). * @param \Drupal\Core\Entity\EntityInterface $entity * The entity to partially normalize. * @param \Drupal\jsonapi\ResourceType\ResourceType $resource_type @@ -162,15 +180,16 @@ class EntityNormalizer extends NormalizerBase implements DenormalizerInterface { * An array with two key-value pairs: * - 'base': array, the base normalization of the entity, that does not * depend on which sparse fieldset was requested. - * - 'fields': CacheableNormalization for every field on this entity, of - * which a subset will need to be selected depending on the - * requested sparse fieldset. + * - 'fields': CacheableNormalization for all requested fields. * * @see ::normalize() */ - private function getPartialNormalization(EntityInterface $entity, ResourceType $resource_type, $format = NULL, array $context = []) { + private function getPartialNormalization(array $field_names, EntityInterface $entity, ResourceType $resource_type, $format = NULL, array $context = []) { $normalizer_values = []; foreach ($this->getFields($entity, $resource_type->getBundle(), $resource_type) as $field_name => $field) { + if (!in_array($field_name, $field_names, TRUE)) { + continue; + } $normalizer_values[$field_name] = $this->serializeField($field, $context, $format); } // Create the array of normalized fields, starting with the URI.