Skip to content

Commit 683d6fc

Browse files
author
abluchet
committed
Add SubresourceOperations to metadata
1 parent 1c2bdcb commit 683d6fc

16 files changed

+108
-24
lines changed

src/Annotation/ApiResource.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ final class ApiResource
4848
*/
4949
public $collectionOperations;
5050

51+
/**
52+
* @var array
53+
*/
54+
public $subresourceOperations;
55+
5156
/**
5257
* @var array
5358
*/

src/Bridge/Symfony/Routing/ApiLoader.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,6 @@ public function load($data, $type = null): RouteCollection
8787

8888
if (null !== $collectionOperations = $resourceMetadata->getCollectionOperations()) {
8989
foreach ($collectionOperations as $operationName => $operation) {
90-
if ('subresource' === substr($operationName, -11)) {
91-
continue;
92-
}
9390
$this->addRoute($routeCollection, $resourceClass, $operationName, $operation, $resourceShortName, OperationType::COLLECTION);
9491
}
9592
}
@@ -118,13 +115,13 @@ public function load($data, $type = null): RouteCollection
118115
'collection' => $operation['collection'],
119116
'operationId' => $operationId,
120117
],
121-
] + $operations['defaults'],
122-
$operation['requirements'],
123-
$operation['options'],
124-
$operation['host'],
125-
$operation['schemes'],
118+
] + ($operation['defaults'] ?? []),
119+
($operation['requirements'] ?? []),
120+
($operation['options'] ?? []),
121+
($operation['host'] ?? ''),
122+
($operation['schemes'] ?? []),
126123
['GET'],
127-
$operation['condition']
124+
($operation['condition'] ?? '')
128125
));
129126
}
130127
}

src/Metadata/Extractor/XmlExtractor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ protected function extractPath(string $path)
4747
'iri' => $this->phpize($resource, 'iri', 'string'),
4848
'itemOperations' => $this->getOperations($resource, 'itemOperation'),
4949
'collectionOperations' => $this->getOperations($resource, 'collectionOperation'),
50+
'subresourceOperations' => $this->getOperations($resource, 'subresourceOperation'),
5051
'attributes' => $this->getAttributes($resource, 'attribute') ?: null,
5152
'properties' => $this->getProperties($resource) ?: null,
5253
];

src/Metadata/Extractor/YamlExtractor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private function extractResources(array $resourcesYaml, string $path)
6767
'iri' => $this->phpize($resourceYaml, 'iri', 'string'),
6868
'itemOperations' => $resourceYaml['itemOperations'] ?? null,
6969
'collectionOperations' => $resourceYaml['collectionOperations'] ?? null,
70+
'subresourceOperations' => $resourceYaml['subresourceOperations'] ?? null,
7071
'attributes' => $resourceYaml['attributes'] ?? null,
7172
];
7273

src/Metadata/Resource/Factory/AnnotationResourceMetadataFactory.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,13 @@ private function createMetadata(ApiResource $annotation, ResourceMetadata $paren
9090
$annotation->iri,
9191
$annotation->itemOperations,
9292
$annotation->collectionOperations,
93-
$annotation->attributes
93+
$annotation->attributes,
94+
$annotation->subresourceOperations
9495
);
9596
}
9697

9798
$resourceMetadata = $parentResourceMetadata;
98-
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'attributes'] as $property) {
99+
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'attributes'] as $property) {
99100
$resourceMetadata = $this->createWith($resourceMetadata, $property, $annotation->$property);
100101
}
101102

src/Metadata/Resource/Factory/ExtractorResourceMetadataFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private function handleNotFound(ResourceMetadata $parentPropertyMetadata = null,
8484
*/
8585
private function update(ResourceMetadata $resourceMetadata, array $metadata): ResourceMetadata
8686
{
87-
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'attributes'] as $property) {
87+
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'attributes'] as $property) {
8888
if (null === $metadata[$property] || null !== $resourceMetadata->{'get'.ucfirst($property)}()) {
8989
continue;
9090
}

src/Metadata/Resource/ResourceMetadata.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ final class ResourceMetadata
2525
private $iri;
2626
private $itemOperations;
2727
private $collectionOperations;
28+
private $subresourceOperations;
2829
private $attributes;
2930

30-
public function __construct(string $shortName = null, string $description = null, string $iri = null, array $itemOperations = null, array $collectionOperations = null, array $attributes = null)
31+
public function __construct(string $shortName = null, string $description = null, string $iri = null, array $itemOperations = null, array $collectionOperations = null, array $attributes = null, array $subresourceOperations = null)
3132
{
3233
$this->shortName = $shortName;
3334
$this->description = $description;
3435
$this->iri = $iri;
3536
$this->itemOperations = $itemOperations;
3637
$this->collectionOperations = $collectionOperations;
38+
$this->subresourceOperations = $subresourceOperations;
3739
$this->attributes = $attributes;
3840
}
3941

@@ -162,6 +164,31 @@ public function withCollectionOperations(array $collectionOperations): self
162164
return $metadata;
163165
}
164166

167+
/**
168+
* Gets subresource operations.
169+
*
170+
* @return array|null
171+
*/
172+
public function getSubresourceOperations()
173+
{
174+
return $this->subresourceOperations;
175+
}
176+
177+
/**
178+
* Returns a new instance with the given subresource operations.
179+
*
180+
* @param array $subresourceOperations
181+
*
182+
* @return self
183+
*/
184+
public function withSubresourceOperations(array $subresourceOperations): self
185+
{
186+
$metadata = clone $this;
187+
$metadata->subresourceOperations = $subresourceOperations;
188+
189+
return $metadata;
190+
}
191+
165192
/**
166193
* Gets a collection operation attribute, optionally fallback to a resource attribute.
167194
*
@@ -192,6 +219,21 @@ public function getItemOperationAttribute(string $operationName = null, string $
192219
return $this->getOperationAttribute($this->itemOperations, $operationName, $key, $defaultValue, $resourceFallback);
193220
}
194221

222+
/**
223+
* Gets a subresource operation attribute, optionally fallback to a resource attribute.
224+
*
225+
* @param string|null $operationName
226+
* @param string $key
227+
* @param mixed $defaultValue
228+
* @param bool $resourceFallback
229+
*
230+
* @return mixed
231+
*/
232+
public function getSubresourceOperationAttribute(string $operationName = null, string $key, $defaultValue = null, bool $resourceFallback = false)
233+
{
234+
return $this->getOperationAttribute($this->subresourceOperations, $operationName, $key, $defaultValue, $resourceFallback);
235+
}
236+
195237
/**
196238
* Gets an operation attribute, optionally fallback to a resource attribute.
197239
*

src/Metadata/schema/metadata.xsd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<xsd:element name="property" minOccurs="0" maxOccurs="unbounded" type="property"/>
2323
<xsd:element name="itemOperations" minOccurs="0" maxOccurs="unbounded" type="itemOperations"/>
2424
<xsd:element name="collectionOperations" minOccurs="0" maxOccurs="unbounded" type="collectionOperations"/>
25+
<xsd:element name="subresourceOperations" minOccurs="0" maxOccurs="unbounded" type="subresourceOperations"/>
2526
</xsd:sequence>
2627

2728
<xsd:attribute type="xsd:string" name="class" use="required"/>
@@ -56,6 +57,19 @@
5657
<xsd:attribute type="xsd:string" name="name"/>
5758
</xsd:complexType>
5859

60+
<xsd:complexType name="subresourceOperations">
61+
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
62+
<xsd:element name="subresourceOperation" minOccurs="0" maxOccurs="unbounded" type="subresourceOperation"/>
63+
</xsd:sequence>
64+
</xsd:complexType>
65+
66+
<xsd:complexType name="subresourceOperation">
67+
<xsd:sequence minOccurs="1" maxOccurs="unbounded">
68+
<xsd:element name="attribute" minOccurs="1" maxOccurs="unbounded" type="attribute"/>
69+
</xsd:sequence>
70+
<xsd:attribute type="xsd:string" name="name"/>
71+
</xsd:complexType>
72+
5973
<xsd:complexType name="attribute" mixed="true">
6074
<xsd:choice maxOccurs="unbounded">
6175
<xsd:element name="attribute" type="attribute" minOccurs="0" maxOccurs="unbounded" />

src/Operation/Factory/SubresourceOperationFactory.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
102102
self::SUBRESOURCE_SUFFIX
103103
);
104104

105-
$collectionOperation = $rootResourceMetadata->getCollectionOperations()[$operation['operation_name']] ?? [];
105+
$subresourceOperation = $rootResourceMetadata->getSubresourceOperations()[$operation['operation_name']] ?? [];
106106

107107
$operation['route_name'] = sprintf(
108108
'%s%s_%s',
@@ -111,7 +111,7 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
111111
$operation['operation_name']
112112
);
113113

114-
$operation['path'] = $collectionOperation['path'] ?? sprintf(
114+
$operation['path'] = $subresourceOperation['path'] ?? sprintf(
115115
'/%s/{id}/%s%s',
116116
$this->pathSegmentNameGenerator->getSegmentName($rootShortname, true),
117117
$this->pathSegmentNameGenerator->getSegmentName($operation['property'], $operation['collection']),
@@ -134,10 +134,10 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
134134
$operation['shortNames'][] = $resourceMetadata->getShortName();
135135
}
136136

137-
$collectionOperation = $rootResourceMetadata->getCollectionOperations()[$operation['operation_name']] ?? [];
137+
$subresourceOperation = $rootResourceMetadata->getSubresourceOperations()[$operation['operation_name']] ?? [];
138138

139-
if (isset($collectionOperation['path'])) {
140-
$operation['path'] = $collectionOperation['path'];
139+
if (isset($subresourceOperation['path'])) {
140+
$operation['path'] = $subresourceOperation['path'];
141141
} else {
142142
$operation['path'] = str_replace(self::FORMAT_SUFFIX, '', $parentOperation['path']);
143143
if ($parentOperation['collection']) {
@@ -149,7 +149,7 @@ private function computeSubresourceOperations(string $resourceClass, array &$tre
149149
}
150150

151151
foreach (self::ROUTE_OPTIONS as $routeOption => $defaultValue) {
152-
$operation[$routeOption] = $collectionOperation[$routeOption] ?? $defaultValue;
152+
$operation[$routeOption] = $subresourceOperation[$routeOption] ?? $defaultValue;
153153
}
154154

155155
$tree[$operation['route_name']] = $operation;

tests/Fixtures/FileConfigurations/resources.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
<attribute name="path">the/collection/path</attribute>
2626
</collectionOperation>
2727
</collectionOperations>
28+
<subresourceOperations>
29+
<subresourceOperation name="my_collection_subresource">
30+
<attribute name="path">the/subresource/path</attribute>
31+
</subresourceOperation>
32+
</subresourceOperations>
2833
<attribute name="normalization_context">
2934
<attribute name="groups">
3035
<attribute>default</attribute>

tests/Fixtures/FileConfigurations/resources.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ resources:
1212
my_collection_op:
1313
method: 'POST'
1414
path: 'the/collection/path'
15+
subresourceOperations:
16+
my_collection_subresource:
17+
path: 'the/subresource/path'
1518
attributes:
1619
normalization_context:
1720
groups: ['default']

tests/Fixtures/FileConfigurations/single_resource.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
my_collection_op:
1111
method: 'POST'
1212
path: 'the/collection/path'
13+
subresourceOperations:
14+
my_collection_subresource:
15+
path: 'the/subresource/path'
1316
attributes:
1417
normalization_context:
1518
groups: ['default']

tests/Metadata/Resource/Factory/AnnotationResourceMetadataFactoryTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function testCreate(ProphecyInterface $reader, ProphecyInterface $decorat
4242
$this->assertEquals('http://example.com', $metadata->getIri());
4343
$this->assertEquals(['foo' => ['bar' => true]], $metadata->getItemOperations());
4444
$this->assertEquals(['baz' => ['tab' => false]], $metadata->getCollectionOperations());
45+
$this->assertEquals(['sub' => ['bus' => false]], $metadata->getSubresourceOperations());
4546
$this->assertEquals(['a' => 1], $metadata->getAttributes());
4647
}
4748

@@ -53,6 +54,7 @@ public function getCreateDependencies()
5354
$annotation->iri = 'http://example.com';
5455
$annotation->itemOperations = ['foo' => ['bar' => true]];
5556
$annotation->collectionOperations = ['baz' => ['tab' => false]];
57+
$annotation->subresourceOperations = ['sub' => ['bus' => false]];
5658
$annotation->attributes = ['a' => 1];
5759

5860
$reader = $this->prophesize(Reader::class);

tests/Metadata/Resource/Factory/FileConfigurationMetadataFactoryProvider.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public function resourceMetadataProvider()
3838
'collectionOperations' => [
3939
'my_collection_op' => ['method' => 'POST', 'path' => 'the/collection/path'],
4040
],
41+
'subresourceOperations' => [
42+
'my_collection_subresource' => ['path' => 'the/subresource/path'],
43+
],
4144
'iri' => 'someirischema',
4245
'attributes' => [
4346
'normalization_context' => [
@@ -53,7 +56,7 @@ public function resourceMetadataProvider()
5356
],
5457
];
5558

56-
foreach (['shortName', 'description', 'itemOperations', 'collectionOperations', 'iri', 'attributes'] as $property) {
59+
foreach (['shortName', 'description', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'iri', 'attributes'] as $property) {
5760
$wither = 'with'.ucfirst($property);
5861
$resourceMetadata = $resourceMetadata->$wither($metadata[$property]);
5962
}

tests/Metadata/Resource/ResourceMetadataTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class ResourceMetadataTest extends TestCase
2323
{
2424
public function testValueObject()
2525
{
26-
$metadata = new ResourceMetadata('shortName', 'desc', 'http://example.com/foo', ['iop1' => ['foo' => 'a'], 'iop2' => ['bar' => 'b']], ['cop1' => ['foo' => 'c'], 'cop2' => ['bar' => 'd']], ['baz' => 'bar']);
26+
$metadata = new ResourceMetadata('shortName', 'desc', 'http://example.com/foo', ['iop1' => ['foo' => 'a'], 'iop2' => ['bar' => 'b']], ['cop1' => ['foo' => 'c'], 'cop2' => ['bar' => 'd']], ['baz' => 'bar'], ['sop1' => ['sub' => 'bus']]);
2727
$this->assertEquals('shortName', $metadata->getShortName());
2828
$this->assertEquals('desc', $metadata->getDescription());
2929
$this->assertEquals('http://example.com/foo', $metadata->getIri());
@@ -42,6 +42,10 @@ public function testValueObject()
4242
$this->assertEquals(['baz' => 'bar'], $metadata->getAttributes());
4343
$this->assertEquals('bar', $metadata->getAttribute('baz'));
4444
$this->assertEquals('z', $metadata->getAttribute('notExist', 'z'));
45+
$this->assertEquals(['sop1' => ['sub' => 'bus']], $metadata->getSubresourceOperations());
46+
$this->assertEquals('bus', $metadata->getSubresourceOperationAttribute('sop1', 'sub'));
47+
$this->assertEquals('sub', $metadata->getSubresourceOperationAttribute('sop1', 'bus', 'sub', false));
48+
$this->assertEquals('bar', $metadata->getSubresourceOperationAttribute('sop1', 'baz', 'sub', true));
4549

4650
$newMetadata = $metadata->withShortName('name');
4751
$this->assertNotSame($metadata, $newMetadata);

tests/Operation/Factory/SubresourceOperationFactoryTest.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,13 @@ public function testCreateByOverriding()
146146
{
147147
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
148148
$resourceMetadataFactoryProphecy->create(RelatedDummyEntity::class)->shouldBeCalled()->willReturn(new ResourceMetadata('relatedDummyEntity'));
149-
$resourceMetadataFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn((new ResourceMetadata('dummyEntity'))->withCollectionOperations([
149+
$resourceMetadataFactoryProphecy->create(DummyEntity::class)->shouldBeCalled()->willReturn((new ResourceMetadata('dummyEntity'))->withSubresourceOperations([
150150
'subcollections_get_subresource' => [
151151
'path' => '/dummy_entities/{id}/foobars',
152152
],
153+
'subcollections_another_subresource_get_subresource' => [
154+
'path' => '/dummy_entities/{id}/foobars/{subcollection}/another_foobar.{_format}',
155+
],
153156
]));
154157

155158
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
@@ -237,7 +240,7 @@ public function testCreateByOverriding()
237240
['subcollection', RelatedDummyEntity::class, true],
238241
],
239242
'route_name' => 'api_dummy_entities_subcollections_another_subresource_get_subresource',
240-
'path' => '/dummy_entities/{id}/foobars/{subcollection}/another_subresource.{_format}',
243+
'path' => '/dummy_entities/{id}/foobars/{subcollection}/another_foobar.{_format}',
241244
'operation_name' => 'subcollections_another_subresource_get_subresource',
242245
] + SubresourceOperationFactory::ROUTE_OPTIONS,
243246
'api_dummy_entities_subcollections_another_subresource_subresource_get_subresource' => [
@@ -251,7 +254,7 @@ public function testCreateByOverriding()
251254
['anotherSubresource', DummyEntity::class, false],
252255
],
253256
'route_name' => 'api_dummy_entities_subcollections_another_subresource_subresource_get_subresource',
254-
'path' => '/dummy_entities/{id}/foobars/{subcollection}/another_subresource/subresource.{_format}',
257+
'path' => '/dummy_entities/{id}/foobars/{subcollection}/another_foobar/subresource.{_format}',
255258
'operation_name' => 'subcollections_another_subresource_subresource_get_subresource',
256259
] + SubresourceOperationFactory::ROUTE_OPTIONS,
257260
], $subresourceOperationFactory->create(DummyEntity::class));

0 commit comments

Comments
 (0)