Skip to content

Commit ed1ef25

Browse files
authored
fix(state): specify :property parameter properties (#7110)
1 parent 1284cf8 commit ed1ef25

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

src/Metadata/Parameter.php

+15
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ abstract class Parameter
2626
* @param (array<string, mixed>&array{type?: string, default?: string})|null $schema
2727
* @param array<string, mixed> $extraProperties
2828
* @param ParameterProviderInterface|callable|string|null $provider
29+
* @param list<string> $properties a list of properties this parameter applies to (works with the :property placeholder)
2930
* @param FilterInterface|string|null $filter
3031
* @param mixed $constraints an array of Symfony constraints, or an array of Laravel rules
3132
*/
@@ -37,6 +38,7 @@ public function __construct(
3738
protected mixed $filter = null,
3839
protected ?string $property = null,
3940
protected ?string $description = null,
41+
protected ?array $properties = null,
4042
protected ?bool $required = null,
4143
protected ?int $priority = null,
4244
protected ?false $hydra = null,
@@ -281,4 +283,17 @@ public function withExtraProperties(array $extraProperties): static
281283

282284
return $self;
283285
}
286+
287+
public function getProperties(): ?array
288+
{
289+
return $this->properties;
290+
}
291+
292+
public function withProperties(?array $properties): self
293+
{
294+
$self = clone $this;
295+
$self->properties = $properties;
296+
297+
return $self;
298+
}
284299
}

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

+9-7
Original file line numberDiff line numberDiff line change
@@ -92,32 +92,34 @@ public function create(string $resourceClass): ResourceMetadataCollection
9292
/**
9393
* @return array{propertyNames: string[], properties: array<string, ApiProperty>}
9494
*/
95-
private function getProperties(string $resourceClass): array
95+
private function getProperties(string $resourceClass, ?Parameter $parameter = null): array
9696
{
97-
if (isset($this->localPropertyCache[$resourceClass])) {
98-
return $this->localPropertyCache[$resourceClass];
97+
$k = $resourceClass.($parameter?->getProperties() ? ($parameter->getKey() ?? '') : '');
98+
if (isset($this->localPropertyCache[$k])) {
99+
return $this->localPropertyCache[$k];
99100
}
100101

101102
$propertyNames = [];
102103
$properties = [];
103-
foreach ($this->propertyNameCollectionFactory->create($resourceClass) as $property) {
104+
foreach ($parameter?->getProperties() ?? $this->propertyNameCollectionFactory->create($resourceClass) as $property) {
104105
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $property);
105106
if ($propertyMetadata->isReadable()) {
106107
$propertyNames[] = $property;
107108
$properties[$property] = $propertyMetadata;
108109
}
109110
}
110111

111-
$this->localPropertyCache = [$resourceClass => ['propertyNames' => $propertyNames, 'properties' => $properties]];
112+
$this->localPropertyCache[$k] = ['propertyNames' => $propertyNames, 'properties' => $properties];
112113

113-
return $this->localPropertyCache[$resourceClass];
114+
return $this->localPropertyCache[$k];
114115
}
115116

116117
private function getDefaultParameters(Operation $operation, string $resourceClass, int &$internalPriority): Parameters
117118
{
118-
['propertyNames' => $propertyNames, 'properties' => $properties] = $this->getProperties($resourceClass);
119+
$propertyNames = $properties = [];
119120
$parameters = $operation->getParameters() ?? new Parameters();
120121
foreach ($parameters as $key => $parameter) {
122+
['propertyNames' => $propertyNames, 'properties' => $properties] = $this->getProperties($resourceClass, $parameter);
121123
if (null === $parameter->getProvider() && (($f = $parameter->getFilter()) && $f instanceof ParameterProviderFilterInterface)) {
122124
$parameters->add($key, $parameter->withProvider($f->getParameterProvider()));
123125
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Metadata\Tests\Fixtures\ApiResource;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\GetCollection;
18+
use ApiPlatform\Metadata\QueryParameter;
19+
20+
#[
21+
ApiResource(operations: [
22+
new GetCollection(name: 'collection', parameters: [':property' => new QueryParameter(properties: ['name'])]),
23+
])
24+
]
25+
class WithLimitedPropertyParameter
26+
{
27+
public $id;
28+
public $name;
29+
public $description;
30+
}

src/Metadata/Tests/Resource/Factory/ParameterResourceMetadataCollectionFactoryTest.php

+40-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ApiPlatform\Metadata\QueryParameter;
2323
use ApiPlatform\Metadata\Resource\Factory\AttributesResourceMetadataCollectionFactory;
2424
use ApiPlatform\Metadata\Resource\Factory\ParameterResourceMetadataCollectionFactory;
25+
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\WithLimitedPropertyParameter;
2526
use ApiPlatform\Metadata\Tests\Fixtures\ApiResource\WithParameter;
2627
use ApiPlatform\OpenApi\Model\Parameter;
2728
use PHPUnit\Framework\TestCase;
@@ -52,7 +53,8 @@ public function getDescription(string $resourceClass): array
5253
'property' => 'everywhere',
5354
'type' => 'string',
5455
'required' => false,
55-
'openapi' => ['allowEmptyValue' => true]],
56+
'openapi' => ['allowEmptyValue' => true],
57+
],
5658
];
5759
}
5860
});
@@ -88,4 +90,41 @@ public function testParameterFactoryNoFilter(): void
8890
$operation = $parameter->create(WithParameter::class)->getOperation('collection');
8991
$this->assertInstanceOf(Parameters::class, $parameters = $operation->getParameters());
9092
}
93+
94+
public function testParameterFactoryWithLimitedProperties(): void
95+
{
96+
$nameCollection = $this->createMock(PropertyNameCollectionFactoryInterface::class);
97+
$nameCollection->expects($this->never())->method('create');
98+
99+
$propertyMetadata = $this->createStub(PropertyMetadataFactoryInterface::class);
100+
$propertyMetadata->method('create')->willReturnMap([
101+
[WithLimitedPropertyParameter::class, 'name', [], new ApiProperty(readable: true)],
102+
]);
103+
104+
$filterLocator = $this->createStub(ContainerInterface::class);
105+
$filterLocator->method('has')->willReturn(false);
106+
107+
$attributesFactory = new AttributesResourceMetadataCollectionFactory();
108+
$parameterFactory = new ParameterResourceMetadataCollectionFactory(
109+
$nameCollection,
110+
$propertyMetadata,
111+
$attributesFactory,
112+
$filterLocator
113+
);
114+
115+
$resourceCollection = $parameterFactory->create(WithLimitedPropertyParameter::class);
116+
$operation = $resourceCollection->getOperation('collection');
117+
$parameters = $operation->getParameters();
118+
119+
$this->assertInstanceOf(Parameters::class, $parameters);
120+
$this->assertCount(1, $parameters);
121+
$this->assertTrue($parameters->has('name'));
122+
$this->assertFalse($parameters->has('id'));
123+
$this->assertFalse($parameters->has('description'));
124+
125+
$param = $parameters->get('name');
126+
$this->assertInstanceOf(QueryParameter::class, $param);
127+
$this->assertSame('name', $param->getKey());
128+
$this->assertSame(['name'], $param->getProperties());
129+
}
91130
}

0 commit comments

Comments
 (0)