Skip to content

Commit 0b3699e

Browse files
committed
Merge 3.4
2 parents 4f65ef2 + 8bdf044 commit 0b3699e

File tree

8 files changed

+244
-60
lines changed

8 files changed

+244
-60
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ Notes:
152152

153153
* [0d5f35683](https://github.com/api-platform/core/commit/0d5f356839eb6aa9f536044abe4affa736553e76) feat(laravel): laravel component (#5882)
154154

155+
## v3.4.5
156+
157+
### Bug fixes
158+
159+
* [fc8fa00a1](https://github.com/api-platform/core/commit/fc8fa00a19320b65547a60537261959c11f8e6a8) fix(hydra): iri template when using query parameter (#6742)
160+
155161
## v3.4.4
156162

157163
### Bug fixes

src/Hydra/Serializer/CollectionFiltersNormalizer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ private function getSearch(string $resourceClass, array $parts, array $filters,
153153
continue;
154154
}
155155

156-
if (!($property = $parameter->getProperty()) && ($filterId = $parameter->getFilter()) && ($filter = $this->getFilter($filterId))) {
156+
if (($filterId = $parameter->getFilter()) && ($filter = $this->getFilter($filterId))) {
157157
foreach ($filter->getDescription($resourceClass) as $variable => $description) {
158158
// This is a practice induced by PHP and is not necessary when implementing URI template
159159
if (str_ends_with((string) $variable, '[]')) {
@@ -174,7 +174,7 @@ private function getSearch(string $resourceClass, array $parts, array $filters,
174174
continue;
175175
}
176176

177-
if (!$property) {
177+
if (!($property = $parameter->getProperty())) {
178178
continue;
179179
}
180180

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,12 @@ private function addFilterMetadata(Parameter $parameter): Parameter
155155
return $parameter;
156156
}
157157

158-
if (null === $parameter->getSchema() && $filter instanceof JsonSchemaFilterInterface) {
159-
if ($schema = $filter->getSchema($parameter)) {
160-
$parameter = $parameter->withSchema($schema);
161-
}
158+
if (null === $parameter->getSchema() && $filter instanceof JsonSchemaFilterInterface && $schema = $filter->getSchema($parameter)) {
159+
$parameter = $parameter->withSchema($schema);
162160
}
163161

164-
if (null === $parameter->getOpenApi() && $filter instanceof OpenApiParameterFilterInterface) {
165-
if ($openApiParameter = $filter->getOpenApiParameters($parameter)) {
166-
$parameter = $parameter->withOpenApi($openApiParameter);
167-
}
162+
if (null === $parameter->getOpenApi() && $filter instanceof OpenApiParameterFilterInterface && ($openApiParameter = $filter->getOpenApiParameters($parameter)) && $openApiParameter instanceof OpenApiParameter) {
163+
$parameter = $parameter->withOpenApi($openApiParameter);
168164
}
169165

170166
return $parameter;
@@ -216,10 +212,6 @@ private function setDefaults(string $key, Parameter $parameter, string $resource
216212
$parameter = $parameter->withRequired($required);
217213
}
218214

219-
if (null === $parameter->getOpenApi() && ($openApi = $description[$key]['openapi'] ?? null) && $openApi instanceof OpenApiParameter) {
220-
$parameter = $parameter->withOpenApi($openApi);
221-
}
222-
223215
return $this->addFilterMetadata($parameter);
224216
}
225217

src/OpenApi/Factory/OpenApiFactory.php

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,26 @@ private function collectPaths(ApiResource $resource, ResourceMetadataCollection
261261
}
262262
}
263263

264+
$entityClass = $this->getFilterClass($operation);
264265
$openapiParameters = $openapiOperation->getParameters();
265266
foreach ($operation->getParameters() ?? [] as $key => $p) {
266267
if (false === $p->getOpenApi()) {
267268
continue;
268269
}
269270

271+
if (($f = $p->getFilter()) && \is_string($f) && $this->filterLocator->has($f)) {
272+
$filter = $this->filterLocator->get($f);
273+
foreach ($filter->getDescription($entityClass) as $name => $description) {
274+
if ($prop = $p->getProperty()) {
275+
$name = str_replace($prop, $key, $name);
276+
}
277+
278+
$openapiParameters[] = $this->getFilterParameter($name, $description, $operation->getShortName(), $f);
279+
}
280+
281+
continue;
282+
}
283+
270284
$in = $p instanceof HeaderParameterInterface ? 'header' : 'query';
271285
$defaultParameter = new Parameter($key, $in, $p->getDescription() ?? "$resourceShortName $key", $p->getRequired() ?? false, false, false, $p->getSchema() ?? ['type' => 'string']);
272286

@@ -557,57 +571,102 @@ private function getLinks(ResourceMetadataCollection $resourceMetadataCollection
557571
private function getFiltersParameters(CollectionOperationInterface|HttpOperation $operation): array
558572
{
559573
$parameters = [];
560-
561574
$resourceFilters = $operation->getFilters();
575+
$entityClass = $this->getFilterClass($operation);
576+
562577
foreach ($resourceFilters ?? [] as $filterId) {
563578
if (!$this->filterLocator->has($filterId)) {
564579
continue;
565580
}
566581

567582
$filter = $this->filterLocator->get($filterId);
568-
$entityClass = $operation->getClass();
569-
if ($options = $operation->getStateOptions()) {
570-
if ($options instanceof DoctrineOptions && $options->getEntityClass()) {
571-
$entityClass = $options->getEntityClass();
572-
}
583+
foreach ($filter->getDescription($entityClass) as $name => $description) {
584+
$parameters[] = $this->getFilterParameter($name, $description, $operation->getShortName(), $filterId);
585+
}
586+
}
573587

574-
if ($options instanceof DoctrineODMOptions && $options->getDocumentClass()) {
575-
$entityClass = $options->getDocumentClass();
576-
}
588+
return $parameters;
589+
}
590+
591+
private function getFilterClass(HttpOperation $operation): ?string
592+
{
593+
$entityClass = $operation->getClass();
594+
if ($options = $operation->getStateOptions()) {
595+
if ($options instanceof DoctrineOptions && $options->getEntityClass()) {
596+
return $options->getEntityClass();
597+
}
598+
599+
if ($options instanceof DoctrineODMOptions && $options->getDocumentClass()) {
600+
return $options->getDocumentClass();
577601
}
602+
}
578603

579-
foreach ($filter->getDescription($entityClass) as $name => $data) {
580-
$schema = $data['schema'] ?? [];
604+
return $entityClass;
605+
}
581606

582-
if (isset($data['type']) && \in_array($data['type'] ?? null, Type::$builtinTypes, true) && !isset($schema['type'])) {
583-
$schema += $this->getType(new Type($data['type'], false, null, $data['is_collection'] ?? false));
584-
}
607+
private function getFilterParameter(string $name, array $description, string $shortName, string $filter): Parameter
608+
{
609+
if (isset($description['swagger'])) {
610+
trigger_deprecation('api-platform/core', '4.0', \sprintf('Using the "swagger" field of the %s::getDescription() (%s) is deprecated.', $filter, $shortName));
611+
}
585612

586-
if (!isset($schema['type'])) {
587-
$schema['type'] = 'string';
588-
}
613+
if (!isset($description['openapi']) || $description['openapi'] instanceof Parameter) {
614+
$schema = $description['schema'] ?? [];
615+
616+
if (isset($description['type']) && \in_array($description['type'], Type::$builtinTypes, true) && !isset($schema['type'])) {
617+
$schema += $this->jsonSchemaTypeFactory ? $this->jsonSchemaTypeFactory->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false)) : $this->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false));
618+
}
589619

590-
$style = 'array' === ($schema['type'] ?? null) && \in_array(
591-
$data['type'],
592-
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
593-
true
594-
) ? 'deepObject' : 'form';
620+
if (!isset($schema['type'])) {
621+
$schema['type'] = 'string';
622+
}
595623

596-
$parameter = isset($data['openapi']) && $data['openapi'] instanceof Parameter ? $data['openapi'] : new Parameter(in: 'query', name: $name, style: $style, explode: $data['is_collection'] ?? false);
624+
$style = 'array' === ($schema['type'] ?? null) && \in_array(
625+
$description['type'],
626+
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
627+
true
628+
) ? 'deepObject' : 'form';
597629

598-
if ('' === $parameter->getDescription() && ($description = $data['description'] ?? '')) {
599-
$parameter = $parameter->withDescription($description);
600-
}
630+
$parameter = isset($description['openapi']) && $description['openapi'] instanceof Parameter ? $description['openapi'] : new Parameter(in: 'query', name: $name, style: $style, explode: $description['is_collection'] ?? false);
601631

602-
if (false === $parameter->getRequired() && false !== ($required = $data['required'] ?? false)) {
603-
$parameter = $parameter->withRequired($required);
604-
}
632+
if ('' === $parameter->getDescription() && ($str = $description['description'] ?? '')) {
633+
$parameter = $parameter->withDescription($str);
634+
}
605635

606-
$parameters[] = $parameter->withSchema($schema);
636+
if (false === $parameter->getRequired() && false !== ($required = $description['required'] ?? false)) {
637+
$parameter = $parameter->withRequired($required);
607638
}
639+
640+
return $parameter->withSchema($schema);
608641
}
609642

610-
return $parameters;
643+
trigger_deprecation('api-platform/core', '4.0', \sprintf('Not using "%s" on the "openapi" field of the %s::getDescription() (%s) is deprecated.', Parameter::class, $filter, $shortName));
644+
if ($this->jsonSchemaTypeFactory) {
645+
$schema = $description['schema'] ?? (\in_array($description['type'], Type::$builtinTypes, true) ? $this->jsonSchemaTypeFactory->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false), 'openapi') : ['type' => 'string']);
646+
} else {
647+
$schema = $description['schema'] ?? (\in_array($description['type'], Type::$builtinTypes, true) ? $this->getType(new Type($description['type'], false, null, $description['is_collection'] ?? false)) : ['type' => 'string']);
648+
}
649+
650+
return new Parameter(
651+
$name,
652+
'query',
653+
$description['description'] ?? '',
654+
$description['required'] ?? false,
655+
$description['openapi']['deprecated'] ?? false,
656+
$description['openapi']['allowEmptyValue'] ?? true,
657+
$schema,
658+
'array' === $schema['type'] && \in_array(
659+
$description['type'],
660+
[Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT],
661+
true
662+
) ? 'deepObject' : 'form',
663+
$description['openapi']['explode'] ?? ('array' === $schema['type']),
664+
$description['openapi']['allowReserved'] ?? false,
665+
$description['openapi']['example'] ?? null,
666+
isset(
667+
$description['openapi']['examples']
668+
) ? new \ArrayObject($description['openapi']['examples']) : null
669+
);
611670
}
612671

613672
private function getPaginationParameters(CollectionOperationInterface|HttpOperation $operation): array

src/OpenApi/Tests/Factory/OpenApiFactoryTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,8 @@ public function testInvoke(): void
879879
'type' => 'string',
880880
'enum' => ['asc', 'desc'],
881881
]),
882-
]
882+
],
883+
deprecated: false
883884
), $filteredPath->getGet());
884885

885886
$paginatedPath = $paths->getPath('/paginated');
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
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\Tests\Fixtures\TestBundle\ApiResource;
15+
16+
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
17+
use ApiPlatform\Doctrine\Orm\State\CollectionProvider;
18+
use ApiPlatform\Doctrine\Orm\State\Options;
19+
use ApiPlatform\Metadata\ApiFilter;
20+
use ApiPlatform\Metadata\GetCollection;
21+
use ApiPlatform\Metadata\QueryParameter;
22+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\FilterWithStateOptionsEntity;
23+
24+
#[GetCollection(
25+
uriTemplate: 'filter_with_state_options',
26+
stateOptions: new Options(entityClass: FilterWithStateOptionsEntity::class),
27+
parameters: ['date' => new QueryParameter(filter: 'filter_with_state_options_date', property: 'dummyDate')],
28+
provider: CollectionProvider::class
29+
)]
30+
#[ApiFilter(DateFilter::class, alias: 'filter_with_state_options_date', properties: ['dummyDate' => DateFilter::EXCLUDE_NULL])]
31+
final class FilterWithStateOptions
32+
{
33+
public function __construct(public readonly string $id, public readonly \DateTimeImmutable $dummyDate, public readonly string $name)
34+
{
35+
}
36+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
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\Tests\Fixtures\TestBundle\Entity;
15+
16+
use Doctrine\ORM\Mapping as ORM;
17+
18+
#[ORM\Entity]
19+
class FilterWithStateOptionsEntity
20+
{
21+
public function __construct(
22+
#[ORM\Column(type: 'integer')]
23+
#[ORM\Id]
24+
#[ORM\GeneratedValue(strategy: 'AUTO')]
25+
public ?int $id = null,
26+
#[ORM\Column(type: 'date_immutable', nullable: true)]
27+
public ?\DateTimeImmutable $dummyDate = null,
28+
#[ORM\Column(type: 'string', nullable: true)]
29+
public ?string $name = null,
30+
) {
31+
}
32+
}

0 commit comments

Comments
 (0)