From ac1b6356d37287f7aa2aa97ffb4d1819fbacac7d Mon Sep 17 00:00:00 2001 From: Thibaut Cholley Date: Sun, 21 Jun 2026 09:42:14 +0200 Subject: [PATCH] refactor: introduce inputClass ouputClass dataClass everywhere --- ...meterResourceMetadataCollectionFactory.php | 3 +- ...DbOdmResourceCollectionMetadataFactory.php | 4 +- ...trineOdmOperationResourceClassResolver.php | 2 +- src/Doctrine/Odm/State/CollectionProvider.php | 2 +- src/Doctrine/Odm/State/ItemProvider.php | 2 +- src/Doctrine/Odm/State/LinksHandlerTrait.php | 6 +- src/Doctrine/Odm/State/Options.php | 9 +- ...mResourceCollectionMetadataFactoryTest.php | 6 +- .../Resource/DoctrineOrmLinkFactory.php | 2 +- ...meterResourceMetadataCollectionFactory.php | 3 +- ...neOrmResourceCollectionMetadataFactory.php | 4 +- ...trineOrmOperationResourceClassResolver.php | 2 +- src/Doctrine/Orm/State/CollectionProvider.php | 2 +- src/Doctrine/Orm/State/ItemProvider.php | 2 +- src/Doctrine/Orm/State/LinksHandlerTrait.php | 4 +- src/Doctrine/Orm/State/Options.php | 9 +- ...mResourceCollectionMetadataFactoryTest.php | 8 +- .../State/CollectionProvider.php | 2 +- src/Elasticsearch/State/ItemProvider.php | 2 +- src/GraphQl/Serializer/ItemNormalizer.php | 2 +- .../Serializer/SerializerContextBuilder.php | 12 +- .../State/Processor/NormalizeProcessor.php | 2 +- .../State/Provider/DenormalizeProvider.php | 4 +- src/GraphQl/State/Provider/ReadProvider.php | 4 +- .../State/Provider/ResolverProvider.php | 2 +- .../Processor/NormalizeProcessorTest.php | 8 +- .../Provider/DenormalizeProviderTest.php | 16 +-- src/GraphQl/Type/FieldsBuilder.php | 4 +- src/GraphQl/Type/TypeBuilder.php | 12 +- src/Hal/Serializer/EntrypointNormalizer.php | 2 +- src/HttpCache/State/AddTagsProcessor.php | 4 +- .../CollectionFiltersNormalizer.php | 2 +- .../Serializer/DocumentationNormalizer.php | 13 +- src/Hydra/State/JsonStreamerProcessor.php | 5 +- src/Hydra/State/JsonStreamerProvider.php | 2 +- .../ValueTransformer/IriValueTransformer.php | 2 +- src/JsonLd/Serializer/ItemNormalizer.php | 4 +- src/JsonSchema/DefinitionNameFactory.php | 4 +- src/JsonSchema/ResourceMetadataTrait.php | 7 +- .../Controller/ApiPlatformController.php | 2 +- ...quentResourceCollectionMetadataFactory.php | 5 +- .../Eloquent/State/CollectionProvider.php | 6 +- src/Laravel/Eloquent/State/ItemProvider.php | 2 +- src/Laravel/Eloquent/State/Options.php | 9 +- src/Laravel/Exception/ErrorRenderer.php | 2 +- src/Laravel/Routing/IriConverter.php | 6 +- ...EloquentOperationResourceClassResolver.php | 2 +- src/Laravel/State/AccessCheckerProvider.php | 2 +- src/Laravel/State/SwaggerUiProvider.php | 4 +- src/Laravel/routes/api.php | 2 +- src/Mcp/Capability/Registry/Loader.php | 4 +- src/Mcp/Server/Handler.php | 2 +- src/Mcp/State/StructuredContentProcessor.php | 2 +- src/Mcp/State/ToolProvider.php | 2 +- src/Metadata/ApiResource.php | 12 ++ src/Metadata/Get.php | 4 +- src/Metadata/HttpOperation.php | 4 +- src/Metadata/IdentifiersExtractor.php | 8 +- src/Metadata/Metadata.php | 132 +++++++++++++++++- src/Metadata/Operation.php | 14 +- ...actorResourceMetadataCollectionFactory.php | 2 +- ...utputResourceMetadataCollectionFactory.php | 21 ++- src/Metadata/Resource/Factory/LinkFactory.php | 8 +- .../MetadataCollectionFactoryTrait.php | 2 +- .../ObjectMapperMetadataCollectionFactory.php | 21 ++- .../Factory/OperationDefaultsTrait.php | 10 +- ...meterResourceMetadataCollectionFactory.php | 4 +- ...pFileResourceMetadataCollectionFactory.php | 2 +- .../PhpFileResourceNameCollectionFactory.php | 2 +- ...plateResourceMetadataCollectionFactory.php | 8 +- .../Tests/IdentifiersExtractorTest.php | 4 +- ...sResourceMetadataCollectionFactoryTest.php | 4 +- src/OpenApi/Factory/OpenApiFactory.php | 18 +-- src/Serializer/InputOutputMetadataTrait.php | 8 ++ src/Serializer/SerializerContextBuilder.php | 6 +- .../State/JsonStreamerProcessor.php | 5 +- src/Serializer/State/JsonStreamerProvider.php | 2 +- src/State/CreateProvider.php | 4 +- src/State/DataOptionsInterface.php | 19 +++ src/State/ErrorProvider.php | 6 +- src/State/ObjectProvider.php | 4 +- .../ReadLinkParameterProvider.php | 2 +- .../Processor/ObjectMapperInputProcessor.php | 6 +- .../Processor/ObjectMapperOutputProcessor.php | 4 +- src/State/Processor/ObjectMapperProcessor.php | 7 +- src/State/Processor/SerializeProcessor.php | 4 +- src/State/Provider/BackedEnumProvider.php | 2 +- .../Provider/ContentNegotiationProvider.php | 6 +- src/State/Provider/DeserializeProvider.php | 4 +- src/State/Provider/ObjectMapperProvider.php | 2 +- src/State/Provider/ReadProvider.php | 2 +- src/State/UriVariablesResolverTrait.php | 2 +- src/State/Util/HttpResponseHeadersTrait.php | 17 +-- src/State/Util/HttpResponseStatusTrait.php | 3 +- .../Bundle/SwaggerUi/SwaggerUiProvider.php | 4 +- src/Symfony/Controller/MainController.php | 6 +- .../EventListener/AddFormatListener.php | 2 +- .../EventListener/DeserializeListener.php | 2 +- src/Symfony/EventListener/ErrorListener.php | 2 +- .../JsonStreamerDeserializeListener.php | 2 +- .../JsonStreamerSerializeListener.php | 2 +- src/Symfony/EventListener/ReadListener.php | 4 +- src/Symfony/EventListener/RespondListener.php | 2 +- .../EventListener/SerializeListener.php | 2 +- .../EventListener/ValidateListener.php | 2 +- src/Symfony/EventListener/WriteListener.php | 4 +- src/Symfony/Routing/IriConverter.php | 8 +- .../Security/State/AccessCheckerProvider.php | 2 +- ...ationResourceMetadataCollectionFactory.php | 2 +- .../State/ContainNonResourceProvider.php | 2 +- .../State/DummyDtoInputOutputProcessor.php | 8 +- .../State/DummyDtoInputOutputProvider.php | 2 +- .../TestBundle/State/FakeProvider.php | 2 +- .../State/RelatedQuestionsProvider.php | 4 +- .../TestBundle/State/SerializableProvider.php | 2 +- 115 files changed, 446 insertions(+), 252 deletions(-) create mode 100644 src/State/DataOptionsInterface.php diff --git a/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmParameterResourceMetadataCollectionFactory.php b/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmParameterResourceMetadataCollectionFactory.php index 8bce8b8ddb5..617951967d3 100644 --- a/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmParameterResourceMetadataCollectionFactory.php +++ b/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmParameterResourceMetadataCollectionFactory.php @@ -13,7 +13,6 @@ namespace ApiPlatform\Doctrine\Odm\Metadata\Resource; -use ApiPlatform\Doctrine\Odm\State\Options; use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; @@ -79,7 +78,7 @@ private function enrichOperation(Operation $operation, string $resourceClass): O return $operation; } - $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $documentClass = $operation->getDataClass(); if (!$this->managerRegistry->getManagerForClass($documentClass) instanceof DocumentManager) { return $operation; } diff --git a/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactory.php b/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactory.php index c5ceb77c329..e8efa60166f 100644 --- a/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactory.php +++ b/src/Doctrine/Odm/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactory.php @@ -45,7 +45,7 @@ public function create(string $resourceClass): ResourceMetadataCollection if ($operations) { foreach ($resourceMetadata->getOperations() as $operationName => $operation) { - $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $documentClass = $operation->getDataClass(); if (!$this->managerRegistry->getManagerForClass($documentClass) instanceof DocumentManager) { continue; } @@ -62,7 +62,7 @@ public function create(string $resourceClass): ResourceMetadataCollection if ($graphQlOperations) { foreach ($graphQlOperations as $operationName => $graphQlOperation) { - $documentClass = $this->getStateOptionsClass($graphQlOperation, $graphQlOperation->getClass(), Options::class); + $documentClass = $graphQlOperation->getDataClass(); if (!$this->managerRegistry->getManagerForClass($documentClass) instanceof DocumentManager) { continue; } diff --git a/src/Doctrine/Odm/Serializer/DoctrineOdmOperationResourceClassResolver.php b/src/Doctrine/Odm/Serializer/DoctrineOdmOperationResourceClassResolver.php index bb5afc41cf2..9f0b62e1d25 100644 --- a/src/Doctrine/Odm/Serializer/DoctrineOdmOperationResourceClassResolver.php +++ b/src/Doctrine/Odm/Serializer/DoctrineOdmOperationResourceClassResolver.php @@ -44,7 +44,7 @@ public function resolve(object|string $resource, Operation $operation): string // Validate object matches the backing document class if ($documentClass && is_a($objectClass, $documentClass, true)) { - return $operation->getClass(); + return $operation->getApiClass(); } } diff --git a/src/Doctrine/Odm/State/CollectionProvider.php b/src/Doctrine/Odm/State/CollectionProvider.php index 6c68b663f3e..e7bd1b66d19 100644 --- a/src/Doctrine/Odm/State/CollectionProvider.php +++ b/src/Doctrine/Odm/State/CollectionProvider.php @@ -47,7 +47,7 @@ public function __construct(ResourceMetadataCollectionFactoryInterface $resource public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable { - $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $documentClass = $operation->getDataClass(); /** @var DocumentManager $manager */ $manager = $this->managerRegistry->getManagerForClass($documentClass); diff --git a/src/Doctrine/Odm/State/ItemProvider.php b/src/Doctrine/Odm/State/ItemProvider.php index 50fc50f6253..f641b2d935b 100644 --- a/src/Doctrine/Odm/State/ItemProvider.php +++ b/src/Doctrine/Odm/State/ItemProvider.php @@ -50,7 +50,7 @@ public function __construct(ResourceMetadataCollectionFactoryInterface $resource public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { - $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $documentClass = $operation->getDataClass(); /** @var DocumentManager $manager */ $manager = $this->managerRegistry->getManagerForClass($documentClass); diff --git a/src/Doctrine/Odm/State/LinksHandlerTrait.php b/src/Doctrine/Odm/State/LinksHandlerTrait.php index 215f0cbb899..d4d5a3025f9 100644 --- a/src/Doctrine/Odm/State/LinksHandlerTrait.php +++ b/src/Doctrine/Odm/State/LinksHandlerTrait.php @@ -133,15 +133,15 @@ private function buildAggregation(string $toClass, array $links, array $identifi private function getLinkFromClass(Link $link, Operation $operation): string { - $documentClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $documentClass = $operation->getDataClass(); $fromClass = $link->getFromClass(); - if ($fromClass === $operation->getClass() && $documentClass) { + if ($fromClass === $operation->getApiClass() && $documentClass) { return $documentClass; } $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation(); - if ($documentClass = $this->getStateOptionsClass($operation, null, Options::class)) { + if ($documentClass = $operation->getDataClass()) { return $documentClass; } diff --git a/src/Doctrine/Odm/State/Options.php b/src/Doctrine/Odm/State/Options.php index 459d6bc49ec..aec5fd4d22e 100644 --- a/src/Doctrine/Odm/State/Options.php +++ b/src/Doctrine/Odm/State/Options.php @@ -14,9 +14,9 @@ namespace ApiPlatform\Doctrine\Odm\State; use ApiPlatform\Doctrine\Common\State\Options as CommonOptions; -use ApiPlatform\State\OptionsInterface; +use ApiPlatform\State\DataOptionsInterface; -class Options extends CommonOptions implements OptionsInterface +class Options extends CommonOptions implements DataOptionsInterface { /** * @param mixed $handleLinks experimental callable, typed mixed as we may want a service name in the future @@ -30,6 +30,11 @@ public function __construct( parent::__construct(handleLinks: $handleLinks); } + public function getDataClass(): ?string + { + return $this->documentClass; + } + public function getDocumentClass(): ?string { return $this->documentClass; diff --git a/src/Doctrine/Odm/Tests/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactoryTest.php b/src/Doctrine/Odm/Tests/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactoryTest.php index cf4d9046804..ce08afec438 100644 --- a/src/Doctrine/Odm/Tests/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactoryTest.php +++ b/src/Doctrine/Odm/Tests/Metadata/Resource/DoctrineMongoDbOdmResourceCollectionMetadataFactoryTest.php @@ -42,7 +42,7 @@ private function getResourceMetadataCollectionFactory(HttpOperation $operation) } $resourceMetadataCollectionFactory = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class); - $resourceMetadataCollectionFactory->create($operation->getClass())->willReturn(new ResourceMetadataCollection($operation->getClass(), [ + $resourceMetadataCollectionFactory->create($operation->getApiClass())->willReturn(new ResourceMetadataCollection($operation->getApiClass(), [ (new ApiResource()) ->withOperations( new Operations([$operation->getName() => $operation]) @@ -80,9 +80,9 @@ public function testWithProvider(HttpOperation $operation, ?string $expectedProv $objectManager = $this->prophesize(DocumentManager::class); $managerRegistry = $this->prophesize(ManagerRegistry::class); - $managerRegistry->getManagerForClass($operation->getClass())->willReturn($objectManager->reveal()); + $managerRegistry->getManagerForClass($operation->getApiClass())->willReturn($objectManager->reveal()); $resourceMetadataCollectionFactory = new DoctrineMongoDbOdmResourceCollectionMetadataFactory($managerRegistry->reveal(), $this->getResourceMetadataCollectionFactory($operation)); - $resourceMetadataCollection = $resourceMetadataCollectionFactory->create($operation->getClass()); + $resourceMetadataCollection = $resourceMetadataCollectionFactory->create($operation->getApiClass()); $this->assertSame($expectedProvider, $resourceMetadataCollection->getOperation($operation->getName())->getProvider()); $this->assertSame($expectedProvider, $resourceMetadataCollection->getOperation('graphql_'.$operation->getName())->getProvider()); $this->assertSame($expectedProcessor, $resourceMetadataCollection->getOperation($operation->getName())->getProcessor()); diff --git a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmLinkFactory.php b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmLinkFactory.php index bf958b80727..86405031d1d 100644 --- a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmLinkFactory.php +++ b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmLinkFactory.php @@ -54,7 +54,7 @@ public function createLinksFromRelations(Metadata $operation): array { $links = $this->linkFactory->createLinksFromRelations($operation); - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getDataClass(); if (!($manager = $this->managerRegistry->getManagerForClass($resourceClass)) instanceof EntityManagerInterface) { return $links; } diff --git a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmParameterResourceMetadataCollectionFactory.php b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmParameterResourceMetadataCollectionFactory.php index be342c758ce..d0f7881d1a5 100644 --- a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmParameterResourceMetadataCollectionFactory.php +++ b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmParameterResourceMetadataCollectionFactory.php @@ -13,7 +13,6 @@ namespace ApiPlatform\Doctrine\Orm\Metadata\Resource; -use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Metadata\Operation; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; use ApiPlatform\Metadata\Resource\ResourceMetadataCollection; @@ -77,7 +76,7 @@ private function enrichOperation(Operation $operation, string $resourceClass): O return $operation; } - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $entityClass = $operation->getDataClass(); if (!$this->managerRegistry->getManagerForClass($entityClass) instanceof EntityManagerInterface) { return $operation; } diff --git a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactory.php b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactory.php index e34e7c27491..a767039c920 100644 --- a/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactory.php +++ b/src/Doctrine/Orm/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactory.php @@ -47,7 +47,7 @@ public function create(string $resourceClass): ResourceMetadataCollection if ($operations) { foreach ($operations as $operationName => $operation) { - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $entityClass = $operation->getDataClass(); $manager = $this->managerRegistry->getManagerForClass($entityClass); if (!$manager instanceof EntityManagerInterface) { @@ -74,7 +74,7 @@ public function create(string $resourceClass): ResourceMetadataCollection if ($graphQlOperations) { foreach ($graphQlOperations as $operationName => $graphQlOperation) { - $entityClass = $this->getStateOptionsClass($graphQlOperation, $graphQlOperation->getClass(), Options::class); + $entityClass = $graphQlOperation->getDataClass(); if (!$this->managerRegistry->getManagerForClass($entityClass) instanceof EntityManagerInterface) { continue; diff --git a/src/Doctrine/Orm/Serializer/DoctrineOrmOperationResourceClassResolver.php b/src/Doctrine/Orm/Serializer/DoctrineOrmOperationResourceClassResolver.php index b6d2535b668..ed5411946cb 100644 --- a/src/Doctrine/Orm/Serializer/DoctrineOrmOperationResourceClassResolver.php +++ b/src/Doctrine/Orm/Serializer/DoctrineOrmOperationResourceClassResolver.php @@ -44,7 +44,7 @@ public function resolve(object|string $resource, Operation $operation): string // Validate object matches the backing entity class if ($entityClass && is_a($objectClass, $entityClass, true)) { - return $operation->getClass(); + return $operation->getApiClass(); } } diff --git a/src/Doctrine/Orm/State/CollectionProvider.php b/src/Doctrine/Orm/State/CollectionProvider.php index 3815447a8d3..c482b2146c8 100644 --- a/src/Doctrine/Orm/State/CollectionProvider.php +++ b/src/Doctrine/Orm/State/CollectionProvider.php @@ -50,7 +50,7 @@ public function __construct(ResourceMetadataCollectionFactoryInterface $resource public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable { - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $entityClass = $operation->getDataClass(); /** @var EntityManagerInterface $manager */ $manager = $this->managerRegistry->getManagerForClass($entityClass); diff --git a/src/Doctrine/Orm/State/ItemProvider.php b/src/Doctrine/Orm/State/ItemProvider.php index 536f6bb611f..b9593522958 100644 --- a/src/Doctrine/Orm/State/ItemProvider.php +++ b/src/Doctrine/Orm/State/ItemProvider.php @@ -50,7 +50,7 @@ public function __construct(ResourceMetadataCollectionFactoryInterface $resource public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $entityClass = $operation->getDataClass(); /** @var EntityManagerInterface|null $manager */ $manager = $this->managerRegistry->getManagerForClass($entityClass); diff --git a/src/Doctrine/Orm/State/LinksHandlerTrait.php b/src/Doctrine/Orm/State/LinksHandlerTrait.php index 72a445d09d6..f1d9ccbcb78 100644 --- a/src/Doctrine/Orm/State/LinksHandlerTrait.php +++ b/src/Doctrine/Orm/State/LinksHandlerTrait.php @@ -172,12 +172,12 @@ private function handleLinks(QueryBuilder $queryBuilder, array $identifiers, Que private function getLinkFromClass(Link $link, Operation $operation): string { $fromClass = $link->getFromClass(); - if ($fromClass === $operation->getClass() && $entityClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class)) { + if ($fromClass === $operation->getApiClass() && $entityClass = $operation->getDataClass()) { return $entityClass; } $operation = $this->resourceMetadataCollectionFactory->create($fromClass)->getOperation(); - return $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + return $operation->getDataClass(); } } diff --git a/src/Doctrine/Orm/State/Options.php b/src/Doctrine/Orm/State/Options.php index 3a9a46c3825..17157f71426 100644 --- a/src/Doctrine/Orm/State/Options.php +++ b/src/Doctrine/Orm/State/Options.php @@ -14,9 +14,9 @@ namespace ApiPlatform\Doctrine\Orm\State; use ApiPlatform\Doctrine\Common\State\Options as CommonOptions; -use ApiPlatform\State\OptionsInterface; +use ApiPlatform\State\DataOptionsInterface; -class Options extends CommonOptions implements OptionsInterface +class Options extends CommonOptions implements DataOptionsInterface { /** * @param string|callable $handleLinks experimental callable, typed mixed as we may want a service name in the future @@ -30,6 +30,11 @@ public function __construct( parent::__construct(handleLinks: $handleLinks); } + public function getDataClass(): ?string + { + return $this->entityClass; + } + public function getEntityClass(): ?string { return $this->entityClass; diff --git a/src/Doctrine/Orm/Tests/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactoryTest.php b/src/Doctrine/Orm/Tests/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactoryTest.php index db7a0d8eb23..14bef5dc9c2 100644 --- a/src/Doctrine/Orm/Tests/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactoryTest.php +++ b/src/Doctrine/Orm/Tests/Metadata/Resource/DoctrineOrmResourceCollectionMetadataFactoryTest.php @@ -43,7 +43,7 @@ class DoctrineOrmResourceCollectionMetadataFactoryTest extends TestCase private function getResourceMetadataCollectionFactory(HttpOperation $operation) { $resourceMetadataCollectionFactory = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class); - $resourceMetadataCollectionFactory->create($operation->getClass())->willReturn(new ResourceMetadataCollection($operation->getClass(), [ + $resourceMetadataCollectionFactory->create($operation->getDataClass())->willReturn(new ResourceMetadataCollection($operation->getApiClass(), [ (new ApiResource()) ->withOperations( new Operations([$operation->getName() => $operation]) @@ -72,11 +72,11 @@ public function testWithoutManager(): void public function testWithProvider(HttpOperation $operation, ?string $expectedProvider = null, ?string $expectedProcessor = null): void { $objectManager = $this->prophesize(EntityManagerInterface::class); - $objectManager->getClassMetadata($operation->getClass())->willReturn(new ClassMetadata(Dummy::class)); + $objectManager->getClassMetadata($operation->getDataClass())->willReturn(new ClassMetadata(Dummy::class)); $managerRegistry = $this->prophesize(ManagerRegistry::class); - $managerRegistry->getManagerForClass($operation->getClass())->willReturn($objectManager->reveal()); + $managerRegistry->getManagerForClass($operation->getDataClass())->willReturn($objectManager->reveal()); $resourceMetadataCollectionFactory = new DoctrineOrmResourceCollectionMetadataFactory($managerRegistry->reveal(), $this->getResourceMetadataCollectionFactory($operation)); - $resourceMetadataCollection = $resourceMetadataCollectionFactory->create($operation->getClass()); + $resourceMetadataCollection = $resourceMetadataCollectionFactory->create($operation->getDataClass()); $this->assertSame($expectedProvider, $resourceMetadataCollection->getOperation($operation->getName())->getProvider()); $this->assertSame($expectedProvider, $resourceMetadataCollection->getOperation('graphql_'.$operation->getName())->getProvider()); $this->assertSame($expectedProcessor, $resourceMetadataCollection->getOperation($operation->getName())->getProcessor()); diff --git a/src/Elasticsearch/State/CollectionProvider.php b/src/Elasticsearch/State/CollectionProvider.php index 4e78be7fa66..ca5703efb8a 100644 --- a/src/Elasticsearch/State/CollectionProvider.php +++ b/src/Elasticsearch/State/CollectionProvider.php @@ -55,7 +55,7 @@ public function __construct( */ public function provide(Operation $operation, array $uriVariables = [], array $context = []): Paginator { - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getDataClass(); $body = []; foreach ($this->collectionExtensions as $collectionExtension) { diff --git a/src/Elasticsearch/State/ItemProvider.php b/src/Elasticsearch/State/ItemProvider.php index 88321d26fa2..1639169d16a 100644 --- a/src/Elasticsearch/State/ItemProvider.php +++ b/src/Elasticsearch/State/ItemProvider.php @@ -49,7 +49,7 @@ public function __construct( */ public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getDataClass(); $options = $operation->getStateOptions(); if (!$options instanceof Options) { $options = new Options(index: $this->getIndex($operation)); diff --git a/src/GraphQl/Serializer/ItemNormalizer.php b/src/GraphQl/Serializer/ItemNormalizer.php index fddc30a7118..70020f6e508 100644 --- a/src/GraphQl/Serializer/ItemNormalizer.php +++ b/src/GraphQl/Serializer/ItemNormalizer.php @@ -79,7 +79,7 @@ public function normalize(mixed $data, ?string $format = null, array $context = if ($this->getOutputClass($context)) { $context['graphql_identifiers'] = [ - self::ITEM_RESOURCE_CLASS_KEY => $context['operation']->getClass(), + self::ITEM_RESOURCE_CLASS_KEY => $context['operation']->getApiClass(), self::ITEM_IDENTIFIERS_KEY => $this->identifiersExtractor->getIdentifiersFromItem($data, $context['operation'] ?? null), ]; diff --git a/src/GraphQl/Serializer/SerializerContextBuilder.php b/src/GraphQl/Serializer/SerializerContextBuilder.php index 9f332c5954a..9d1aff8a8e1 100644 --- a/src/GraphQl/Serializer/SerializerContextBuilder.php +++ b/src/GraphQl/Serializer/SerializerContextBuilder.php @@ -40,16 +40,18 @@ public function create(?string $resourceClass, Operation $operation, array $reso } $context['operation'] = $operation; - if ($operation->getInput()) { - $context['input'] = $operation->getInput(); + $apiClass = $operation->getApiClass(); + $dataClass = $operation->getDataClass(); + if (null !== ($inputClass = $operation->getInputClass()) && $inputClass !== $apiClass) { + $context['input'] = ['class' => $inputClass]; } - if ($operation->getOutput()) { - $context['output'] = $operation->getOutput(); + if (null !== ($outputClass = $operation->getOutputClass()) && $outputClass !== $apiClass) { + $context['output'] = ['class' => $outputClass]; } $context = $normalization ? array_merge($operation->getNormalizationContext() ?? [], $context) : array_merge($operation->getDenormalizationContext() ?? [], $context); if ($normalization) { - $context['attributes'] = $this->fieldsToAttributes($resourceClass, $operation, $resolverContext, $context); + $context['attributes'] = $this->fieldsToAttributes($dataClass, $operation, $resolverContext, $context); } // to keep the cache computation smaller, we have "operation_name" and "iri" anyways diff --git a/src/GraphQl/State/Processor/NormalizeProcessor.php b/src/GraphQl/State/Processor/NormalizeProcessor.php index 29fe2b9f75a..324e61a46e8 100644 --- a/src/GraphQl/State/Processor/NormalizeProcessor.php +++ b/src/GraphQl/State/Processor/NormalizeProcessor.php @@ -79,7 +79,7 @@ private function getData(mixed $itemOrCollection, GraphQlOperation $operation, a return null; } - $normalizationContext = $this->serializerContextBuilder->create($operation->getClass(), $operation, $context, normalization: true); + $normalizationContext = $this->serializerContextBuilder->create($operation->getApiClass(), $operation, $context, normalization: true); $data = null; if (!$operation instanceof CollectionOperationInterface) { diff --git a/src/GraphQl/State/Provider/DenormalizeProvider.php b/src/GraphQl/State/Provider/DenormalizeProvider.php index 481bb8bb6b0..466b08199bd 100644 --- a/src/GraphQl/State/Provider/DenormalizeProvider.php +++ b/src/GraphQl/State/Provider/DenormalizeProvider.php @@ -41,13 +41,13 @@ public function provide(Operation $operation, array $uriVariables = [], array $c return $data; } - $denormalizationContext = $this->serializerContextBuilder->create($operation->getClass(), $operation, $context, normalization: false); + $denormalizationContext = $this->serializerContextBuilder->create($operation->getApiClass(), $operation, $context, normalization: false); if (null !== $data) { $denormalizationContext[AbstractNormalizer::OBJECT_TO_POPULATE] = $data; } - $item = $this->denormalizer->denormalize($context['args']['input'], $operation->getClass(), ItemNormalizer::FORMAT, $denormalizationContext); + $item = $this->denormalizer->denormalize($context['args']['input'], $operation->getInputClass(), ItemNormalizer::FORMAT, $denormalizationContext); if (!\is_object($item)) { throw new \UnexpectedValueException('Expected item to be an object.'); diff --git a/src/GraphQl/State/Provider/ReadProvider.php b/src/GraphQl/State/Provider/ReadProvider.php index 08240ba0b2d..94c0d06ab01 100644 --- a/src/GraphQl/State/Provider/ReadProvider.php +++ b/src/GraphQl/State/Provider/ReadProvider.php @@ -53,7 +53,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c if ($this->serializerContextBuilder) { // Builtin data providers are able to use the serialization context to automatically add join clauses - $context += $this->serializerContextBuilder->create($operation->getClass(), $operation, $context, true); + $context += $this->serializerContextBuilder->create($operation->getApiClass(), $operation, $context, true); } if (!$operation instanceof CollectionOperationInterface) { @@ -78,7 +78,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c throw new NotFoundHttpException(\sprintf('Item "%s" not found.', $args['input']['id'])); } - if ($operation->getClass() !== $this->getObjectClass($item)) { + if ($operation->getDataClass() !== $this->getObjectClass($item)) { throw new \UnexpectedValueException(\sprintf('Item "%s" did not match expected type "%s".', $args['input']['id'], $operation->getShortName())); } } diff --git a/src/GraphQl/State/Provider/ResolverProvider.php b/src/GraphQl/State/Provider/ResolverProvider.php index 5d51f5c8d50..1cc95468c56 100644 --- a/src/GraphQl/State/Provider/ResolverProvider.php +++ b/src/GraphQl/State/Provider/ResolverProvider.php @@ -44,7 +44,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $item = $queryResolver($item, $context); if (!$operation instanceof CollectionOperationInterface) { // The item retrieved can be of another type when using an identifier (see Relay Nodes at query.feature:23) - $this->getResourceClass($item, $operation->getOutput()['class'] ?? $operation->getClass(), \sprintf('Custom query resolver "%s"', $queryResolverId).' has to return an item of class %s but returned an item of class %s.'); + $this->getResourceClass($item, $operation->getOutputClass() , \sprintf('Custom query resolver "%s"', $queryResolverId).' has to return an item of class %s but returned an item of class %s.'); } return $item; diff --git a/src/GraphQl/Tests/State/Processor/NormalizeProcessorTest.php b/src/GraphQl/Tests/State/Processor/NormalizeProcessorTest.php index 2d7ba11fab9..05f619c2d4f 100644 --- a/src/GraphQl/Tests/State/Processor/NormalizeProcessorTest.php +++ b/src/GraphQl/Tests/State/Processor/NormalizeProcessorTest.php @@ -47,10 +47,10 @@ protected function setUp(): void public function testProcess($body, $operation): void { $context = ['args' => []]; - $serializerContext = ['resource_class' => $operation->getClass()]; + $serializerContext = ['resource_class' => $operation->getApiClass()]; $normalizer = $this->createMock(NormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, true)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getApiClass(), $operation, $context, true)->willReturn($serializerContext); $normalizer->expects($this->once())->method('normalize')->with($body, 'graphql', $serializerContext); $processor = new NormalizeProcessor($normalizer, $serializerContextBuilder, new Pagination()); $processor->process($body, $operation, [], $context); @@ -70,11 +70,11 @@ public function testProcessCollection($collection, $operation, $args, ?array $ex { $this->resolveInfoProphecy->getFieldSelection(1)->willReturn($getFieldSelection); $context = ['args' => $args, 'info' => $this->resolveInfoProphecy->reveal()]; - $serializerContext = ['resource_class' => $operation->getClass()]; + $serializerContext = ['resource_class' => $operation->getApiClass()]; $normalizer = $this->prophesize(NormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, true)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getApiClass(), $operation, $context, true)->willReturn($serializerContext); foreach ($collection as $v) { $normalizer->normalize($v, 'graphql', $serializerContext)->willReturn(['normalized_item'])->shouldBeCalledOnce(); } diff --git a/src/GraphQl/Tests/State/Provider/DenormalizeProviderTest.php b/src/GraphQl/Tests/State/Provider/DenormalizeProviderTest.php index b1232566245..f9ed8f79e7c 100644 --- a/src/GraphQl/Tests/State/Provider/DenormalizeProviderTest.php +++ b/src/GraphQl/Tests/State/Provider/DenormalizeProviderTest.php @@ -28,12 +28,12 @@ public function testProvide(): void $objectToPopulate = null; $context = ['args' => ['input' => ['test']]]; $operation = new Mutation(class: \stdClass::class); - $serializerContext = ['resource_class' => $operation->getClass()]; + $serializerContext = ['resource_class' => $operation->getApiClass()]; $decorated = $this->createMock(ProviderInterface::class); $decorated->expects($this->once())->method('provide')->willReturn($objectToPopulate); $denormalizer = $this->createMock(DenormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, false)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getApiClass(), $operation, $context, false)->willReturn($serializerContext); $denormalizer->expects($this->once())->method('denormalize')->with(['test'], \stdClass::class, 'graphql', $serializerContext)->willReturn(new \stdClass()); $provider = new DenormalizeProvider($decorated, $denormalizer, $serializerContextBuilder); $provider->provide($operation, [], $context); @@ -44,12 +44,12 @@ public function testProvideWithObjectToPopulate(): void $objectToPopulate = new \stdClass(); $context = ['args' => ['input' => ['test']]]; $operation = new Mutation(class: \stdClass::class); - $serializerContext = ['resource_class' => $operation->getClass(), 'object_to_populate' => $objectToPopulate]; + $serializerContext = ['resource_class' => $operation->getApiClass(), 'object_to_populate' => $objectToPopulate]; $decorated = $this->createMock(ProviderInterface::class); $decorated->expects($this->once())->method('provide')->willReturn($objectToPopulate); $denormalizer = $this->createMock(DenormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getClass(), $operation, $context, false)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->once())->method('create')->with($operation->getApiClass(), $operation, $context, false)->willReturn($serializerContext); $denormalizer->expects($this->once())->method('denormalize')->with(['test'], \stdClass::class, 'graphql', $serializerContext)->willReturn(new \stdClass()); $provider = new DenormalizeProvider($decorated, $denormalizer, $serializerContextBuilder); $provider->provide($operation, [], $context); @@ -60,12 +60,12 @@ public function testProvideNotCalledWithQuery(): void $objectToPopulate = new \stdClass(); $context = ['args' => ['input' => ['test']]]; $operation = new Query(class: \stdClass::class); - $serializerContext = ['resource_class' => $operation->getClass()]; + $serializerContext = ['resource_class' => $operation->getApiClass()]; $decorated = $this->createMock(ProviderInterface::class); $decorated->expects($this->once())->method('provide')->willReturn($objectToPopulate); $denormalizer = $this->createMock(DenormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->never())->method('create')->with($operation->getClass(), $operation, $context, false)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->never())->method('create')->with($operation->getApiClass(), $operation, $context, false)->willReturn($serializerContext); $denormalizer->expects($this->never())->method('denormalize')->with(['test'], \stdClass::class, 'graphql', $serializerContext)->willReturn(new \stdClass()); $provider = new DenormalizeProvider($decorated, $denormalizer, $serializerContextBuilder); $provider->provide($operation, [], $context); @@ -76,12 +76,12 @@ public function testProvideNotCalledWithoutDeserialize(): void $objectToPopulate = new \stdClass(); $context = ['args' => ['input' => ['test']]]; $operation = new Query(class: \stdClass::class, deserialize: false); - $serializerContext = ['resource_class' => $operation->getClass()]; + $serializerContext = ['resource_class' => $operation->getApiClass()]; $decorated = $this->createMock(ProviderInterface::class); $decorated->expects($this->once())->method('provide')->willReturn($objectToPopulate); $denormalizer = $this->createMock(DenormalizerInterface::class); $serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class); - $serializerContextBuilder->expects($this->never())->method('create')->with($operation->getClass(), $operation, $context, false)->willReturn($serializerContext); + $serializerContextBuilder->expects($this->never())->method('create')->with($operation->getApiClass(), $operation, $context, false)->willReturn($serializerContext); $denormalizer->expects($this->never())->method('denormalize')->with(['test'], \stdClass::class, 'graphql', $serializerContext)->willReturn(new \stdClass()); $provider = new DenormalizeProvider($decorated, $denormalizer, $serializerContextBuilder); $provider->provide($operation, [], $context); diff --git a/src/GraphQl/Type/FieldsBuilder.php b/src/GraphQl/Type/FieldsBuilder.php index 7f63a2d1ba5..b674691ee5b 100644 --- a/src/GraphQl/Type/FieldsBuilder.php +++ b/src/GraphQl/Type/FieldsBuilder.php @@ -511,7 +511,7 @@ private function getParameterArgs(Operation $operation, array $args = []): array if ($filter = $this->resolveFilter($parameter->getFilter())) { $property = $parameter->getProperty() ?? $name; $property = str_replace('.', $this->nestingSeparator, $property); - $description = $filter->getDescription($operation->getClass()); + $description = $filter->getDescription($operation->getDataClass()); foreach ($description as $descKey => $descValue) { $descKey = str_replace('.', $this->nestingSeparator, $descKey); @@ -608,7 +608,7 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root continue; } - $entityClass = $this->getStateOptionsClass($resourceOperation, $resourceOperation->getClass()); + $entityClass = $this->getStateOptionsClass($resourceOperation, $resourceOperation->getApiClass()); foreach ($filter->getDescription($entityClass) as $key => $description) { $filterType = \in_array($description['type'], TypeIdentifier::values(), true) ? Type::builtin($description['type']) : Type::object($description['type']); if (!($description['required'] ?? false)) { diff --git a/src/GraphQl/Type/TypeBuilder.php b/src/GraphQl/Type/TypeBuilder.php index a0f346a54d4..73111b602ca 100644 --- a/src/GraphQl/Type/TypeBuilder.php +++ b/src/GraphQl/Type/TypeBuilder.php @@ -205,7 +205,7 @@ public function getEnumType(Operation $operation): GraphQLType /** @var FieldsBuilderEnumInterface $fieldsBuilder */ $fieldsBuilder = $this->fieldsBuilderLocator->get('api_platform.graphql.fields_builder'); $enumCases = []; - $enumCases = $fieldsBuilder->getEnumFields($operation->getClass()); + $enumCases = $fieldsBuilder->getEnumFields($operation->getDataClass()); $enumConfig = [ 'name' => $enumName, @@ -311,15 +311,13 @@ private function getQueryOperation(ResourceMetadataCollection $resourceMetadataC private function getResourceObjectTypeConfiguration(string $shortName, ResourceMetadataCollection $resourceMetadataCollection, Operation $operation, array $context = []): InputObjectType|ObjectType { $operationName = $operation->getName(); - $resourceClass = $operation->getClass(); $input = $context['input']; $depth = $context['depth'] ?? 0; $wrapped = $context['wrapped'] ?? false; - $ioMetadata = $input ? $operation->getInput() : $operation->getOutput(); - if (null !== $ioMetadata && \array_key_exists('class', $ioMetadata) && null !== $ioMetadata['class']) { - $resourceClass = $ioMetadata['class']; - } + $resourceClass = $input ? $operation->getInputClass() : $operation->getOutputClass(); + // Always pass ['class' => ...] so FieldsBuilder can detect disabled output/input (null class). + $ioMetadata = ['class' => $resourceClass]; $wrapData = !$wrapped && ($operation instanceof Mutation || $operation instanceof Subscription) && !$input && $depth < 1; @@ -343,7 +341,7 @@ private function getResourceObjectTypeConfiguration(string $shortName, ResourceM // The query type can only be reused when both operations produce the same output class. // A mutation declaring its own output class must expose that class on its payload. - if (!$useWrappedType && ($operation->getOutput()['class'] ?? null) !== ($queryOperation?->getOutput()['class'] ?? null)) { + if (!$useWrappedType && $operation->getOutputClass() !== ($queryOperation?->getOutputClass())) { $useWrappedType = true; } diff --git a/src/Hal/Serializer/EntrypointNormalizer.php b/src/Hal/Serializer/EntrypointNormalizer.php index 5a516d1e83e..70402652b7a 100644 --- a/src/Hal/Serializer/EntrypointNormalizer.php +++ b/src/Hal/Serializer/EntrypointNormalizer.php @@ -53,7 +53,7 @@ public function normalize(mixed $data, ?string $format = null, array $context = } try { - $href = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); + $href = $this->iriConverter->getIriFromResource($operation->getApiClass(), UrlGeneratorInterface::ABS_PATH, $operation); $entrypoint['_links'][lcfirst($operation->getShortName())]['href'] = $href; } catch (InvalidArgumentException) { // Ignore resources without GET operations diff --git a/src/HttpCache/State/AddTagsProcessor.php b/src/HttpCache/State/AddTagsProcessor.php index 4ea3386381c..9bd684aeb31 100644 --- a/src/HttpCache/State/AddTagsProcessor.php +++ b/src/HttpCache/State/AddTagsProcessor.php @@ -63,8 +63,8 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $resources = $request->attributes->get('_resources', []); if ($operation instanceof CollectionOperationInterface) { // Allows to purge collections - $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass()); - $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => $uriVariables]); + $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getApiClass()); + $iri = $this->iriConverter->getIriFromResource($operation->getApiClass(), UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => $uriVariables]); $resources[$iri] = $iri; } diff --git a/src/Hydra/Serializer/CollectionFiltersNormalizer.php b/src/Hydra/Serializer/CollectionFiltersNormalizer.php index edc3fee3a8a..84a2110aa16 100644 --- a/src/Hydra/Serializer/CollectionFiltersNormalizer.php +++ b/src/Hydra/Serializer/CollectionFiltersNormalizer.php @@ -105,7 +105,7 @@ public function normalize(mixed $data, ?string $format = null, array $context = } } - $resourceClass = $this->getStateOptionsClass($operation, $resourceClass); + $resourceClass = $operation->getDataClass(); if ($currentFilters || ($parameters && \count($parameters))) { $hydraPrefix = $this->getHydraPrefix($context + $this->defaultContext); diff --git a/src/Hydra/Serializer/DocumentationNormalizer.php b/src/Hydra/Serializer/DocumentationNormalizer.php index 428c6da7a94..2e61700e075 100644 --- a/src/Hydra/Serializer/DocumentationNormalizer.php +++ b/src/Hydra/Serializer/DocumentationNormalizer.php @@ -216,13 +216,11 @@ private function getHydraProperties(string $resourceClass, ApiResource $resource continue; } - $inputMetadata = $operation->getInput(); - if (null !== $inputClass = $inputMetadata['class'] ?? null) { + if (null !== ($inputClass = $operation->getInputClass())) { $classes[$inputClass] = true; } - $outputMetadata = $operation->getOutput(); - if (null !== $outputClass = $outputMetadata['class'] ?? null) { + if (null !== ($outputClass = $operation->getOutputClass())) { $classes[$outputClass] = true; } } @@ -288,11 +286,8 @@ private function getHydraOperation(HttpOperation $operation, string $prefixedSho } $shortName = $operation->getShortName(); - $inputMetadata = $operation->getInput() ?? []; - $outputMetadata = $operation->getOutput() ?? []; - - $inputClass = \array_key_exists('class', $inputMetadata) ? $inputMetadata['class'] : false; - $outputClass = \array_key_exists('class', $outputMetadata) ? $outputMetadata['class'] : false; + $inputClass = $operation->getInputClass(); + $outputClass = $operation->getOutputClass(); if ('GET' === $method && $operation instanceof CollectionOperationInterface) { $hydraOperation += [ diff --git a/src/Hydra/State/JsonStreamerProcessor.php b/src/Hydra/State/JsonStreamerProcessor.php index e2b787bd156..4ab0d266193 100644 --- a/src/Hydra/State/JsonStreamerProcessor.php +++ b/src/Hydra/State/JsonStreamerProcessor.php @@ -75,6 +75,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = || !($request = $context['request'] ?? null) || !$operation->getJsonStream() || 'jsonld' !== $request->getRequestFormat() + || null === ($outputClass = $operation->getOutputClass()) ) { return $this->processor?->process($data, $operation, $uriVariables, $context); } @@ -100,13 +101,13 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $data = $this->jsonStreamer->write( $collection, - Type::generic(Type::object($collection::class), Type::object($operation->getClass())), + Type::generic(Type::object($collection::class), Type::object($outputClass)), ['data' => $data, 'operation' => $operation], ); } else { $data = $this->jsonStreamer->write( $data, - Type::object($operation->getClass()), + Type::object($outputClass), ['data' => $data, 'operation' => $operation], ); } diff --git a/src/Hydra/State/JsonStreamerProvider.php b/src/Hydra/State/JsonStreamerProvider.php index 696d8c1edbe..a5c8e28a825 100644 --- a/src/Hydra/State/JsonStreamerProvider.php +++ b/src/Hydra/State/JsonStreamerProvider.php @@ -39,7 +39,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c return $data; } - $data = $this->jsonStreamReader->read($request->getContent(true), Type::object($operation->getClass())); + $data = $this->jsonStreamReader->read($request->getContent(true), Type::object($operation->getInputClass())); $context['request']->attributes->set('deserialized', true); if (\PHP_VERSION_ID > 80400) { diff --git a/src/JsonLd/JsonStreamer/ValueTransformer/IriValueTransformer.php b/src/JsonLd/JsonStreamer/ValueTransformer/IriValueTransformer.php index 383ed920c8a..7f538f09cd6 100644 --- a/src/JsonLd/JsonStreamer/ValueTransformer/IriValueTransformer.php +++ b/src/JsonLd/JsonStreamer/ValueTransformer/IriValueTransformer.php @@ -35,7 +35,7 @@ public function transform(mixed $value, array $options = []): mixed } if ($options['_current_object'] instanceof Collection) { - return $this->iriConverter->getIriFromResource($options['operation']->getClass(), UrlGeneratorInterface::ABS_PATH, $options['operation']); + return $this->iriConverter->getIriFromResource($options['operation']->getApiClass(), UrlGeneratorInterface::ABS_PATH, $options['operation']); } return $this->iriConverter->getIriFromResource( diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index 54b01cdac20..adb6e19fb04 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -113,7 +113,7 @@ public function normalize(mixed $data, ?string $format = null, array $context = $isResourceClass = $this->resourceClassResolver->isResourceClass($resourceClass); if ($isResourceClass && (null === $previousResourceClass || $this->resourceClassResolver->isResourceClass($previousResourceClass))) { $resourceClass = $this->resourceClassResolver->getResourceClass($data, $previousResourceClass); - if (isset($context['operation']) && $context['operation'] instanceof HttpOperation && $context['operation']->getClass() !== $resourceClass) { + if (isset($context['operation']) && $context['operation'] instanceof HttpOperation && $context['operation']->getApiClass() !== $resourceClass) { $context['operation'] = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(null, false, true); } $context = $this->initContext($resourceClass, $context); @@ -174,7 +174,7 @@ public function normalize(mixed $data, ?string $format = null, array $context = $types = [$operation->getShortName()]; } else { // Use resource-level shortName to avoid operation-specific overrides - $typeClass = $isResourceClass ? $resourceClass : ($operation->getClass() ?? $resourceClass); + $typeClass = $isResourceClass ? $resourceClass : ($operation->getApiClass() ?? $resourceClass); try { $types = [$this->resourceMetadataCollectionFactory->create($typeClass)[0]->getShortName()]; } catch (\Exception) { diff --git a/src/JsonSchema/DefinitionNameFactory.php b/src/JsonSchema/DefinitionNameFactory.php index 2396f9424d5..08b302f234d 100644 --- a/src/JsonSchema/DefinitionNameFactory.php +++ b/src/JsonSchema/DefinitionNameFactory.php @@ -104,12 +104,12 @@ private function createPrefixFromOperation(Operation $operation): ?string } if (!isset($this->prefixCache[$name])) { - $this->prefixCache[$name] = $operation->getClass(); + $this->prefixCache[$name] = $operation->getApiClass(); return $name; } - if ($this->prefixCache[$name] === $operation->getClass()) { + if ($this->prefixCache[$name] === $operation->getApiClass()) { return $name; } diff --git a/src/JsonSchema/ResourceMetadataTrait.php b/src/JsonSchema/ResourceMetadataTrait.php index 71988820c76..d9376012d1c 100644 --- a/src/JsonSchema/ResourceMetadataTrait.php +++ b/src/JsonSchema/ResourceMetadataTrait.php @@ -29,11 +29,10 @@ trait ResourceMetadataTrait private function findOutputClass(string $className, string $type, Operation $operation, ?array $serializerContext): ?string { - $inputOrOutput = ['class' => $className]; - $inputOrOutput = Schema::TYPE_OUTPUT === $type ? ($operation->getOutput() ?? $inputOrOutput) : ($operation->getInput() ?? $inputOrOutput); + $resourceClass = (Schema::TYPE_OUTPUT === $type ? $operation->getOutputClass() : $operation->getInputClass()) ?? $className; $forceSubschema = $serializerContext[SchemaFactory::FORCE_SUBSCHEMA] ?? false; - return $forceSubschema ? ($inputOrOutput['class'] ?? $inputOrOutput->class ?? $operation->getClass()) : ($inputOrOutput['class'] ?? $inputOrOutput->class ?? null); + return $forceSubschema ? ($resourceClass ?? $operation->getApiClass()) : $resourceClass; } private function findOperation(string $className, string $type, ?Operation $operation, ?array $serializerContext, ?string $format = null): Operation @@ -58,7 +57,7 @@ private function findOperation(string $className, string $type, ?Operation $oper } // The best here is to use an Operation when calling `buildSchema`, we try to do a smart guess otherwise - if ($this->resourceMetadataFactory && !$operation->getClass()) { + if ($this->resourceMetadataFactory && !$operation->getApiClass()) { $resourceMetadataCollection = $this->resourceMetadataFactory->create($className); if ($operation->getName()) { diff --git a/src/Laravel/Controller/ApiPlatformController.php b/src/Laravel/Controller/ApiPlatformController.php index d7c59b6f8f7..4a997579dc4 100644 --- a/src/Laravel/Controller/ApiPlatformController.php +++ b/src/Laravel/Controller/ApiPlatformController.php @@ -62,7 +62,7 @@ public function __invoke(Request $request): Response $context = [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]; if (null === $operation->canValidate()) { diff --git a/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php b/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php index 706070d288c..653e939653d 100644 --- a/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php +++ b/src/Laravel/Eloquent/Metadata/Factory/Resource/EloquentResourceCollectionMetadataFactory.php @@ -15,7 +15,6 @@ use ApiPlatform\Laravel\Eloquent\State\CollectionProvider; use ApiPlatform\Laravel\Eloquent\State\ItemProvider; -use ApiPlatform\Laravel\Eloquent\State\Options; use ApiPlatform\Laravel\Eloquent\State\PersistProcessor; use ApiPlatform\Laravel\Eloquent\State\RemoveProcessor; use ApiPlatform\Metadata\CollectionOperationInterface; @@ -85,7 +84,7 @@ public function create(string $resourceClass): ResourceMetadataCollection $operations = $resourceMetadata->getOperations(); foreach ($operations ?? [] as $operationName => $operation) { // Check if this operation uses Eloquent via stateOptions - $modelClass = $this->getStateOptionsClass($operation, $resourceClass, Options::class); + $modelClass = $operation->getDataClass(); $usesEloquent = $isModel || ($modelClass !== $resourceClass); if (!$usesEloquent) { @@ -132,7 +131,7 @@ public function create(string $resourceClass): ResourceMetadataCollection $graphQlOperations = $resourceMetadata->getGraphQlOperations(); foreach ($graphQlOperations ?? [] as $operationName => $graphQlOperation) { // Check if this operation uses Eloquent via stateOptions - $modelClass = $this->getStateOptionsClass($graphQlOperation, $resourceClass, Options::class); + $modelClass = $graphQlOperation->getDataClass(); $usesEloquent = $isModel || ($modelClass !== $resourceClass); if (!$usesEloquent) { diff --git a/src/Laravel/Eloquent/State/CollectionProvider.php b/src/Laravel/Eloquent/State/CollectionProvider.php index b70bb37307d..eec090c8ebc 100644 --- a/src/Laravel/Eloquent/State/CollectionProvider.php +++ b/src/Laravel/Eloquent/State/CollectionProvider.php @@ -48,7 +48,7 @@ public function __construct( public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - $resourceClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $resourceClass = $operation->getDataClass(); $model = new $resourceClass(); if (!$model instanceof Model) { @@ -56,9 +56,9 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } if ($handleLinks = $this->getLinksHandler($operation)) { - $query = $handleLinks($model->query(), $uriVariables, ['operation' => $operation, 'modelClass' => $operation->getClass()] + $context); + $query = $handleLinks($model->query(), $uriVariables, ['operation' => $operation, 'modelClass' => $operation->getApiClass()] + $context); } else { - $query = $this->linksHandler->handleLinks($model->query(), $uriVariables, ['operation' => $operation, 'modelClass' => $operation->getClass()] + $context); + $query = $this->linksHandler->handleLinks($model->query(), $uriVariables, ['operation' => $operation, 'modelClass' => $operation->getApiClass()] + $context); } foreach ($this->queryExtensions as $extension) { diff --git a/src/Laravel/Eloquent/State/ItemProvider.php b/src/Laravel/Eloquent/State/ItemProvider.php index 38d2bb1bbb5..d7d7b6aab88 100644 --- a/src/Laravel/Eloquent/State/ItemProvider.php +++ b/src/Laravel/Eloquent/State/ItemProvider.php @@ -43,7 +43,7 @@ public function __construct( public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - $resourceClass = $this->getStateOptionsClass($operation, $operation->getClass(), Options::class); + $resourceClass = $operation->getDataClass(); $model = new $resourceClass(); if (!$model instanceof Model) { diff --git a/src/Laravel/Eloquent/State/Options.php b/src/Laravel/Eloquent/State/Options.php index 3e328adf3d2..096e141b17e 100644 --- a/src/Laravel/Eloquent/State/Options.php +++ b/src/Laravel/Eloquent/State/Options.php @@ -13,9 +13,9 @@ namespace ApiPlatform\Laravel\Eloquent\State; -use ApiPlatform\State\OptionsInterface; +use ApiPlatform\State\DataOptionsInterface; -class Options implements OptionsInterface +class Options implements DataOptionsInterface { /** * @param string|callable $handleLinks experimental callable, typed mixed as we may want a service name in the future @@ -28,6 +28,11 @@ public function __construct( ) { } + public function getDataClass(): ?string + { + return $this->modelClass; + } + public function getHandleLinks(): mixed { return $this->handleLinks; diff --git a/src/Laravel/Exception/ErrorRenderer.php b/src/Laravel/Exception/ErrorRenderer.php index ee187f55f53..5ebd2f62fe7 100644 --- a/src/Laravel/Exception/ErrorRenderer.php +++ b/src/Laravel/Exception/ErrorRenderer.php @@ -142,7 +142,7 @@ public function render(Request $request, \Throwable $exception): ?Response $dup = $request->duplicate(null, null, []); $dup->setMethod('GET'); - $dup->attributes->set('_api_resource_class', $operation->getClass()); + $dup->attributes->set('_api_resource_class', $operation->getApiClass()); $dup->attributes->set('_api_previous_operation', $apiOperation); $dup->attributes->set('_api_operation', $operation); $dup->attributes->set('_api_operation_name', $operation->getName()); diff --git a/src/Laravel/Routing/IriConverter.php b/src/Laravel/Routing/IriConverter.php index 82afad6be8a..24460b73c54 100644 --- a/src/Laravel/Routing/IriConverter.php +++ b/src/Laravel/Routing/IriConverter.php @@ -129,7 +129,7 @@ public function getIriFromResource(object|string $resource, int $referenceType = if ($operation instanceof HttpOperation && 301 === $operation->getStatus()) { /** @var class-string $operationClass */ - $operationClass = $operation->getClass() ?? $resourceClass; + $operationClass = $operation->getApiClass() ?? $resourceClass; $operation = ($operation instanceof CollectionOperationInterface ? new GetCollection() : new Get())->withClass($operationClass); unset($context['uri_variables']); } @@ -171,7 +171,7 @@ private function generateRoute(object|string $resource, int $referenceType = Url } catch (RuntimeException $e) { // We can try using context uri variables if any if (!$identifiers) { - throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getApiClass()), $e->getCode(), $e); } } } @@ -181,7 +181,7 @@ private function generateRoute(object|string $resource, int $referenceType = Url return $this->router->generate($routeName, $identifiers, $operation->getUrlGenerationStrategy() ?? $referenceType); } catch (RoutingExceptionInterface $e) { - throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getApiClass()), $e->getCode(), $e); } } diff --git a/src/Laravel/Serializer/EloquentOperationResourceClassResolver.php b/src/Laravel/Serializer/EloquentOperationResourceClassResolver.php index f4f690a77ca..90fbbf56940 100644 --- a/src/Laravel/Serializer/EloquentOperationResourceClassResolver.php +++ b/src/Laravel/Serializer/EloquentOperationResourceClassResolver.php @@ -44,7 +44,7 @@ public function resolve(object|string $resource, Operation $operation): string // Validate object matches the backing model class if ($modelClass && is_a($objectClass, $modelClass, true)) { - return $operation->getClass(); + return $operation->getApiClass(); } } diff --git a/src/Laravel/State/AccessCheckerProvider.php b/src/Laravel/State/AccessCheckerProvider.php index 21b432bf5dc..97366cf6754 100644 --- a/src/Laravel/State/AccessCheckerProvider.php +++ b/src/Laravel/State/AccessCheckerProvider.php @@ -55,7 +55,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c 'operation' => $operation, ]; - if (!$this->resourceAccessChecker->isGranted($operation->getClass(), $policy, $resourceAccessCheckerContext)) { + if (!$this->resourceAccessChecker->isGranted($operation->getApiClass(), $policy, $resourceAccessCheckerContext)) { throw $operation instanceof HttpOperation ? new AuthorizationException($message ?? 'Access Denied.') : new AccessDeniedHttpException($message ?? 'Access Denied.'); } diff --git a/src/Laravel/State/SwaggerUiProvider.php b/src/Laravel/State/SwaggerUiProvider.php index ed7dbc91ef6..3eeaeb16ae5 100644 --- a/src/Laravel/State/SwaggerUiProvider.php +++ b/src/Laravel/State/SwaggerUiProvider.php @@ -46,7 +46,7 @@ public function __construct( public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { // We went through the DocumentationAction - if (OpenApi::class === $operation->getClass()) { + if (OpenApi::class === $operation->getApiClass()) { return $this->decorated->provide($operation, $uriVariables, $context); } @@ -68,7 +68,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c // when it fails we'll get an Error, and we'll fix the status accordingly // @see features/main/content_negotiation.feature:119 // When requesting DocumentationAction or EntrypointAction with Accept: text/html we render SwaggerUi - if (!$operation instanceof Error && Documentation::class !== $operation->getClass()) { + if (!$operation instanceof Error && Documentation::class !== $operation->getApiClass()) { $this->decorated->provide($operation, $uriVariables, $context); } diff --git a/src/Laravel/routes/api.php b/src/Laravel/routes/api.php index 72d337efcf5..031ddcfb8d3 100644 --- a/src/Laravel/routes/api.php +++ b/src/Laravel/routes/api.php @@ -65,7 +65,7 @@ '_format' => '^\.[a-zA-Z]+', ] + ($operation->getRequirements() ?? [])) ->name($operation->getName()) - ->setDefaults(['_api_operation_name' => $operation->getName(), '_api_resource_class' => $operation->getClass()]); + ->setDefaults(['_api_operation_name' => $operation->getName(), '_api_resource_class' => $operation->getApiClass()]); $route->middleware(ApiPlatformMiddleware::class.':'.$operation->getName()); diff --git a/src/Mcp/Capability/Registry/Loader.php b/src/Mcp/Capability/Registry/Loader.php index c88fd1608a3..091ef664dc5 100644 --- a/src/Mcp/Capability/Registry/Loader.php +++ b/src/Mcp/Capability/Registry/Loader.php @@ -49,13 +49,13 @@ public function load(RegistryInterface $registry): void foreach ($metadata as $resource) { foreach ($resource->getMcp() ?? [] as $mcp) { if ($mcp instanceof McpTool) { - $inputClass = $mcp->getInput()['class'] ?? $mcp->getClass(); + $inputClass = $mcp->getInputClass(); $inputFormat = array_key_first($mcp->getInputFormats() ?? ['json' => ['application/json']]); $inputSchema = $this->schemaFactory->buildSchema($inputClass, $inputFormat, Schema::TYPE_INPUT, $mcp, null, [SchemaFactory::FORCE_SUBSCHEMA => true]); $outputSchema = null; if (false !== $mcp->getStructuredContent()) { - $outputClass = $mcp->getOutput()['class'] ?? $mcp->getClass(); + $outputClass = $mcp->getOutputClass(); $outputFormat = array_key_first($mcp->getOutputFormats() ?? ['json' => ['application/json']]); $outputSchema = $this->schemaFactory->buildSchema($outputClass, $outputFormat, Schema::TYPE_OUTPUT, $mcp, null, [SchemaFactory::FORCE_SUBSCHEMA => true])->getArrayCopy(); } diff --git a/src/Mcp/Server/Handler.php b/src/Mcp/Server/Handler.php index 60de8cfa6d5..f5138718f14 100644 --- a/src/Mcp/Server/Handler.php +++ b/src/Mcp/Server/Handler.php @@ -99,7 +99,7 @@ public function handle(Request $request, SessionInterface $session): Response|Er 'mcp_request' => $request, 'mcp_session' => $session, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]; if (!$isResource) { diff --git a/src/Mcp/State/StructuredContentProcessor.php b/src/Mcp/State/StructuredContentProcessor.php index 1a92b43b51a..0cf8a73a6ac 100644 --- a/src/Mcp/State/StructuredContentProcessor.php +++ b/src/Mcp/State/StructuredContentProcessor.php @@ -52,7 +52,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $request = $context['request'] ?? null; $context['original_data'] = $result; - $class = $operation->getClass(); + $class = $operation->getApiClass(); $includeStructuredContent = $operation instanceof McpTool || $operation instanceof McpResource ? $operation->getStructuredContent() ?? true : false; $structuredContent = null; diff --git a/src/Mcp/State/ToolProvider.php b/src/Mcp/State/ToolProvider.php index dff2e8874eb..59411e746e6 100644 --- a/src/Mcp/State/ToolProvider.php +++ b/src/Mcp/State/ToolProvider.php @@ -35,7 +35,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } $data = (object) $context['mcp_data']; - $class = $operation->getInput()['class'] ?? $operation->getClass(); + $class = $operation->getInputClass(); return $this->objectMapper->map($data, $class); } diff --git a/src/Metadata/ApiResource.php b/src/Metadata/ApiResource.php index eabfdda8fc0..591e9f0e7a7 100644 --- a/src/Metadata/ApiResource.php +++ b/src/Metadata/ApiResource.php @@ -430,8 +430,16 @@ public function __construct( * @var string|bool|null */ protected $messenger = null, + /** + * @deprecated use inputClass instead + */ protected $input = null, + protected ?string $inputClass = null, + /** + * @deprecated use outputClass instead + */ protected $output = null, + protected ?string $outputClass = null, /** * Override the default order of items in your collection. Note that this is handled by our doctrine filters such as * the [OrderFilter](/docs/reference/Doctrine/Orm/Filter/OrderFilter). @@ -973,6 +981,7 @@ public function __construct( protected array $extraProperties = [], ?bool $map = null, protected ?array $mcp = null, + protected ?string $dataClass = null, ) { parent::__construct( shortName: $shortName, @@ -988,7 +997,9 @@ class: $class, mercure: $mercure, messenger: $messenger, input: $input, + inputClass: $inputClass, output: $output, + outputClass: $outputClass, order: $order, fetchPartial: $fetchPartial, forceEager: $forceEager, @@ -1020,6 +1031,7 @@ class: $class, jsonStream: $jsonStream, extraProperties: $extraProperties, map: $map, + dataClass: $dataClass, ); /* @var Operations $operations> */ diff --git a/src/Metadata/Get.php b/src/Metadata/Get.php index 4babd54eb27..17890235abf 100644 --- a/src/Metadata/Get.php +++ b/src/Metadata/Get.php @@ -103,6 +103,7 @@ public function __construct( ?bool $jsonStream = null, array $extraProperties = [], ?bool $map = null, + ?string $dataClass = null, ) { parent::__construct( uriTemplate: $uriTemplate, @@ -185,7 +186,8 @@ class: $class, hideHydraOperation: $hideHydraOperation, jsonStream: $jsonStream, extraProperties: $extraProperties, - map: $map + map: $map, + dataClass: $dataClass ); } } diff --git a/src/Metadata/HttpOperation.php b/src/Metadata/HttpOperation.php index 58d4cf98c7f..8dec070dfb2 100644 --- a/src/Metadata/HttpOperation.php +++ b/src/Metadata/HttpOperation.php @@ -223,6 +223,7 @@ public function __construct( ?bool $jsonStream = null, array $extraProperties = [], ?bool $map = null, + ?string $dataClass = null, ) { $this->formats = (null === $formats || \is_array($formats)) ? $formats : [$formats]; $this->inputFormats = (null === $inputFormats || \is_array($inputFormats)) ? $inputFormats : [$inputFormats]; @@ -283,7 +284,8 @@ class: $class, hideHydraOperation: $hideHydraOperation, jsonStream: $jsonStream, extraProperties: $extraProperties, - map: $map + map: $map, + dataClass: $dataClass ); } diff --git a/src/Metadata/IdentifiersExtractor.php b/src/Metadata/IdentifiersExtractor.php index 7c0c8c5c98c..afd50303db8 100644 --- a/src/Metadata/IdentifiersExtractor.php +++ b/src/Metadata/IdentifiersExtractor.php @@ -58,7 +58,7 @@ public function getIdentifiersFromItem(object $item, ?Operation $operation = nul } } - if ($operation && $operation->getClass()) { + if ($operation && $operation->getApiClass()) { return $this->getIdentifiersFromOperation($item, $operation, $context); } @@ -84,7 +84,7 @@ private function getIdentifiersFromOperation(object $item, Operation $operation, if (1 < (is_countable($link->getIdentifiers()) ? \count($link->getIdentifiers()) : 0)) { $compositeIdentifiers = []; foreach ($link->getIdentifiers() as $identifier) { - $compositeIdentifiers[$identifier] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $identifier, $link->getParameterName(), null, $context, $operation); + $compositeIdentifiers[$identifier] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getApiClass(), $identifier, $link->getParameterName(), null, $context, $operation); } $identifiers[$link->getParameterName()] = CompositeIdentifierParser::stringify($compositeIdentifiers); @@ -92,7 +92,7 @@ private function getIdentifiersFromOperation(object $item, Operation $operation, } $parameterName = $link->getParameterName(); - $identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getClass(), $link->getIdentifiers()[0] ?? $k, $parameterName, $link->getToProperty(), $context, $operation); + $identifiers[$parameterName] = $this->getIdentifierValue($item, $link->getFromClass() ?? $operation->getApiClass(), $link->getIdentifiers()[0] ?? $k, $parameterName, $link->getToProperty(), $context, $operation); } return $identifiers; @@ -114,7 +114,7 @@ private function getIdentifierValue(object $item, string $class, string $propert } // ItemUriTemplate is defined on a collection and we read the identifier alghough the PHP class may be different - if (isset($context['item_uri_template']) && $operation->getClass() === $class) { + if (isset($context['item_uri_template']) && $operation->getApiClass() === $class) { try { return $this->resolveIdentifierValue($this->propertyAccessor->getValue($item, $property), $parameterName); } catch (NoSuchPropertyException $e) { diff --git a/src/Metadata/Metadata.php b/src/Metadata/Metadata.php index 009612c9900..b0310dc5000 100644 --- a/src/Metadata/Metadata.php +++ b/src/Metadata/Metadata.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Metadata; +use ApiPlatform\State\DataOptionsInterface; use ApiPlatform\State\OptionsInterface; /** @@ -29,8 +30,10 @@ abstract class Metadata * @param string|\Stringable|null $securityPostDenormalize https://api-platform.com/docs/core/security/#executing-access-control-rules-after-denormalization * @param mixed|null $mercure * @param mixed|null $messenger - * @param mixed|null $input - * @param mixed|null $output + * @param mixed|null $input @deprecated use inputClass instead + * @param mixed|null $inputClass + * @param mixed|null $output @deprecated use outputClass instead + * @param mixed|null $outputClass * @param mixed|null $provider * @param mixed|null $processor * @param Parameters|array $parameters @@ -50,7 +53,9 @@ public function __construct( protected $mercure = null, protected $messenger = null, protected $input = null, + protected ?string $inputClass = null, protected $output = null, + protected ?string $outputClass = null, protected ?array $order = null, protected ?bool $fetchPartial = null, protected ?bool $forceEager = null, @@ -83,12 +88,17 @@ public function __construct( protected ?bool $jsonStream = null, protected ?bool $map = null, protected array $extraProperties = [], + protected ?string $dataClass = null, ) { if (\is_array($parameters) && $parameters) { $parameters = new Parameters($parameters); } $this->parameters = $parameters; + + if (null === $this->dataClass && $this->stateOptions instanceof DataOptionsInterface) { + $this->dataClass = $this->stateOptions->getDataClass(); + } } public function canMap(): ?bool @@ -125,6 +135,14 @@ public function getClass(): ?string return $this->class; } + /** + * @return class-string|null + */ + public function getApiClass(): ?string + { + return $this->class; + } + /** * @param class-string $class */ @@ -272,26 +290,109 @@ public function withMessenger(mixed $messenger): static return $self; } + /** + * @deprecated use getInputClass() instead + */ public function getInput(): mixed { + trigger_deprecation('api-platform/metadata', '4.3', 'The method "getInput()" is deprecated, use "getInputClass()" instead.'); + return $this->input; } + /** + * @deprecated use withInputClass() instead + */ public function withInput(mixed $input): static { + trigger_deprecation('api-platform/metadata', '4.3', 'The method "withInput()" is deprecated, use "withInputClass()" instead.'); + $self = clone $this; $self->input = $input; return $self; } + public function getInputClass(): ?string + { + if (null !== $this->inputClass) { + return $this->inputClass; + } + + if (false === $this->input) { + return null; + } + + if (\is_array($this->input) && \is_string($this->input['class'] ?? null)) { + return $this->input['class']; + } + + if (null === $this->input) { + return $this->getClass(); + } + + return null; + } + + /** + * @param class-string|null $inputClass + * + * @return $this + */ + public function withInputClass(?string $inputClass): static + { + $self = clone $this; + $self->inputClass = $inputClass; + + return $self; + } + + public function getOutputClass(): ?string + { + if (null !== $this->outputClass) { + return $this->outputClass; + } + + if (false === $this->output) { + return null; + } + + if (\is_array($this->output) && \is_string($this->output['class'] ?? null)) { + return $this->output['class']; + } + + if (null === $this->output) { + return $this->getClass(); + } + + return null; + } + + public function withOutputClass(?string $outputClass): static + { + $self = clone $this; + $self->outputClass = $outputClass; + + return $self; + } + + /** + * @deprecated use getOutputClass() instead + */ public function getOutput(): mixed { + trigger_deprecation('api-platform/metadata', '4.3', 'The method "getOutput()" is deprecated, use "getOutputClass()" instead.'); + return $this->output; } + /** + * @deprecated use withOutputClass() instead + */ public function withOutput(mixed $output): static { + trigger_deprecation('api-platform/metadata', '4.3', 'The method "withOutput()" is deprecated, use "withOutputClass()" instead.'); + $self = clone $this; $self->output = $output; @@ -581,6 +682,33 @@ public function withStateOptions(?OptionsInterface $stateOptions): static $self = clone $this; $self->stateOptions = $stateOptions; + if ($stateOptions instanceof DataOptionsInterface) { + $self->dataClass = $stateOptions->getDataClass(); + } + + return $self; + } + + /** + * @return class-string|null + */ + public function getDataClass(): ?string + { + if (null === $this->dataClass) { + return $this->class; + } + + return $this->dataClass; + } + + /** + * @param class-string|null $dataClass + */ + public function withDataClass(?string $dataClass): static + { + $self = clone $this; + $self->dataClass = $dataClass; + return $self; } diff --git a/src/Metadata/Operation.php b/src/Metadata/Operation.php index cbd53751e59..acefc7957f1 100644 --- a/src/Metadata/Operation.php +++ b/src/Metadata/Operation.php @@ -729,8 +729,16 @@ public function __construct( * For more examples, read our guide on [validation](/guides/validation). */ protected ?array $validationContext = null, + /** + * @deprecated use inputClass instead + */ protected $input = null, + protected ?string $inputClass = null, + /** + * @deprecated use outputClass instead + */ protected $output = null, + protected ?string $outputClass = null, protected $mercure = null, /** * The `messenger` option dispatches the current resource through the Message Bus. @@ -816,6 +824,7 @@ public function __construct( protected ?bool $jsonStream = null, protected array $extraProperties = [], ?bool $map = null, + protected ?string $dataClass = null, ) { parent::__construct( shortName: $shortName, @@ -831,7 +840,9 @@ class: $class, mercure: $mercure, messenger: $messenger, input: $input, + inputClass: $inputClass, output: $output, + outputClass: $outputClass, order: $order, fetchPartial: $fetchPartial, forceEager: $forceEager, @@ -863,7 +874,8 @@ class: $class, hideHydraOperation: $hideHydraOperation, jsonStream: $jsonStream, extraProperties: $extraProperties, - map: $map + map: $map, + dataClass: $dataClass ); } diff --git a/src/Metadata/Resource/Factory/ExtractorResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/ExtractorResourceMetadataCollectionFactory.php index fd7abf9d705..7431952d6d9 100644 --- a/src/Metadata/Resource/Factory/ExtractorResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/ExtractorResourceMetadataCollectionFactory.php @@ -136,7 +136,7 @@ private function addOperations(?array $data, ApiResource $resource): ApiResource [$key, $operation] = $this->getOperationWithDefaults($resource, $operation); if (\array_key_exists($key, $operations)) { - throw new RuntimeException(\sprintf('Operation name "%s" is declared twice on resource "%s". Operation names must be unique because they are also used as Symfony route names. Remove the duplicate "name" so the framework can disambiguate by method, or set distinct names.', $key, $resource->getClass() ?? '')); + throw new RuntimeException(\sprintf('Operation name "%s" is declared twice on resource "%s". Operation names must be unique because they are also used as Symfony route names. Remove the duplicate "name" so the framework can disambiguate by method, or set distinct names.', $key, $resource->getApiClass() ?? '')); } $operations[$key] = $operation; } diff --git a/src/Metadata/Resource/Factory/InputOutputResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/InputOutputResourceMetadataCollectionFactory.php index 00d2c6ea252..a08867f811c 100644 --- a/src/Metadata/Resource/Factory/InputOutputResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/InputOutputResourceMetadataCollectionFactory.php @@ -37,8 +37,8 @@ public function create(string $resourceClass): ResourceMetadataCollection $resourceMetadataCollection = $this->decorated->create($resourceClass); foreach ($resourceMetadataCollection as $key => $resourceMetadata) { - $resourceMetadata = $resourceMetadata->withInput($this->transformInputOutput($resourceMetadata->getInput())); - $resourceMetadata = $resourceMetadata->withOutput($this->transformInputOutput($resourceMetadata->getOutput())); + $resourceMetadata = $resourceMetadata->withInputClass($this->transformInputOutput($resourceMetadata->getInput())['class'] ?? null); + $resourceMetadata = $resourceMetadata->withOutputClass($this->transformInputOutput($resourceMetadata->getOutput())['class'] ?? null); if ($resourceMetadata->getOperations()) { $resourceMetadata = $resourceMetadata->withOperations($this->getTransformedOperations($resourceMetadata->getOperations(), $resourceMetadata)); @@ -61,23 +61,20 @@ public function create(string $resourceClass): ResourceMetadataCollection private function getTransformedOperations(Operations|array $operations, ApiResource $resourceMetadata): Operations|array { foreach ($operations as $key => $operation) { - $operation = $operation->withInput(null !== $operation->getInput() ? $this->transformInputOutput($operation->getInput()) : $resourceMetadata->getInput()); - $operation = $operation->withOutput(null !== $operation->getOutput() ? $this->transformInputOutput($operation->getOutput()) : $resourceMetadata->getOutput()); + $resolvedInputClass = null !== $operation->getInput() ? ($this->transformInputOutput($operation->getInput())['class'] ?? null) : $resourceMetadata->getInputClass(); + $operation = $operation->withInputClass($resolvedInputClass); - if ( - $operation->getInput() - && \array_key_exists('class', $operation->getInput()) - && null === $operation->getInput()['class'] - ) { + $resolvedOutputClass = null !== $operation->getOutput() ? ($this->transformInputOutput($operation->getOutput())['class'] ?? null) : $resourceMetadata->getOutputClass(); + $operation = $operation->withOutputClass($resolvedOutputClass); + + if (null === $resolvedInputClass) { $operation = $operation->withDeserialize(null === $operation->canDeserialize() ? false : $operation->canDeserialize()); $operation = $operation->withValidate(null === $operation->canValidate() ? false : $operation->canValidate()); } if ( $operation instanceof HttpOperation - && $operation->getOutput() - && \array_key_exists('class', $operation->getOutput()) - && null === $operation->getOutput()['class'] + && null === $resolvedOutputClass && null === $operation->getStatus() ) { $operation = $operation->withStatus(204); diff --git a/src/Metadata/Resource/Factory/LinkFactory.php b/src/Metadata/Resource/Factory/LinkFactory.php index fb87e584b0a..e6c8b740665 100644 --- a/src/Metadata/Resource/Factory/LinkFactory.php +++ b/src/Metadata/Resource/Factory/LinkFactory.php @@ -41,7 +41,7 @@ public function __construct(private readonly PropertyNameCollectionFactoryInterf */ public function createLinkFromProperty(Metadata $operation, string $property): Link { - $metadata = $this->propertyMetadataFactory->create($resourceClass = $operation->getClass(), $property); + $metadata = $this->propertyMetadataFactory->create($resourceClass = $operation->getDataClass(), $property); $relationClass = $this->getPropertyClassType($metadata->getNativeType()); if (!$relationClass) { throw new RuntimeException(\sprintf('We could not find a class matching the uriVariable "%s" on "%s".', $property, $resourceClass)); @@ -57,7 +57,7 @@ public function createLinkFromProperty(Metadata $operation, string $property): L */ public function createLinksFromIdentifiers(Metadata $operation): array { - $identifiers = $this->getIdentifiersFromResourceClass($resourceClass = $operation->getClass()); + $identifiers = $this->getIdentifiersFromResourceClass($resourceClass = $operation->getApiClass()); if (!$identifiers) { return []; @@ -83,7 +83,7 @@ public function createLinksFromIdentifiers(Metadata $operation): array public function createLinksFromRelations(Metadata $operation): array { $links = []; - foreach ($this->propertyNameCollectionFactory->create($resourceClass = $operation->getClass()) as $property) { + foreach ($this->propertyNameCollectionFactory->create($resourceClass = $operation->getApiClass()) as $property) { $metadata = $this->propertyMetadataFactory->create($resourceClass, $property); if (!($relationClass = $this->getPropertyClassType($metadata->getNativeType())) || !$this->resourceClassResolver->isResourceClass($relationClass)) { @@ -105,7 +105,7 @@ public function createLinksFromAttributes(Metadata $operation): array { $links = []; try { - $reflectionClass = new \ReflectionClass($resourceClass = $operation->getClass()); + $reflectionClass = new \ReflectionClass($resourceClass = $operation->getApiClass()); foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) { $reflectionProperty = $reflectionClass->getProperty($property); diff --git a/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php b/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php index cd74e7776df..edde2634c6d 100644 --- a/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php +++ b/src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php @@ -251,7 +251,7 @@ private function deduplicateShortNames(array $resources): array if (!$enabled) { if (1 === $shortNameCounts[$shortName]) { - trigger_deprecation('api-platform/core', '4.2', 'Having multiple "#[ApiResource]" attributes with the same "shortName" "%s" on class "%s" is deprecated and will result in automatic short name deduplication in API Platform 5.x. Set "defaults.extra_properties.deduplicate_resource_short_names" to "true" in the API Platform configuration to enable it now.', $shortName, $resource->getClass()); + trigger_deprecation('api-platform/core', '4.2', 'Having multiple "#[ApiResource]" attributes with the same "shortName" "%s" on class "%s" is deprecated and will result in automatic short name deduplication in API Platform 5.x. Set "defaults.extra_properties.deduplicate_resource_short_names" to "true" in the API Platform configuration to enable it now.', $shortName, $resource->getApiClass()); } ++$shortNameCounts[$shortName]; continue; diff --git a/src/Metadata/Resource/Factory/ObjectMapperMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/ObjectMapperMetadataCollectionFactory.php index 37204def6a4..8425cff31c1 100644 --- a/src/Metadata/Resource/Factory/ObjectMapperMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/ObjectMapperMetadataCollectionFactory.php @@ -57,16 +57,23 @@ public function create(string $resourceClass): ResourceMetadataCollection $entityClass = $options->getModelClass(); } - $class = $operation->getInput()['class'] ?? $operation->getClass(); - $outputClass = $operation->getOutput()['class'] ?? null; + $class = $operation->getInputClass(); + $outputClass = $operation->getOutputClass(); $entityMap = null; + // Only guard output: skip when outputClass equals the resource class (fallback from + // getOutputClass() with no explicit output), to avoid triggering mapping for resources + // that declare #[Map] for entity→resource but have no separate output DTO. + $effectiveOutputClass = ($outputClass !== null && $outputClass !== $operation->getApiClass()) ? $outputClass : null; + + // TODO review that + // Look for Mapping metadata - if ($this->canBeMapped($class) || ($outputClass && $this->canBeMapped($outputClass)) || ($entityClass && ($entityMap = $this->canBeMapped($entityClass)))) { + if ($this->canBeMapped($class) || ($effectiveOutputClass && $this->canBeMapped($effectiveOutputClass)) || ($entityClass && ($entityMap = $this->canBeMapped($entityClass)))) { $found = true; if ($entityMap) { foreach ($entityMap as $mapping) { - if ($found = ($mapping->source === $operation->getClass() || $mapping->target === $operation->getClass())) { + if ($found = ($mapping->source === $operation->getApiClass() || $mapping->target === $operation->getApiClass())) { break; } } @@ -89,8 +96,12 @@ public function create(string $resourceClass): ResourceMetadataCollection /** * @return bool|list */ - private function canBeMapped(string $class): bool|array + private function canBeMapped(?string $class): bool|array { + if (!$class) { + return false; + } + try { $r = new \ReflectionClass($class); if (!$r->isInstantiable() || !($mapping = $this->objectMapperMetadata->create($r->newInstanceWithoutConstructor(), null, ['_api_check_can_be_mapped' => true]))) { diff --git a/src/Metadata/Resource/Factory/OperationDefaultsTrait.php b/src/Metadata/Resource/Factory/OperationDefaultsTrait.php index 3c53fc69840..1e6a7a4c1e2 100644 --- a/src/Metadata/Resource/Factory/OperationDefaultsTrait.php +++ b/src/Metadata/Resource/Factory/OperationDefaultsTrait.php @@ -48,7 +48,7 @@ trait OperationDefaultsTrait private function addGlobalDefaults(ApiResource|Operation $operation): ApiResource|Operation { // Do not add global defaults for internal resources: - if (\in_array($operation->getClass(), [Error::class, ValidationException::class], true)) { + if (\in_array($operation->getApiClass(), [Error::class, ValidationException::class], true)) { return $operation; } @@ -94,7 +94,7 @@ private function getResourceWithDefaults(string $resourceClass, string $shortNam { $resource = $resource ->withShortName($resource->getShortName() ?? $shortName) - ->withClass($resource->getClass() ?? $resourceClass); + ->withClass($resource->getApiClass() ?? $resourceClass); return $this->addGlobalDefaults($resource); } @@ -104,7 +104,7 @@ private function getResourceWithDefaults(string $resourceClass, string $shortNam */ private function getDefaultHttpOperations(ApiResource $resource): iterable { - if (enum_exists($resource->getClass())) { + if (enum_exists($resource->getApiClass())) { return new Operations([new GetCollection(paginationEnabled: false), new Get()]); } @@ -134,7 +134,7 @@ private function getDefaultHttpOperations(ApiResource $resource): iterable private function addDefaultGraphQlOperations(ApiResource $resource): ApiResource { - $operations = enum_exists($resource->getClass()) ? [new Query(), new QueryCollection(paginationEnabled: false)] : [new Query(), new QueryCollection(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')]; + $operations = enum_exists($resource->getApiClass()) ? [new Query(), new QueryCollection(paginationEnabled: false)] : [new Query(), new QueryCollection(), (new Mutation())->withName('update'), (new DeleteMutation())->withName('delete'), (new Mutation())->withName('create')]; $graphQlOperations = []; foreach ($operations as $operation) { [$key, $operation] = $this->getOperationWithDefaults($resource, $operation); @@ -218,7 +218,7 @@ private function getOperationWithDefaults(ApiResource $resource, Operation $oper $operation = $operation->withName($operation->getRouteName()); } - $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, $resource->getClass()); + $operationName = $operation->getName() ?? $this->getDefaultOperationName($operation, $resource->getApiClass()); return [ $operationName, diff --git a/src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php index f55734196e5..3e518a8c3e2 100644 --- a/src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php @@ -104,7 +104,7 @@ private function getProperties(string $resourceClass, ?Parameter $parameter = nu { $filterClass = $parameter?->getFilterClass(); if (null === $filterClass && null !== $operation) { - $filterClass = $this->getStateOptionsClass($operation, $resourceClass); + $filterClass = $operation->getDataClass(); } $filterClass ??= $resourceClass; @@ -430,7 +430,7 @@ private function setDefaults(string $key, Parameter $parameter, ?object $filter, private function getLegacyFilterMetadata(Parameter $parameter, Operation $operation, FilterInterface $filter): Parameter { - $description = $filter->getDescription($this->getStateOptionsClass($operation, $operation->getClass())); + $description = $filter->getDescription($operation->getDataClass()); $key = $parameter->getKey(); if (($schema = $description[$key]['schema'] ?? null) && null === $parameter->getSchema()) { $parameter = $parameter->withSchema($schema); diff --git a/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php index 1a48574063e..227099ec366 100644 --- a/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/PhpFileResourceMetadataCollectionFactory.php @@ -47,7 +47,7 @@ public function create(string $resourceClass): ResourceMetadataCollection } foreach ($this->metadataExtractor->getResources() as $resource) { - if ($resourceClass !== $resource->getClass()) { + if ($resourceClass !== $resource->getApiClass()) { continue; } diff --git a/src/Metadata/Resource/Factory/PhpFileResourceNameCollectionFactory.php b/src/Metadata/Resource/Factory/PhpFileResourceNameCollectionFactory.php index be341d50647..77573908483 100644 --- a/src/Metadata/Resource/Factory/PhpFileResourceNameCollectionFactory.php +++ b/src/Metadata/Resource/Factory/PhpFileResourceNameCollectionFactory.php @@ -41,7 +41,7 @@ public function create(): ResourceNameCollection } foreach ($this->metadataExtractor->getResources() as $resource) { - $resourceClass = $resource->getClass(); + $resourceClass = $resource->getApiClass(); if (null === $resourceClass) { continue; diff --git a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php index feace7c512c..2bf014d8ced 100644 --- a/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/UriTemplateResourceMetadataCollectionFactory.php @@ -148,7 +148,7 @@ private function configureUriVariables(ApiResource|HttpOperation $operation): Ap } foreach ($uriVariables = $operation->getUriVariables() as $parameterName => $l) { - $link = null === $l->getFromClass() ? $l->withFromClass($operation->getClass()) : $l; + $link = null === $l->getFromClass() ? $l->withFromClass($operation->getApiClass()) : $l; $uriVariables[$parameterName] = $this->linkFactory->completeLink($link); } $operation = $operation->withUriVariables($uriVariables); @@ -175,8 +175,8 @@ private function configureUriVariables(ApiResource|HttpOperation $operation): Ap } $newUriVariables[$variable] = (new Link()) - ->withFromClass($operation->getClass()) - ->withIdentifiers([property_exists($operation->getClass(), $variable) ? $variable : 'id']) + ->withFromClass($operation->getApiClass()) + ->withIdentifiers([property_exists($operation->getApiClass(), $variable) ? $variable : 'id']) ->withParameterName($variable); } @@ -211,7 +211,7 @@ private function normalizeUriVariables(ApiResource|HttpOperation $operation): Ap $uriVariables = (array) ($operation->getUriVariables() ?? []); $normalizedUriVariables = []; - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getApiClass(); foreach ($uriVariables as $parameterName => $uriVariable) { $normalizedParameterName = $parameterName; diff --git a/src/Metadata/Tests/IdentifiersExtractorTest.php b/src/Metadata/Tests/IdentifiersExtractorTest.php index 9693e3499b1..7e3bd7243e0 100644 --- a/src/Metadata/Tests/IdentifiersExtractorTest.php +++ b/src/Metadata/Tests/IdentifiersExtractorTest.php @@ -54,7 +54,7 @@ public function testGetIdentifiersFromItem(): void $operation = $this->prophesize(HttpOperation::class); $item = new Dummy(); $resourceClass = Dummy::class; - $operation->getClass()->willReturn(null); + $operation->getApiClass()->willReturn(null); $resourceClassResolverProphecy->isResourceClass(Argument::any())->willReturn(true); $resourceClassResolverProphecy->getResourceClass($item)->willReturn($resourceClass); @@ -82,7 +82,7 @@ public function testGetIdentifiersFromItemWithOperation(): void $operation = $this->prophesize(HttpOperation::class); $item = new Dummy(); $resourceClass = Dummy::class; - $operation->getClass()->willReturn($resourceClass); + $operation->getApiClass()->willReturn($resourceClass); $operation->getUriVariables()->willReturn([]); $resourceClassResolverProphecy->isResourceClass(Argument::any())->willReturn(true); diff --git a/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php b/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php index 07af77d035c..8f34e9c747d 100644 --- a/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php +++ b/src/Metadata/Tests/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php @@ -330,13 +330,13 @@ public function testCreatePropagatesExplicitResourceClass(): void $this->assertCount(1, $collection); $resource = $collection[0]; - $this->assertSame(ResourceClassPropagationTarget::class, $resource->getClass()); + $this->assertSame(ResourceClassPropagationTarget::class, $resource->getApiClass()); $operations = $resource->getOperations(); $this->assertNotNull($operations); $this->assertGreaterThan(0, \count($operations)); foreach ($operations as $operation) { - $this->assertSame(ResourceClassPropagationTarget::class, $operation->getClass()); + $this->assertSame(ResourceClassPropagationTarget::class, $operation->getApiClass()); } } diff --git a/src/OpenApi/Factory/OpenApiFactory.php b/src/OpenApi/Factory/OpenApiFactory.php index 6273ea8ba51..895d8a07885 100644 --- a/src/OpenApi/Factory/OpenApiFactory.php +++ b/src/OpenApi/Factory/OpenApiFactory.php @@ -206,7 +206,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection continue; } - $resourceClass = $operation->getClass() ?? $resource->getClass(); + $resourceClass = $operation->getApiClass() ?? $resource->getApiClass(); $routeName = $operation->getRouteName() ?? $operation->getName(); if (!$this->routeCollection && $this->router) { @@ -333,7 +333,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection } } - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass()); + $entityClass = $operation->getDataClass(); $openapiParameters = $openapiOperation->getParameters(); foreach ($operation->getParameters() ?? [] as $key => $p) { if (false === $p->getOpenApi()) { @@ -473,7 +473,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection if ( \in_array($method, ['PATCH', 'PUT', 'POST'], true) - && !(false === ($input = $operation->getInput()) || (\is_array($input) && null === $input['class'])) + && $operation->getInputClass() ) { $content = $openapiOperation->getRequestBody()?->getContent(); if (null === $content) { @@ -521,7 +521,7 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection */ private function buildOpenApiResponse(array $existingResponses, int|string $status, string $description, Operation $openapiOperation, ?HttpOperation $operation = null, ?array $responseMimeTypes = null, ?array $operationOutputSchemas = null, ?ResourceMetadataCollection $resourceMetadataCollection = null, bool $isErrorResponse = false): Operation { - $noOutput = !$isErrorResponse && \is_array($operation?->getOutput()) && null === $operation->getOutput()['class']; + $noOutput = !$isErrorResponse && null === $operation?->getOutputClass(); $response = $existingResponses[$status] ?? new Response($description); if (null === $response->getDescription()) { @@ -676,7 +676,7 @@ private function getLinks(ResourceMetadataCollection $resourceMetadataCollection continue; } - if ($uriVariableDefinition->getFromClass() === $currentOperation->getClass()) { + if ($uriVariableDefinition->getFromClass() === $currentOperation->getApiClass()) { $parameters[$parameterName] = '$response.body#/'.($uriVariableDefinition->getIdentifiers()[0] ?? 'id'); } } @@ -700,7 +700,7 @@ private function getFiltersParameters(CollectionOperationInterface|HttpOperation { $parameters = []; $resourceFilters = $operation->getFilters(); - $entityClass = $this->getStateOptionsClass($operation, $operation->getClass()); + $entityClass = $operation->getDataClass(); foreach ($resourceFilters ?? [] as $filterId) { if (!$this->filterLocator->has($filterId)) { @@ -1028,13 +1028,13 @@ private function addOperationErrors( $operationErrorSchemas = []; foreach ($responseMimeTypes as $operationFormat) { $operationErrorSchema = null; - $operationErrorSchema = $this->jsonSchemaFactory->buildSchema($errorResource->getClass(), $operationFormat, Schema::TYPE_OUTPUT, null, $schema, $serializerContext); + $operationErrorSchema = $this->jsonSchemaFactory->buildSchema($errorResource->getApiClass(), $operationFormat, Schema::TYPE_OUTPUT, null, $schema, $serializerContext); $this->appendSchemaDefinitions($schemas, $operationErrorSchema->getDefinitions()); $operationErrorSchemas[$operationFormat] = $operationErrorSchema; } if (!$status = $errorResource->getStatus()) { - throw new RuntimeException(\sprintf('The error class "%s" has no status defined, please either implement ProblemExceptionInterface, or make it an ErrorResource with a status', $errorResource->getClass())); + throw new RuntimeException(\sprintf('The error class "%s" has no status defined, please either implement ProblemExceptionInterface, or make it an ErrorResource with a status', $errorResource->getApiClass())); } $operation = $this->buildOpenApiResponse($operation->getResponses() ?: [], $status, $errorResource->getDescription() ?? '', $operation, $originalOperation, $responseMimeTypes, $operationErrorSchemas, $resourceMetadataCollection, true); @@ -1084,7 +1084,7 @@ private function getErrorResource(string $error, ?int $status = null, ?string $d $errorResource = new ErrorResource(status: $status, description: $description, class: $defaultErrorResourceClass); } - if (!$errorResource->getClass()) { + if (!$errorResource->getApiClass()) { $errorResource = $errorResource->withClass($error); } diff --git a/src/Serializer/InputOutputMetadataTrait.php b/src/Serializer/InputOutputMetadataTrait.php index 03b604f97fe..16d16f9b673 100644 --- a/src/Serializer/InputOutputMetadataTrait.php +++ b/src/Serializer/InputOutputMetadataTrait.php @@ -21,6 +21,10 @@ trait InputOutputMetadataTrait protected function getInputClass(array $context = []): ?string { + if (\is_string($context['input'] ?? null)) { + return $context['input']; + } + if (!$this->resourceMetadataCollectionFactory) { return $context['input']['class'] ?? null; } @@ -34,6 +38,10 @@ protected function getInputClass(array $context = []): ?string protected function getOutputClass(array $context = []): ?string { + if (\is_string($context['output'] ?? null)) { + return $context['output']; + } + if (!$this->resourceMetadataCollectionFactory) { return $context['output']['class'] ?? null; } diff --git a/src/Serializer/SerializerContextBuilder.php b/src/Serializer/SerializerContextBuilder.php index 63ee797f426..76987f30124 100644 --- a/src/Serializer/SerializerContextBuilder.php +++ b/src/Serializer/SerializerContextBuilder.php @@ -64,8 +64,8 @@ public function createFromRequest(Request $request, bool $normalization, ?array $context['iri_only'] ??= false; $context['request_uri'] = $request->getRequestUri(); $context['uri'] = $request->getUri(); - $context['input'] = $operation->getInput(); - $context['output'] = $operation->getOutput(); + $context['input'] = $operation->getInputClass() === $operation->getApiClass() ? null : ['class' => $operation->getInputClass()]; + $context['output'] = $operation->getOutputClass() === $operation->getApiClass() ? null : ['class' => $operation->getOutputClass()]; // Special case as this is usually handled by our OperationContextTrait, here we want to force the IRI in the response if (!$operation instanceof CollectionOperationInterface && method_exists($operation, 'getItemUriTemplate') && $operation->getItemUriTemplate()) { @@ -86,7 +86,7 @@ public function createFromRequest(Request $request, bool $normalization, ?array } if (null === $context['output'] && $this->getStateOptionsClass($operation)) { - $context['force_resource_class'] = $operation->getClass(); + $context['force_resource_class'] = $operation->getApiClass(); } if ($this->debug && isset($context['groups']) && $operation instanceof ErrorOperation) { diff --git a/src/Serializer/State/JsonStreamerProcessor.php b/src/Serializer/State/JsonStreamerProcessor.php index c91b43bcba2..e21abd5aebd 100644 --- a/src/Serializer/State/JsonStreamerProcessor.php +++ b/src/Serializer/State/JsonStreamerProcessor.php @@ -64,6 +64,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = || !($request = $context['request'] ?? null) || !$operation->getJsonStream() || 'json' !== $request->getRequestFormat() + || null === ($outputClass = $operation->getOutputClass()) ) { return $this->processor?->process($data, $operation, $uriVariables, $context); } @@ -71,13 +72,13 @@ public function process(mixed $data, Operation $operation, array $uriVariables = if ($operation instanceof CollectionOperationInterface) { $data = $this->jsonStreamer->write( $data, - Type::list(Type::object($operation->getClass())), + Type::list(Type::object($outputClass)), ['data' => $data, 'operation' => $operation], ); } else { $data = $this->jsonStreamer->write( $data, - Type::object($operation->getClass()), + Type::object($outputClass), ['data' => $data, 'operation' => $operation], ); } diff --git a/src/Serializer/State/JsonStreamerProvider.php b/src/Serializer/State/JsonStreamerProvider.php index 26ac7fc923d..07339d72e00 100644 --- a/src/Serializer/State/JsonStreamerProvider.php +++ b/src/Serializer/State/JsonStreamerProvider.php @@ -39,7 +39,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c return $data; } - $data = $this->jsonStreamReader->read($request->getContent(true), Type::object($operation->getClass())); + $data = $this->jsonStreamReader->read($request->getContent(true), Type::object($operation->getInputClass())); $context['request']->attributes->set('deserialized', true); if (\PHP_VERSION_ID > 80400) { diff --git a/src/State/CreateProvider.php b/src/State/CreateProvider.php index f3d7103d4f0..a4667647e50 100644 --- a/src/State/CreateProvider.php +++ b/src/State/CreateProvider.php @@ -62,9 +62,9 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } try { - $resource = new ($operation->getClass()); + $resource = new ($operation->getApiClass()); } catch (\Throwable $e) { - throw new RuntimeException(\sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getClass(), ProviderInterface::class), 0, $e); + throw new RuntimeException(\sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getApiClass(), ProviderInterface::class), 0, $e); } $property = $operationUriVariables[$key]->getToProperty() ?? $key; diff --git a/src/State/DataOptionsInterface.php b/src/State/DataOptionsInterface.php new file mode 100644 index 00000000000..52d00d4d352 --- /dev/null +++ b/src/State/DataOptionsInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\State; + +interface DataOptionsInterface extends OptionsInterface +{ + public function getDataClass(): ?string; +} diff --git a/src/State/ErrorProvider.php b/src/State/ErrorProvider.php index d8e13853d0a..a76ce0f078a 100644 --- a/src/State/ErrorProvider.php +++ b/src/State/ErrorProvider.php @@ -45,7 +45,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c // We change the operation to get our normalization context according to the format if ($this->resourceMetadataCollectionFactory) { - $resourceCollection = $this->resourceMetadataCollectionFactory->create($operation->getClass()); + $resourceCollection = $this->resourceMetadataCollectionFactory->create($operation->getApiClass()); foreach ($resourceCollection as $resource) { foreach ($resource->getOperations() as $name => $operation) { if (isset($operation->getOutputFormats()[$request->getRequestFormat()])) { @@ -59,7 +59,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $text = Response::$statusTexts[$status] ?? throw new NotFoundHttpException(); - $cl = $operation->getClass(); + $cl = $operation->getApiClass(); return match ($request->getRequestFormat()) { 'html' => $this->renderError((int) $status, $text), @@ -72,7 +72,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } $status = $operation->getStatus() ?? 500; - $cl = is_a($operation->getClass(), ErrorResourceInterface::class, true) ? $operation->getClass() : Error::class; + $cl = is_a($operation->getApiClass(), ErrorResourceInterface::class, true) ? $operation->getApiClass() : Error::class; $error = $cl::createFromException($exception, $status); if (!$this->debug && $status >= 500 && method_exists($error, 'setDetail')) { $error->setDetail('Internal Server Error'); diff --git a/src/State/ObjectProvider.php b/src/State/ObjectProvider.php index 97a0572e717..f08204825d9 100644 --- a/src/State/ObjectProvider.php +++ b/src/State/ObjectProvider.php @@ -26,9 +26,9 @@ final class ObjectProvider implements ProviderInterface public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?object { try { - return new ($operation->getClass()); + return new ($operation->getApiClass()); } catch (\Throwable $e) { - throw new RuntimeException(\sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getClass(), ProviderInterface::class), 0, $e); + throw new RuntimeException(\sprintf('An error occurred while trying to create an instance of the "%s" resource. Consider writing your own "%s" implementation and setting it as `provider` on your operation instead.', $operation->getApiClass(), ProviderInterface::class), 0, $e); } } } diff --git a/src/State/ParameterProvider/ReadLinkParameterProvider.php b/src/State/ParameterProvider/ReadLinkParameterProvider.php index 9b64676d5c0..49fbee6c9e8 100644 --- a/src/State/ParameterProvider/ReadLinkParameterProvider.php +++ b/src/State/ParameterProvider/ReadLinkParameterProvider.php @@ -52,7 +52,7 @@ public function provide(Parameter $parameter, array $parameters = [], array $con $securityObjectName ??= $parameter->getKey(); - $linkClass ??= $extraProperties['resource_class'] ?? $operation->getClass(); + $linkClass ??= $extraProperties['resource_class'] ?? $operation->getApiClass(); if (!$linkClass) { return $operation; diff --git a/src/State/Processor/ObjectMapperInputProcessor.php b/src/State/Processor/ObjectMapperInputProcessor.php index 845be27e098..ffbb3546c9b 100644 --- a/src/State/Processor/ObjectMapperInputProcessor.php +++ b/src/State/Processor/ObjectMapperInputProcessor.php @@ -39,12 +39,12 @@ public function __construct( public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - $class = $operation->getInput()['class'] ?? $operation->getClass(); + $class = $operation->getInputClass(); if ( $data instanceof Response || !$this->objectMapper - || !($operation->canWrite() ?? true) + || !($operation->canWrite() ?? false) || null === $data || null === $class || !is_a($data, $class, true) @@ -54,7 +54,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = } $request = $context['request'] ?? null; - $mapped = $this->objectMapper->map($data, $request?->attributes->get('mapped_data') ?? $this->getStateOptionsClass($operation, $operation->getClass())); + $mapped = $this->objectMapper->map($data, $request?->attributes->get('mapped_data') ?? $operation->getDataClass()); $request?->attributes->set('mapped_data', $mapped); return $this->decorated ? $this->decorated->process($mapped, $operation, $uriVariables, $context) : $mapped; diff --git a/src/State/Processor/ObjectMapperOutputProcessor.php b/src/State/Processor/ObjectMapperOutputProcessor.php index 91a4fa169eb..12dce971bcb 100644 --- a/src/State/Processor/ObjectMapperOutputProcessor.php +++ b/src/State/Processor/ObjectMapperOutputProcessor.php @@ -39,7 +39,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = if ( $data instanceof Response || !$this->objectMapper - || !($operation->canWrite() ?? true) + || !($operation->canWrite() ?? false) || null === $data || !$operation->canMap() ) { @@ -48,7 +48,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $request = $context['request'] ?? null; $request?->attributes->set('persisted_data', $data); - $dto = $this->objectMapper->map($data, $operation->getClass()); + $dto = $this->objectMapper->map($data, $operation->getOutputClass()); return $this->decorated ? $this->decorated->process($dto, $operation, $uriVariables, $context) : $dto; } diff --git a/src/State/Processor/ObjectMapperProcessor.php b/src/State/Processor/ObjectMapperProcessor.php index f7bb34a367e..16dc2176dd8 100644 --- a/src/State/Processor/ObjectMapperProcessor.php +++ b/src/State/Processor/ObjectMapperProcessor.php @@ -40,7 +40,7 @@ public function __construct( public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - $class = $operation->getInput()['class'] ?? $operation->getClass(); + $class = $operation->getInputClass(); if ( $data instanceof Response @@ -52,14 +52,13 @@ public function process(mixed $data, Operation $operation, array $uriVariables = ) { return $this->decorated->process($data, $operation, $uriVariables, $context); } - $request = $context['request'] ?? null; // maps the Resource to an Entity if ($request?->attributes->get('mapped_data')) { $mappedData = $this->objectMapper->map($data, $request->attributes->get('mapped_data')); } else { - $mappedData = $this->objectMapper->map($data, $this->getStateOptionsClass($operation, $operation->getClass())); + $mappedData = $this->objectMapper->map($data, $operation->getDataClass()); } $request?->attributes->set('mapped_data', $mappedData); @@ -81,7 +80,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = return $this->objectMapper->map( // persist the entity $persisted, - $operation->getClass() + $operation->getOutputClass() ); } } diff --git a/src/State/Processor/SerializeProcessor.php b/src/State/Processor/SerializeProcessor.php index 8047a384899..fb293771a6b 100644 --- a/src/State/Processor/SerializeProcessor.php +++ b/src/State/Processor/SerializeProcessor.php @@ -60,7 +60,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = // @see ApiPlatform\State\Processor\RespondProcessor $context['original_data'] = $data; - $class = $operation->getClass(); + $class = $operation->getApiClass(); $serializerContext = $this->serializerContextBuilder->createFromRequest($request, true, [ 'resource_class' => $class, 'operation' => $operation, @@ -68,7 +68,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $serializerContext['uri_variables'] = $uriVariables; - if (isset($serializerContext['output']) && \array_key_exists('class', $serializerContext['output']) && null === $serializerContext['output']['class']) { + if (\is_array($serializerContext['output'] ?? null) && \array_key_exists('class', $serializerContext['output']) && null === $serializerContext['output']['class']) { $this->stopwatch?->stop('api_platform.processor.serialize'); return $this->processor ? $this->processor->process(null, $operation, $uriVariables, $context) : null; diff --git a/src/State/Provider/BackedEnumProvider.php b/src/State/Provider/BackedEnumProvider.php index 71769ee3bd6..0f105d3f763 100644 --- a/src/State/Provider/BackedEnumProvider.php +++ b/src/State/Provider/BackedEnumProvider.php @@ -23,7 +23,7 @@ final class BackedEnumProvider implements ProviderInterface { public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array { - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getApiClass(); if (!$resourceClass || !is_a($resourceClass, \BackedEnum::class, true)) { throw new RuntimeException('This resource is not an enum'); } diff --git a/src/State/Provider/ContentNegotiationProvider.php b/src/State/Provider/ContentNegotiationProvider.php index 42e81252282..3ce7fe199f8 100644 --- a/src/State/Provider/ContentNegotiationProvider.php +++ b/src/State/Provider/ContentNegotiationProvider.php @@ -97,11 +97,7 @@ private function flattenMimeTypes(array $formats): array */ private function getInputFormat(HttpOperation $operation, Request $request): ?string { - if ( - false === ($input = $operation->getInput()) - || (\is_array($input) && null === $input['class']) - || false === $operation->canDeserialize() - ) { + if (false === $operation->canDeserialize()) { return null; } diff --git a/src/State/Provider/DeserializeProvider.php b/src/State/Provider/DeserializeProvider.php index 338c8371418..27ab1c5cfb6 100644 --- a/src/State/Provider/DeserializeProvider.php +++ b/src/State/Provider/DeserializeProvider.php @@ -71,7 +71,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } $serializerContext = $this->serializerContextBuilder->createFromRequest($request, false, [ - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), 'operation' => $operation, ]); @@ -100,7 +100,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c unset($serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE]); try { - $data = $this->serializer->deserialize((string) $request->getContent(), $serializerContext['deserializer_type'] ?? $operation->getClass(), $format, $serializerContext); + $data = $this->serializer->deserialize((string) $request->getContent(), $serializerContext['deserializer_type'] ?? $operation->getInputClass(), $format, $serializerContext); } catch (PartialDenormalizationException $e) { if (!class_exists(ConstraintViolationList::class)) { throw $e; diff --git a/src/State/Provider/ObjectMapperProvider.php b/src/State/Provider/ObjectMapperProvider.php index 9986f43954f..0896ea77a55 100644 --- a/src/State/Provider/ObjectMapperProvider.php +++ b/src/State/Provider/ObjectMapperProvider.php @@ -41,7 +41,7 @@ public function __construct( public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { $data = $this->decorated->provide($operation, $uriVariables, $context); - $class = $operation->getOutput()['class'] ?? $operation->getClass(); + $class = $operation->getOutputClass(); if (!$this->objectMapper || !$operation->canMap()) { return $data; diff --git a/src/State/Provider/ReadProvider.php b/src/State/Provider/ReadProvider.php index c6c65a3888b..6894535fddc 100644 --- a/src/State/Provider/ReadProvider.php +++ b/src/State/Provider/ReadProvider.php @@ -69,7 +69,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $context['filters'] = $filters; } - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getApiClass(); if ($this->serializerContextBuilder && $request) { // Builtin data providers are able to use the serialization context to automatically add join clauses diff --git a/src/State/UriVariablesResolverTrait.php b/src/State/UriVariablesResolverTrait.php index 0463041b1f2..b94fea1850c 100644 --- a/src/State/UriVariablesResolverTrait.php +++ b/src/State/UriVariablesResolverTrait.php @@ -64,7 +64,7 @@ private function getOperationUriVariables(?HttpOperation $operation = null, arra if ($this->uriVariablesConverter) { $context = ['operation' => $operation, 'uri_variables_map' => $uriVariablesMap]; - $identifiers = $this->uriVariablesConverter->convert($identifiers, $operation->getClass() ?? $resourceClass, $context); + $identifiers = $this->uriVariablesConverter->convert($identifiers, $operation->getApiClass() ?? $resourceClass, $context); } return $identifiers; diff --git a/src/State/Util/HttpResponseHeadersTrait.php b/src/State/Util/HttpResponseHeadersTrait.php index 6b0190c50b6..5b710cb0a72 100644 --- a/src/State/Util/HttpResponseHeadersTrait.php +++ b/src/State/Util/HttpResponseHeadersTrait.php @@ -53,10 +53,11 @@ private function getHeaders(Request $request, HttpOperation $operation, array $c { $status = $this->getStatus($request, $operation, $context); $method = $request->getMethod(); - $output = $operation->getOutput(); - $outputMetadata = $output ?? ['class' => $operation->getClass()]; - $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class']; - $outputExplicitlyDisabled = \is_array($output) && \array_key_exists('class', $output) && null === $output['class']; + $outputClass = $operation->getOutputClass(); + $hasOutput = null !== $outputClass; + // Disabled only when output: false was explicitly set. When there's no resource class at all + // (e.g. context endpoints), getOutputClass() also returns null but the output is not disabled. + $outputExplicitlyDisabled = null === $outputClass && null !== $operation->getClass(); // RFC 7230 §3.3.2 / §3.3.3: 204, 205 and 304 responses MUST NOT include a payload body, // and a sender MUST NOT generate a Content-Type field for a message without a body. $isBodylessStatus = \in_array($status, [Response::HTTP_NO_CONTENT, Response::HTTP_RESET_CONTENT, Response::HTTP_NOT_MODIFIED], true); @@ -118,8 +119,8 @@ private function getHeaders(Request $request, HttpOperation $operation, array $c $iri = null; if ($hasData) { $iri = $this->iriConverter->getIriFromResource($originalData); - } elseif ($operation->getClass()) { - $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation); + } elseif ($operation->getApiClass()) { + $iri = $this->iriConverter->getIriFromResource($operation->getApiClass(), UrlGeneratorInterface::ABS_PATH, $operation); } if ($iri && 'GET' !== $method) { @@ -140,7 +141,7 @@ private function getHeaders(Request $request, HttpOperation $operation, array $c if ( !$operation instanceof Error && $operation->getUriTemplate() - && $this->resourceClassResolver?->isResourceClass($operation->getClass()) + && $this->resourceClassResolver?->isResourceClass($operation->getApiClass()) ) { $this->addLinkedDataPlatformHeaders($headers, $operation); } @@ -156,7 +157,7 @@ private function addLinkedDataPlatformHeaders(array &$headers, HttpOperation $op $acceptPost = null; $allowedMethods = ['OPTIONS', 'HEAD']; - $resourceCollection = $this->resourceMetadataCollectionFactory->create($operation->getClass()); + $resourceCollection = $this->resourceMetadataCollectionFactory->create($operation->getApiClass()); foreach ($resourceCollection as $resource) { foreach ($resource->getOperations() as $op) { if ($op->getUriTemplate() === $operation->getUriTemplate()) { diff --git a/src/State/Util/HttpResponseStatusTrait.php b/src/State/Util/HttpResponseStatusTrait.php index 89b9156c3ea..c590572eabd 100644 --- a/src/State/Util/HttpResponseStatusTrait.php +++ b/src/State/Util/HttpResponseStatusTrait.php @@ -40,8 +40,7 @@ private function getStatus(Request $request, HttpOperation $operation, array $co $status = $operation->getStatus(); $method = $request->getMethod(); - $outputMetadata = $operation->getOutput() ?? ['class' => $operation->getClass()]; - $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class']; + $hasOutput = null !== $operation->getOutputClass(); $originalData = $context['original_data'] ?? null; $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData))); diff --git a/src/Symfony/Bundle/SwaggerUi/SwaggerUiProvider.php b/src/Symfony/Bundle/SwaggerUi/SwaggerUiProvider.php index f2c526e90e6..0be26e167b5 100644 --- a/src/Symfony/Bundle/SwaggerUi/SwaggerUiProvider.php +++ b/src/Symfony/Bundle/SwaggerUi/SwaggerUiProvider.php @@ -37,7 +37,7 @@ public function __construct(private readonly ProviderInterface $decorated, priva public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { // We went through the DocumentationAction - if (OpenApi::class === $operation->getClass()) { + if (OpenApi::class === $operation->getApiClass()) { return $this->decorated->provide($operation, $uriVariables, $context); } @@ -58,7 +58,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c // when it fails we'll get an Error and we'll fix the status accordingly // @see features/main/content_negotiation.feature:119 // When requesting DocumentationAction or EntrypointAction with Accept: text/html we render SwaggerUi - if (!$operation instanceof Error && !\in_array($operation->getClass(), [Documentation::class, Entrypoint::class], true)) { + if (!$operation instanceof Error && !\in_array($operation->getApiClass(), [Documentation::class, Entrypoint::class], true)) { $this->decorated->provide($operation, $uriVariables, $context); } diff --git a/src/Symfony/Controller/MainController.php b/src/Symfony/Controller/MainController.php index f50c49ec105..8835d717f17 100644 --- a/src/Symfony/Controller/MainController.php +++ b/src/Symfony/Controller/MainController.php @@ -56,7 +56,7 @@ public function __invoke(Request $request): Response $uriVariables = []; if (!$request->attributes->has('exception')) { try { - $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass()); + $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getApiClass()); $request->attributes->set('_api_uri_variables', $uriVariables); } catch (InvalidIdentifierException|InvalidUriVariableException $e) { throw new NotFoundHttpException('Invalid uri variables.', $e); @@ -66,7 +66,7 @@ public function __invoke(Request $request): Response $context = [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]; if (null === $operation->canValidate()) { @@ -99,7 +99,7 @@ public function __invoke(Request $request): Response if (!$request->attributes->has('exception')) { try { - $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass()); + $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getApiClass()); } catch (InvalidIdentifierException|InvalidUriVariableException $e) { // if this occurs with our base operation we throw above so log instead of throw here if ($this->logger) { diff --git a/src/Symfony/EventListener/AddFormatListener.php b/src/Symfony/EventListener/AddFormatListener.php index efbe15dd3d0..a18cb2aa06b 100644 --- a/src/Symfony/EventListener/AddFormatListener.php +++ b/src/Symfony/EventListener/AddFormatListener.php @@ -58,7 +58,7 @@ public function onKernelRequest(RequestEvent $event): void $this->provider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 'request' => $request, 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]); } } diff --git a/src/Symfony/EventListener/DeserializeListener.php b/src/Symfony/EventListener/DeserializeListener.php index bf34cea24ca..9d9a222cd2b 100644 --- a/src/Symfony/EventListener/DeserializeListener.php +++ b/src/Symfony/EventListener/DeserializeListener.php @@ -82,7 +82,7 @@ public function onKernelRequest(RequestEvent $event): void $this->provider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 'request' => $request, 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]); } } diff --git a/src/Symfony/EventListener/ErrorListener.php b/src/Symfony/EventListener/ErrorListener.php index 24266534586..9edd6583c3a 100644 --- a/src/Symfony/EventListener/ErrorListener.php +++ b/src/Symfony/EventListener/ErrorListener.php @@ -113,7 +113,7 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re $operation = $operation->withNormalizationContext($normalizationContext); - $dup->attributes->set('_api_resource_class', $operation->getClass()); + $dup->attributes->set('_api_resource_class', $operation->getApiClass()); $dup->attributes->set('_api_previous_operation', $apiOperation); $dup->attributes->set('_api_operation', $operation); $dup->attributes->set('_api_operation_name', $operation->getName()); diff --git a/src/Symfony/EventListener/JsonStreamerDeserializeListener.php b/src/Symfony/EventListener/JsonStreamerDeserializeListener.php index aebc9cf6182..f57f0d0147f 100644 --- a/src/Symfony/EventListener/JsonStreamerDeserializeListener.php +++ b/src/Symfony/EventListener/JsonStreamerDeserializeListener.php @@ -60,7 +60,7 @@ public function onKernelRequest(RequestEvent $event): void $data = $this->jsonStreamerProvider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 'request' => $request, 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]); $request->attributes->set('data', $data); diff --git a/src/Symfony/EventListener/JsonStreamerSerializeListener.php b/src/Symfony/EventListener/JsonStreamerSerializeListener.php index 9655ceb3d36..8237aa14ba4 100644 --- a/src/Symfony/EventListener/JsonStreamerSerializeListener.php +++ b/src/Symfony/EventListener/JsonStreamerSerializeListener.php @@ -54,7 +54,7 @@ public function onKernelView(ViewEvent $event): void $response = $this->jsonStreamerProcessor->process($event->getControllerResult(), $operation, $uriVariables, [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), 'original_data' => $request->attributes->get('original_data'), ]); diff --git a/src/Symfony/EventListener/ReadListener.php b/src/Symfony/EventListener/ReadListener.php index 203d2d27644..dee7edcc8b5 100644 --- a/src/Symfony/EventListener/ReadListener.php +++ b/src/Symfony/EventListener/ReadListener.php @@ -74,7 +74,7 @@ public function onKernelRequest(RequestEvent $event): void $uriVariables = []; if (!$request->attributes->has('exception')) { try { - $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass()); + $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getApiClass()); } catch (InvalidIdentifierException|InvalidUriVariableException $e) { if ($operation->canRead()) { throw new NotFoundHttpException('Invalid identifier value or configuration.', $e); @@ -86,7 +86,7 @@ public function onKernelRequest(RequestEvent $event): void $context = [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]; $this->parameterProvider?->provide($operation, $uriVariables, $context); $this->provider->provide($operation, $uriVariables, $context); diff --git a/src/Symfony/EventListener/RespondListener.php b/src/Symfony/EventListener/RespondListener.php index 15c12c7f103..80764f2339c 100644 --- a/src/Symfony/EventListener/RespondListener.php +++ b/src/Symfony/EventListener/RespondListener.php @@ -54,7 +54,7 @@ public function onKernelView(ViewEvent $event): void $response = $this->processor->process($event->getControllerResult(), $operation, $uriVariables, [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), 'original_data' => $request->attributes->get('original_data'), ]); diff --git a/src/Symfony/EventListener/SerializeListener.php b/src/Symfony/EventListener/SerializeListener.php index 16dfd17311b..8cd943703be 100644 --- a/src/Symfony/EventListener/SerializeListener.php +++ b/src/Symfony/EventListener/SerializeListener.php @@ -65,7 +65,7 @@ public function onKernelView(ViewEvent $event): void $serialized = $this->processor->process($controllerResult, $operation, $uriVariables, [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]); $event->setControllerResult($serialized); diff --git a/src/Symfony/EventListener/ValidateListener.php b/src/Symfony/EventListener/ValidateListener.php index cd9d8eb9e39..b885e761b07 100644 --- a/src/Symfony/EventListener/ValidateListener.php +++ b/src/Symfony/EventListener/ValidateListener.php @@ -57,7 +57,7 @@ public function onKernelView(ViewEvent $event): void $this->provider->provide($operation, $request->attributes->get('_api_uri_variables') ?? [], [ 'request' => $request, 'uri_variables' => $request->attributes->get('_api_uri_variables') ?? [], - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), ]); } } diff --git a/src/Symfony/EventListener/WriteListener.php b/src/Symfony/EventListener/WriteListener.php index 812b66f414a..3ef8e2a8c67 100644 --- a/src/Symfony/EventListener/WriteListener.php +++ b/src/Symfony/EventListener/WriteListener.php @@ -71,7 +71,7 @@ public function onKernelView(ViewEvent $event): void $uriVariables = $request->attributes->get('_api_uri_variables') ?? []; if (!$uriVariables && !$operation instanceof Error) { try { - $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass()); + $uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getApiClass()); } catch (InvalidIdentifierException|InvalidUriVariableException $e) { throw new NotFoundHttpException('Invalid identifier value or configuration.', $e); } @@ -80,7 +80,7 @@ public function onKernelView(ViewEvent $event): void $data = $this->processor->process($event->getControllerResult(), $operation, $uriVariables, [ 'request' => $request, 'uri_variables' => $uriVariables, - 'resource_class' => $operation->getClass(), + 'resource_class' => $operation->getApiClass(), 'previous_data' => false === $operation->canRead() ? null : $request->attributes->get('previous_data'), // this is a clone 'read_data' => false === $operation->canRead() ? null : $request->attributes->get('read_data'), // this is what we read 'data' => false === $operation->canRead() ? null : $request->attributes->get('data'), // this should be the same as getControllerResult but is the result of deserialization diff --git a/src/Symfony/Routing/IriConverter.php b/src/Symfony/Routing/IriConverter.php index eb5ce19576f..57e6a0676ac 100644 --- a/src/Symfony/Routing/IriConverter.php +++ b/src/Symfony/Routing/IriConverter.php @@ -84,7 +84,7 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation } } - if ($operation && !is_a($parameters['_api_resource_class'], $operation->getClass(), true)) { + if ($operation && !is_a($parameters['_api_resource_class'], $operation->getApiClass(), true)) { throw new InvalidArgumentException(\sprintf('The iri "%s" does not reference the correct resource.', $iri)); } @@ -151,7 +151,7 @@ public function getIriFromResource(object|string $resource, int $referenceType = } if ($operation instanceof HttpOperation && 301 === $operation->getStatus()) { - $operation = ($operation instanceof CollectionOperationInterface ? new GetCollection() : new Get())->withClass($operation->getClass()); + $operation = ($operation instanceof CollectionOperationInterface ? new GetCollection() : new Get())->withClass($operation->getApiClass()); unset($context['uri_variables']); } @@ -199,7 +199,7 @@ private function generateSymfonyRoute(object|string $resource, int $referenceTyp } catch (InvalidArgumentException|RuntimeException $e) { // We can try using context uri variables if any if (!$identifiers && ($context['is_resource_class'] ?? false)) { - throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getApiClass()), $e->getCode(), $e); } } } @@ -209,7 +209,7 @@ private function generateSymfonyRoute(object|string $resource, int $referenceTyp return $this->router->generate($routeName, $identifiers, $operation->getUrlGenerationStrategy() ?? $referenceType); } catch (RoutingExceptionInterface $e) { - throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getClass()), $e->getCode(), $e); + throw new InvalidArgumentException(\sprintf('Unable to generate an IRI for the item of type "%s"', $operation->getApiClass()), $e->getCode(), $e); } } } diff --git a/src/Symfony/Security/State/AccessCheckerProvider.php b/src/Symfony/Security/State/AccessCheckerProvider.php index ec14aceff1c..c9ec5d1da96 100644 --- a/src/Symfony/Security/State/AccessCheckerProvider.php +++ b/src/Symfony/Security/State/AccessCheckerProvider.php @@ -97,7 +97,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c return $this->decorated->provide($operation, $uriVariables, $context); } - if (!$this->resourceAccessChecker->isGranted($operation->getClass(), $isGranted, $resourceAccessCheckerContext)) { + if (!$this->resourceAccessChecker->isGranted($operation->getApiClass(), $isGranted, $resourceAccessCheckerContext)) { $operation instanceof GraphQlOperation ? throw new AccessDeniedHttpException($message ?? 'Access Denied.') : throw new AccessDeniedException($message ?? 'Access Denied.'); } diff --git a/src/Validator/Metadata/Resource/Factory/ParameterValidationResourceMetadataCollectionFactory.php b/src/Validator/Metadata/Resource/Factory/ParameterValidationResourceMetadataCollectionFactory.php index b0efb3eca68..0b86b49012b 100644 --- a/src/Validator/Metadata/Resource/Factory/ParameterValidationResourceMetadataCollectionFactory.php +++ b/src/Validator/Metadata/Resource/Factory/ParameterValidationResourceMetadataCollectionFactory.php @@ -115,7 +115,7 @@ private function addFilterValidation(HttpOperation $operation): Parameters } $filter = $this->filterLocator->get($filter); - foreach ($filter->getDescription($operation->getClass()) as $parameterName => $definition) { + foreach ($filter->getDescription($operation->getDataClass()) as $parameterName => $definition) { $key = $parameterName; $required = $definition['required'] ?? false; $schema = $definition['schema'] ?? null; diff --git a/tests/Fixtures/TestBundle/State/ContainNonResourceProvider.php b/tests/Fixtures/TestBundle/State/ContainNonResourceProvider.php index cb5990ce659..1e4efe8429b 100644 --- a/tests/Fixtures/TestBundle/State/ContainNonResourceProvider.php +++ b/tests/Fixtures/TestBundle/State/ContainNonResourceProvider.php @@ -32,7 +32,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c throw new \InvalidArgumentException('The id must be a scalar.'); } - $resourceClass = $operation->getClass(); + $resourceClass = $operation->getApiClass(); // Retrieve the blog post item from somewhere $cnr = new $resourceClass(); $cnr->id = $id; diff --git a/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProcessor.php b/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProcessor.php index 3fb2498adbd..e52edfafd5a 100644 --- a/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProcessor.php +++ b/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProcessor.php @@ -42,11 +42,11 @@ public function process(mixed $data, Operation $operation, array $uriVariables = } /** @var EntityManager */ - $manager = $this->registry->getManagerForClass($operation->getClass()); - $entity = new ($operation->getClass())(); + $manager = $this->registry->getManagerForClass($operation->getApiClass()); + $entity = new ($operation->getApiClass())(); if (isset($context['previous_data'])) { - $entity = $manager->getReference($operation->getClass(), $context['previous_data']->id); + $entity = $manager->getReference($operation->getApiClass(), $context['previous_data']->id); } $entity->str = $data->foo; @@ -58,7 +58,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $manager->persist($entity); $manager->flush(); - $outputDto = DummyDtoInputOutputDocument::class === $operation->getClass() ? new OutputDtoDocument() : new OutputDto(); + $outputDto = DummyDtoInputOutputDocument::class === $operation->getApiClass() ? new OutputDtoDocument() : new OutputDto(); $outputDto->id = $entity->id; $outputDto->baz = $entity->num; $outputDto->bat = $entity->str; diff --git a/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProvider.php b/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProvider.php index d9f72c4b5be..60ce6d03e24 100644 --- a/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProvider.php +++ b/tests/Fixtures/TestBundle/State/DummyDtoInputOutputProvider.php @@ -35,7 +35,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c /** @var DummyDtoInputOutput */ $data = $this->decorated->provide($operation, $uriVariables, $context); - $outputDto = DummyDtoInputOutputDocument::class === $operation->getClass() ? new OutputDtoDocument() : new OutputDto(); + $outputDto = DummyDtoInputOutputDocument::class === $operation->getApiClass() ? new OutputDtoDocument() : new OutputDto(); $outputDto->id = $data->id; $outputDto->baz = $data->num; $outputDto->bat = $data->str; diff --git a/tests/Fixtures/TestBundle/State/FakeProvider.php b/tests/Fixtures/TestBundle/State/FakeProvider.php index b38b8b3da71..3a501bd1cf1 100644 --- a/tests/Fixtures/TestBundle/State/FakeProvider.php +++ b/tests/Fixtures/TestBundle/State/FakeProvider.php @@ -20,7 +20,7 @@ final class FakeProvider implements ProviderInterface { public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - $className = $operation->getClass(); + $className = $operation->getApiClass(); $data = [ '12345' => new $className('12345', 'Vincent'), '67890' => new $className('67890', 'Grégoire'), diff --git a/tests/Fixtures/TestBundle/State/RelatedQuestionsProvider.php b/tests/Fixtures/TestBundle/State/RelatedQuestionsProvider.php index 20da9d73b95..58df5872f34 100644 --- a/tests/Fixtures/TestBundle/State/RelatedQuestionsProvider.php +++ b/tests/Fixtures/TestBundle/State/RelatedQuestionsProvider.php @@ -27,8 +27,8 @@ public function __construct(private readonly ManagerRegistry $registry) public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable { - $manager = $this->registry->getManagerForClass($operation->getClass()); - $repository = $manager->getRepository($operation->getClass()); + $manager = $this->registry->getManagerForClass($operation->getDataClass()); + $repository = $manager->getRepository($operation->getDataClass()); /** @var Question|QuestionDocument */ $question = $repository->findOneBy(['id' => 1]); diff --git a/tests/Fixtures/TestBundle/State/SerializableProvider.php b/tests/Fixtures/TestBundle/State/SerializableProvider.php index 3eca79faf54..5cdaf186d3b 100644 --- a/tests/Fixtures/TestBundle/State/SerializableProvider.php +++ b/tests/Fixtures/TestBundle/State/SerializableProvider.php @@ -38,6 +38,6 @@ public function provide(Operation $operation, array $uriVariables = [], array $c "foo": "Lorem", "bar": "Ipsum" } -JSON, $operation->getClass(), 'json'); +JSON, $operation->getApiClass(), 'json'); } }