Skip to content

Commit 601501e

Browse files
committed
Offer post, put, delete, patch subresource routes
1 parent 5af79dc commit 601501e

File tree

6 files changed

+1182
-34
lines changed

6 files changed

+1182
-34
lines changed

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@
212212
<service id="api_platform.action.put_item" alias="api_platform.action.placeholder" public="true" />
213213
<service id="api_platform.action.delete_item" alias="api_platform.action.placeholder" public="true" />
214214
<service id="api_platform.action.get_subresource" alias="api_platform.action.placeholder" public="true" />
215+
<service id="api_platform.action.post_subresource" alias="api_platform.action.placeholder" public="true" />
216+
<service id="api_platform.action.put_subresource" alias="api_platform.action.placeholder" public="true" />
217+
<service id="api_platform.action.delete_subresource" alias="api_platform.action.placeholder" public="true" />
218+
<service id="api_platform.action.patch_subresource" alias="api_platform.action.placeholder" public="true" />
215219

216220
<service id="api_platform.action.entrypoint" class="ApiPlatform\Core\Action\EntrypointAction" public="true">
217221
<argument type="service" id="api_platform.metadata.resource.name_collection_factory" />
@@ -268,6 +272,7 @@
268272
<argument type="service" id="api_platform.metadata.property.name_collection_factory" />
269273
<argument type="service" id="api_platform.metadata.property.metadata_factory" />
270274
<argument type="service" id="api_platform.path_segment_name_generator" />
275+
<argument>%api_platform.formats%</argument>
271276
</service>
272277

273278
<service id="api_platform.subresource_operation_factory.cached" class="ApiPlatform\Core\Operation\Factory\CachedSubresourceOperationFactory" decorates="api_platform.subresource_operation_factory" decoration-priority="-10" public="false">

src/Bridge/Symfony/Routing/ApiLoader.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,9 @@ public function load($data, $type = null): RouteCollection
109109
}
110110

111111
foreach ($this->subresourceOperationFactory->create($resourceClass) as $operationId => $operation) {
112-
if (null === $controller = $operation['controller'] ?? null) {
113-
$controller = self::DEFAULT_ACTION_PATTERN.'get_subresource';
112+
$this->assertOperationMethod($resourceClass, $operationId, $operation);
114113

115-
if (!$this->container->has($controller)) {
116-
throw new RuntimeException(sprintf('There is no builtin action for the %s %s operation. You need to define the controller yourself.', OperationType::SUBRESOURCE, 'GET'));
117-
}
118-
}
114+
$controller = $this->resolveOperationController($operation, OperationType::SUBRESOURCE);
119115

120116
$routeCollection->add($operation['route_name'], new Route(
121117
$operation['path'],
@@ -135,7 +131,7 @@ public function load($data, $type = null): RouteCollection
135131
$operation['options'] ?? [],
136132
$operation['host'] ?? '',
137133
$operation['schemes'] ?? [],
138-
['GET'],
134+
[$operation['method']],
139135
$operation['condition'] ?? ''
140136
));
141137
}
@@ -189,17 +185,9 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
189185
return;
190186
}
191187

192-
if (!isset($operation['method'])) {
193-
throw new RuntimeException(sprintf('Either a "route_name" or a "method" operation attribute must exist for the operation "%s" of the resource "%s".', $operationName, $resourceClass));
194-
}
195-
196-
if (null === $controller = $operation['controller'] ?? null) {
197-
$controller = sprintf('%s%s_%s', self::DEFAULT_ACTION_PATTERN, strtolower($operation['method']), $operationType);
188+
$this->assertOperationMethod($resourceClass, $operationName, $operation);
198189

199-
if (!$this->container->has($controller)) {
200-
throw new RuntimeException(sprintf('There is no builtin action for the %s %s operation. You need to define the controller yourself.', $operationType, $operation['method']));
201-
}
202-
}
190+
$controller = $this->resolveOperationController($operation, $operationType);
203191

204192
$path = trim(trim($resourceMetadata->getAttribute('route_prefix', '')), '/');
205193
$path .= $this->operationPathResolver->resolveOperationPath($resourceShortName, $operation, $operationType, $operationName);
@@ -222,4 +210,24 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas
222210

223211
$routeCollection->add(RouteNameGenerator::generate($operationName, $resourceShortName, $operationType), $route);
224212
}
213+
214+
private function assertOperationMethod(string $resourceClass, string $operationName, array $operation)
215+
{
216+
if (!isset($operation['method'])) {
217+
throw new RuntimeException(sprintf('Either a "route_name" or a "method" operation attribute must exist for the operation "%s" of the resource "%s".', $operationName, $resourceClass));
218+
}
219+
}
220+
221+
private function resolveOperationController(array $operation, string $operationType): string
222+
{
223+
if (null === $controller = $operation['controller'] ?? null) {
224+
$controller = sprintf('%s%s_%s', self::DEFAULT_ACTION_PATTERN, strtolower($operation['method']), $operationType);
225+
226+
if (!$this->container->has($controller)) {
227+
throw new RuntimeException(sprintf('There is no builtin action for the %s %s operation. You need to define the controller yourself.', $operationType, $operation['method']));
228+
}
229+
}
230+
231+
return $controller;
232+
}
225233
}

src/Metadata/Resource/Factory/OperationResourceMetadataFactory.php

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,19 @@ public function __construct(ResourceMetadataFactoryInterface $decorated, array $
4949
}
5050

5151
/**
52-
* {@inheritdoc}
52+
* @internal
5353
*/
54-
public function create(string $resourceClass): ResourceMetadata
54+
public static function populateOperations(string $resourceClass, ResourceMetadata $resourceMetadata, array $formats): ResourceMetadata
5555
{
56-
$resourceMetadata = $this->decorated->create($resourceClass);
5756
$isAbstract = (new \ReflectionClass($resourceClass))->isAbstract();
5857

5958
$collectionOperations = $resourceMetadata->getCollectionOperations();
6059
if (null === $collectionOperations) {
61-
$resourceMetadata = $resourceMetadata->withCollectionOperations($this->createOperations(
60+
$resourceMetadata = $resourceMetadata->withCollectionOperations(static::createOperations(
6261
$isAbstract ? ['GET'] : ['GET', 'POST']
6362
));
6463
} else {
65-
$resourceMetadata = $this->normalize(true, $resourceMetadata, $collectionOperations);
64+
$resourceMetadata = static::normalize(true, $resourceMetadata, $collectionOperations, $formats);
6665
}
6766

6867
$itemOperations = $resourceMetadata->getItemOperations();
@@ -72,16 +71,28 @@ public function create(string $resourceClass): ResourceMetadata
7271
if (!$isAbstract) {
7372
$methods[] = 'PUT';
7473

75-
if (isset($this->formats['jsonapi'])) {
74+
if (isset($formats['jsonapi'])) {
7675
$methods[] = 'PATCH';
7776
}
7877
}
7978

80-
$resourceMetadata = $resourceMetadata->withItemOperations($this->createOperations($methods));
79+
$resourceMetadata = $resourceMetadata->withItemOperations(static::createOperations($methods));
8180
} else {
82-
$resourceMetadata = $this->normalize(false, $resourceMetadata, $itemOperations);
81+
$resourceMetadata = static::normalize(false, $resourceMetadata, $itemOperations, $formats);
8382
}
8483

84+
return $resourceMetadata;
85+
}
86+
87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function create(string $resourceClass): ResourceMetadata
91+
{
92+
$resourceMetadata = $this->decorated->create($resourceClass);
93+
$formats = $this->formats;
94+
$resourceMetadata = self::populateOperations($resourceClass, $resourceMetadata, $formats);
95+
8596
$graphql = $resourceMetadata->getGraphql();
8697
if (null === $graphql) {
8798
$resourceMetadata = $resourceMetadata->withGraphql(['query' => [], 'delete' => [], 'update' => [], 'create' => []]);
@@ -92,7 +103,7 @@ public function create(string $resourceClass): ResourceMetadata
92103
return $resourceMetadata;
93104
}
94105

95-
private function createOperations(array $methods): array
106+
private static function createOperations(array $methods): array
96107
{
97108
$operations = [];
98109
foreach ($methods as $method) {
@@ -102,7 +113,7 @@ private function createOperations(array $methods): array
102113
return $operations;
103114
}
104115

105-
private function normalize(bool $collection, ResourceMetadata $resourceMetadata, array $operations): ResourceMetadata
116+
private static function normalize(bool $collection, ResourceMetadata $resourceMetadata, array $operations, array $formats): ResourceMetadata
106117
{
107118
$newOperations = [];
108119
foreach ($operations as $operationName => $operation) {
@@ -116,7 +127,7 @@ private function normalize(bool $collection, ResourceMetadata $resourceMetadata,
116127
if ($collection) {
117128
$supported = isset(self::SUPPORTED_COLLECTION_OPERATION_METHODS[$upperOperationName]);
118129
} else {
119-
$supported = isset(self::SUPPORTED_ITEM_OPERATION_METHODS[$upperOperationName]) || (isset($this->formats['jsonapi']) && 'PATCH' === $upperOperationName);
130+
$supported = isset(self::SUPPORTED_ITEM_OPERATION_METHODS[$upperOperationName]) || (isset($formats['jsonapi']) && 'PATCH' === $upperOperationName);
120131
}
121132

122133
if (!isset($operation['method']) && !isset($operation['route_name'])) {

0 commit comments

Comments
 (0)