Skip to content

Commit cb5421a

Browse files
committed
Allow an input and an output for a given resource class
1 parent c24b677 commit cb5421a

22 files changed

+395
-24
lines changed

src/Annotation/ApiResource.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@
3737
* @Attribute("filters", type="string[]"),
3838
* @Attribute("graphql", type="array"),
3939
* @Attribute("hydraContext", type="array"),
40+
* @Attribute("inputClass", type="string"),
4041
* @Attribute("iri", type="string"),
4142
* @Attribute("itemOperations", type="array"),
4243
* @Attribute("maximumItemsPerPage", type="int"),
4344
* @Attribute("normalizationContext", type="array"),
4445
* @Attribute("order", type="array"),
46+
* @Attribute("outputClass", type="string"),
4547
* @Attribute("paginationClientEnabled", type="bool"),
4648
* @Attribute("paginationClientItemsPerPage", type="bool"),
4749
* @Attribute("paginationClientPartial", type="bool"),
@@ -264,6 +266,20 @@ final class ApiResource
264266
*/
265267
private $sunset;
266268

269+
/**
270+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
271+
*
272+
* @var string
273+
*/
274+
private $inputClass;
275+
276+
/**
277+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
278+
*
279+
* @var string
280+
*/
281+
private $outputClass;
282+
267283
/**
268284
* @throws InvalidArgumentException
269285
*/

src/Bridge/Symfony/Routing/ApiLoader.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ public function load($data, $type = null): RouteCollection
123123
'_controller' => $controller,
124124
'_format' => null,
125125
'_api_resource_class' => $operation['resource_class'],
126+
'_api_input_class' => $operation['input_class'],
127+
'_api_output_class' => $operation['output_class'],
126128
'_api_subresource_operation_name' => $operation['route_name'],
127129
'_api_subresource_context' => [
128130
'property' => $operation['property'],
@@ -210,6 +212,8 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
210212
'_controller' => $controller,
211213
'_format' => null,
212214
'_api_resource_class' => $resourceClass,
215+
'_api_input_class' => $resourceMetadata->getAttribute('input_class', $resourceClass),
216+
'_api_output_class' => $resourceMetadata->getAttribute('output_class', $resourceClass),
213217
sprintf('_api_%s_operation_name', $operationType) => $operationName,
214218
] + ($operation['defaults'] ?? []),
215219
$operation['requirements'] ?? [],

src/DataProvider/OperationDataProviderTrait.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ trait OperationDataProviderTrait
4949
*/
5050
private function getCollectionData(array $attributes, array $context)
5151
{
52-
return $this->collectionDataProvider->getCollection($attributes['resource_class'], $attributes['collection_operation_name'], $context);
52+
return $this->collectionDataProvider->getCollection($attributes['output_class'], $attributes['collection_operation_name'], $context);
5353
}
5454

5555
/**
@@ -59,7 +59,7 @@ private function getCollectionData(array $attributes, array $context)
5959
*/
6060
private function getItemData($identifiers, array $attributes, array $context)
6161
{
62-
return $this->itemDataProvider->getItem($attributes['resource_class'], $identifiers, $attributes['item_operation_name'], $context);
62+
return $this->itemDataProvider->getItem($attributes['output_class'], $identifiers, $attributes['item_operation_name'], $context);
6363
}
6464

6565
/**
@@ -75,7 +75,7 @@ private function getSubresourceData($identifiers, array $attributes, array $cont
7575
throw new RuntimeException('Subresources not supported');
7676
}
7777

78-
return $this->subresourceDataProvider->getSubresource($attributes['resource_class'], $identifiers, $attributes['subresource_context'] + $context, $attributes['subresource_operation_name']);
78+
return $this->subresourceDataProvider->getSubresource($attributes['output_class'], $identifiers, $attributes['subresource_context'] + $context, $attributes['subresource_operation_name']);
7979
}
8080

8181
/**
@@ -93,7 +93,7 @@ private function extractIdentifiers(array $parameters, array $attributes)
9393
$id = $parameters['id'];
9494

9595
if (null !== $this->identifierConverter) {
96-
return $this->identifierConverter->convert((string) $id, $attributes['resource_class']);
96+
return $this->identifierConverter->convert((string) $id, $attributes['output_class']);
9797
}
9898

9999
return $id;

src/EventListener/DeserializeListener.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public function onKernelRequest(GetResponseEvent $event)
8181

8282
$format = $this->getFormat($request);
8383
$context = $this->serializerContextBuilder->createFromRequest($request, false, $attributes);
84+
if (isset($context['input_class'])) {
85+
$context['resource_class'] = $context['input_class'];
86+
}
8487

8588
$data = $request->attributes->get('data');
8689
if (null !== $data) {
@@ -90,7 +93,7 @@ public function onKernelRequest(GetResponseEvent $event)
9093
$request->attributes->set(
9194
'data',
9295
$this->serializer->deserialize(
93-
$requestContent, $attributes['resource_class'], $format, $context
96+
$requestContent, $attributes['input_class'], $format, $context
9497
)
9598
);
9699
}

src/EventListener/SerializeListener.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
6363
}
6464
$resources = new ResourceList();
6565
$context['resources'] = &$resources;
66+
if (isset($context['output_class'])) {
67+
$context['resource_class'] = $context['output_class'];
68+
}
69+
6670
$request->attributes->set('_api_normalization_context', $context);
6771

6872
$event->setControllerResult($this->serializer->serialize($controllerResult, $request->getRequestFormat(), $context));

src/EventListener/WriteListener.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
6161

6262
$event->setControllerResult($persistResult ?? $controllerResult);
6363

64-
if (null !== $this->iriConverter) {
64+
// Controller result must be immutable for _api_write_item_iri
65+
// if it's class changed compared to the base class let's avoid calling the IriConverter
66+
// especially that the Output class could be a DTO that's not referencing any route
67+
if (null !== $this->iriConverter && \get_class($controllerResult) === \get_class($event->getControllerResult())) {
6568
$request->attributes->set('_api_write_item_iri', $this->iriConverter->getIriFromItem($controllerResult));
6669
}
6770
break;

src/Operation/Factory/SubresourceOperationFactory.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
106106
'collection' => $subresource->isCollection(),
107107
'resource_class' => $subresourceClass,
108108
'shortNames' => [$subresourceMetadata->getShortName()],
109+
'input_class' => $subresourceMetadata->getAttribute('input_class', $subresourceClass),
110+
'output_class' => $subresourceMetadata->getAttribute('output_class', $subresourceClass),
109111
];
110112

111113
if (null === $parentOperation) {

src/Serializer/SerializerContextBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public function createFromRequest(Request $request, bool $normalization, array $
7373
}
7474

7575
$context['resource_class'] = $attributes['resource_class'];
76+
$context['input_class'] = $attributes['input_class'] ?? $attributes['resource_class'];
77+
$context['output_class'] = $attributes['output_class'] ?? $attributes['resource_class'];
7678
$context['request_uri'] = $request->getRequestUri();
7779
$context['uri'] = $request->getUri();
7880

src/Util/AttributesExtractor.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ private function __construct()
3535
public static function extractAttributes(array $attributes): array
3636
{
3737
$result = ['resource_class' => $attributes['_api_resource_class'] ?? null];
38+
$result['input_class'] = $attributes['_api_input_class'] ?? $result['resource_class'];
39+
$result['output_class'] = $attributes['_api_output_class'] ?? $result['resource_class'];
3840

3941
if ($subresourceContext = $attributes['_api_subresource_context'] ?? null) {
4042
$result['subresource_context'] = $subresourceContext;

tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public function testWithResource()
136136
$this->response
137137
);
138138

139-
$this->assertSame(['resource_class' => DummyEntity::class, 'item_operation_name' => 'get', 'receive' => true, 'persist' => true], $dataCollector->getRequestAttributes());
139+
$this->assertSame(['resource_class' => DummyEntity::class, 'input_class' => DummyEntity::class, 'output_class' => DummyEntity::class, 'item_operation_name' => 'get', 'receive' => true, 'persist' => true], $dataCollector->getRequestAttributes());
140140
$this->assertSame(['foo', 'bar'], $dataCollector->getAcceptableContentTypes());
141141
$this->assertSame(DummyEntity::class, $dataCollector->getResourceClass());
142142
$this->assertSame(['foo' => null, 'a_filter' => \stdClass::class], $dataCollector->getFilters());

0 commit comments

Comments
 (0)