diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php index 97c5629..2a723bd 100644 --- a/src/Serializer/Serializer.php +++ b/src/Serializer/Serializer.php @@ -6,7 +6,6 @@ use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Cache\CacheableMetadata; use Drupal\jsonapi\Normalizer\CacheableNormalizerInterface as JsonApiCacheableNormalizerInterface; use Drupal\jsonapi\Normalizer\Value\CacheableNormalization; -use Drupal\jsonapi\Normalizer\Value\CacheableOmission; use Drupal\serialization\Normalizer\CacheableNormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -70,23 +69,19 @@ final class Serializer extends SymfonySerializer implements JsonApiCacheableNorm static::applyContextCacheability($context, $normalization); return $normalization->getNormalization(); } - // In case the data is traversable, normalize its children and unwrap any - // CacheableNormalizations so that the return type expectations for - // NormalizerInterface::normalize() are preserved. The cacheability will be - // preserved by adding it to the out-of-band context cacheability mechanism. + if ($this->fallbackNormalizer->supportsNormalization($data, $format, $context)) { + return $this->fallbackNormalizer->normalize($data, $format, $context); + } + // Before completely falling back to the fallback normalizer, if the data is + // an array or traversable, attempt to normalize it. if (\is_array($data) || $data instanceof \Traversable) { - $normalized = parent::normalize($data, $format, $context); - foreach ($normalized as $key => $item) { - if ($item instanceof CacheableOmission) { - static::applyContextCacheability($context, $item); - unset($normalized[$key]); - } - elseif ($item instanceof CacheableNormalization) { - static::applyContextCacheability($context, $item); - $normalized[$key] = $item->getNormalization(); - } + $normalized = []; + foreach ($data as $key => $val) { + $normalized[$key] = $this->getCacheableNormalization($val, $format, $context); } - return $normalized; + $normalization = CacheableNormalization::aggregate($normalized); + static::applyContextCacheability($context, $normalization); + return $normalization->getNormalization(); } // None of this serializer's normalizers are able to normalize the data, // so use the fallback normalizer. @@ -206,7 +201,7 @@ final class Serializer extends SymfonySerializer implements JsonApiCacheableNorm */ protected static function applyContextCacheability(array &$context, CacheableDependencyInterface $dependency) { $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = isset($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]) - ? $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]->merge($dependency) + ? $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]->addCacheableDependency($dependency) : CacheableMetadata::createFromObject($dependency); } diff --git a/tests/src/Kernel/Serializer/SerializerTest.php b/tests/src/Kernel/Serializer/SerializerTest.php index 8dc4033..2e21365 100644 --- a/tests/src/Kernel/Serializer/SerializerTest.php +++ b/tests/src/Kernel/Serializer/SerializerTest.php @@ -9,7 +9,6 @@ use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\Tests\jsonapi\Kernel\JsonapiKernelTestBase; use Drupal\user\Entity\User; -use Symfony\Component\Serializer\Exception\NotNormalizableValueException; /** * Tests the JSON:API serializer. @@ -90,13 +89,35 @@ class SerializerTest extends JsonapiKernelTestBase { */ public function testJsonApiNormalizer() { $context = ['account' => $this->user]; - + $expected_normalization = [ + 'value' => 'This is some text.', + 'format' => 'text_plain', + 'processed' => '', + ]; $data = $this->node->field_text; - $value = $this->sut->getCacheableNormalization($data, 'api_json', $context); - $this->assertTrue($value instanceof CacheableNormalization); + $array_of_data = [$data]; + $traversable_data = new \ArrayIterator($array_of_data); + + $normalization = $this->sut->normalize($data, 'api_json', $context); + $cacheable_normalization = $this->sut->getCacheableNormalization($data, 'api_json', $context); + + $this->assertSame($expected_normalization, $normalization); + $this->assertTrue($cacheable_normalization instanceof CacheableNormalization); + $this->assertSame($expected_normalization, $cacheable_normalization->getNormalization()); + + $array_normalization = $this->sut->normalize($array_of_data, 'api_json', $context); + $cacheable_array_normalization = $this->sut->getCacheableNormalization($array_of_data, 'api_json', $context); - $this->setExpectedException(\LogicException::class, 'JSON:API normalizers must be called with getCacheableNormalization() rather than normalize().'); - $this->sut->normalize($data, 'api_json', $context); + $this->assertSame([$expected_normalization], $array_normalization); + $this->assertTrue($cacheable_array_normalization instanceof CacheableNormalization); + $this->assertSame([$expected_normalization], $cacheable_array_normalization->getNormalization()); + + $traversable_normalization = $this->sut->normalize($traversable_data, 'api_json', $context); + $cacheable_traversable_normalization = $this->sut->getCacheableNormalization($traversable_data, 'api_json', $context); + + $this->assertSame([$expected_normalization], $traversable_normalization); + $this->assertTrue($cacheable_traversable_normalization instanceof CacheableNormalization); + $this->assertSame([$expected_normalization], $cacheable_traversable_normalization->getNormalization()); } /** @@ -107,12 +128,9 @@ class SerializerTest extends JsonapiKernelTestBase { */ public function testFallbackNormalizer($data, $expected) { $context = ['account' => $this->user]; - $value = $this->sut->normalize($data, 'api_json', $context); $this->assertSame($expected, $value); - - $this->setExpectedException(NotNormalizableValueException::class); - $this->sut->getCacheableNormalization($data, 'api_json', $context); + $this->assertSame($expected, $this->sut->getCacheableNormalization($data, 'api_json', $context)->getNormalization()); } /**