vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php line 52

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the API Platform project.
  4. *
  5. * (c) Kévin Dunglas <dunglas@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\JsonLd\Serializer;
  12. use ApiPlatform\Api\IriConverterInterface;
  13. use ApiPlatform\Api\ResourceClassResolverInterface;
  14. use ApiPlatform\Api\UrlGeneratorInterface;
  15. use ApiPlatform\Core\Api\IriConverterInterface as LegacyIriConverterInterface;
  16. use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
  17. use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
  18. use ApiPlatform\JsonLd\AnonymousContextBuilderInterface;
  19. use ApiPlatform\JsonLd\ContextBuilderInterface;
  20. use ApiPlatform\Metadata\HttpOperation;
  21. use ApiPlatform\Serializer\AbstractItemNormalizer;
  22. use ApiPlatform\Serializer\ContextTrait;
  23. use ApiPlatform\Symfony\Security\ResourceAccessCheckerInterface;
  24. use ApiPlatform\Util\ClassInfoTrait;
  25. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  26. use Symfony\Component\Serializer\Exception\LogicException;
  27. use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
  28. use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
  29. use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
  30. /**
  31. * Converts between objects and array including JSON-LD and Hydra metadata.
  32. *
  33. * @author Kévin Dunglas <dunglas@gmail.com>
  34. */
  35. final class ItemNormalizer extends AbstractItemNormalizer
  36. {
  37. use ClassInfoTrait;
  38. use ContextTrait;
  39. use JsonLdContextTrait;
  40. public const FORMAT = 'jsonld';
  41. private $contextBuilder;
  42. public function __construct($resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ContextBuilderInterface $contextBuilder, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, array $defaultContext = [], iterable $dataTransformers = [], ResourceAccessCheckerInterface $resourceAccessChecker = null)
  43. {
  44. parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, null, false, $defaultContext, $dataTransformers, $resourceMetadataFactory, $resourceAccessChecker);
  45. if ($iriConverter instanceof LegacyIriConverterInterface) {
  46. trigger_deprecation('api-platform/core', '2.7', sprintf('Use an implementation of "%s" instead of "%s".', IriConverterInterface::class, LegacyIriConverterInterface::class));
  47. }
  48. $this->contextBuilder = $contextBuilder;
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function supportsNormalization($data, $format = null, array $context = []): bool
  54. {
  55. return self::FORMAT === $format && parent::supportsNormalization($data, $format, $context);
  56. }
  57. /**
  58. * {@inheritdoc}
  59. *
  60. * @throws LogicException
  61. *
  62. * @return array|string|int|float|bool|\ArrayObject|null
  63. */
  64. public function normalize($object, $format = null, array $context = [])
  65. {
  66. $resourceClass = $this->getObjectClass($object);
  67. if ($outputClass = $this->getOutputClass($resourceClass, $context) && !isset($context[self::IS_TRANSFORMED_TO_SAME_CLASS])) {
  68. return parent::normalize($object, $format, $context);
  69. }
  70. // TODO: we should not remove the resource_class in the normalizeRawCollection as we would find out anyway that it's not the same as the requested one
  71. $previousResourceClass = $context['resource_class'] ?? null;
  72. $metadata = [];
  73. if ($isResourceClass = $this->resourceClassResolver->isResourceClass($resourceClass)) {
  74. $resourceClass = $this->resourceClassResolver->getResourceClass($object, $resourceClass);
  75. $context = $this->initContext($resourceClass, $context);
  76. $metadata = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
  77. } elseif ($this->contextBuilder instanceof AnonymousContextBuilderInterface) {
  78. if ($context['api_collection_sub_level'] ?? false) {
  79. unset($context['api_collection_sub_level']);
  80. $context['output']['genid'] = true;
  81. $context['output']['iri'] = null;
  82. }
  83. // We should improve what's behind the context creation, its probably more complicated then it should
  84. $metadata = $this->createJsonLdContext($this->contextBuilder, $object, $context);
  85. }
  86. if (isset($context['operation']) && $previousResourceClass !== $resourceClass) {
  87. unset($context['operation'], $context['operation_name']);
  88. }
  89. if ($this->iriConverter instanceof LegacyIriConverterInterface) {
  90. $iri = $this->iriConverter->getIriFromItem($object);
  91. } else {
  92. $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context);
  93. }
  94. if (isset($iri)) {
  95. $context['iri'] = $iri;
  96. $metadata['@id'] = $iri;
  97. }
  98. $context['api_normalize'] = true;
  99. $data = parent::normalize($object, $format, $context);
  100. if (!\is_array($data)) {
  101. return $data;
  102. }
  103. // TODO: remove in 3.0
  104. if ($this->resourceMetadataFactory instanceof ResourceMetadataFactoryInterface) {
  105. $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
  106. $metadata['@type'] = $resourceMetadata->getIri() ?: $resourceMetadata->getShortName();
  107. } elseif ($this->resourceMetadataFactory) {
  108. $operation = $context['operation'] ?? $this->resourceMetadataFactory->create($resourceClass)->getOperation();
  109. $types = $operation instanceof HttpOperation ? $operation->getTypes() : null;
  110. if (null === $types) {
  111. $types = [$operation->getShortName()];
  112. }
  113. $metadata['@type'] = 1 === \count($types) ? $types[0] : $types;
  114. }
  115. return $metadata + $data;
  116. }
  117. /**
  118. * {@inheritdoc}
  119. */
  120. public function supportsDenormalization($data, $type, $format = null, array $context = []): bool
  121. {
  122. return self::FORMAT === $format && parent::supportsDenormalization($data, $type, $format, $context);
  123. }
  124. /**
  125. * {@inheritdoc}
  126. *
  127. * @throws NotNormalizableValueException
  128. *
  129. * @return mixed
  130. */
  131. public function denormalize($data, $class, $format = null, array $context = [])
  132. {
  133. // Avoid issues with proxies if we populated the object
  134. if (isset($data['@id']) && !isset($context[self::OBJECT_TO_POPULATE])) {
  135. if (true !== ($context['api_allow_update'] ?? true)) {
  136. throw new NotNormalizableValueException('Update is not allowed for this operation.');
  137. }
  138. $context[self::OBJECT_TO_POPULATE] = $this->iriConverter instanceof LegacyIriConverterInterface ? $this->iriConverter->getItemFromIri($data['@id'], $context + ['fetch_data' => true]) : $this->iriConverter->getResourceFromIri($data['@id'], $context + ['fetch_data' => true]);
  139. }
  140. return parent::denormalize($data, $class, $format, $context);
  141. }
  142. }
  143. class_alias(ItemNormalizer::class, \ApiPlatform\Core\JsonLd\Serializer\ItemNormalizer::class);