Skip to content

Commit 5e45261

Browse files
soyukaaegypiusjsampedro77
authored
Merge 4.1 (#7499)
Co-authored-by: Nicolas LAURENT <aegypius@users.noreply.github.com> Co-authored-by: Javier Sampedro <jsampedro77@gmail.com> fix(laravel): serializer attributes on Eloquent methods (#7416) fixes #7289 fixes #7338 fix(validator): custom message was not translated (#7424) fixes #7336 fix(serializer): resilient denormalizeRelation capability (#7474) fix(doctrine): properly set properties according to interface (#7487) fix(graphql): stateOptions to get filter class (#7485)
2 parents 9416083 + 4cf4792 commit 5e45261

27 files changed

+760
-107
lines changed

features/filter/filter_validation.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Feature: Validate filters based upon filter description
1616
Scenario: Required filter should throw an error if not set
1717
When I am on "/filter_validators"
1818
Then the response status code should be 422
19-
And the JSON node "detail" should be equal to 'required: This value should not be blank.\nrequired-allow-empty: The parameter "required-allow-empty" is required.'
19+
And the JSON node "detail" should be equal to 'required: This value should not be blank.\nrequired-allow-empty: This value should not be null.'
2020

2121
Scenario: Required filter should not throw an error if set
2222
When I am on "/array_filter_validators?arrayRequired[]=foo&indexedArrayRequired[foo]=foo"

features/http_cache/headers.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ Feature: Default values of HTTP cache headers
99
Then the response status code should be 200
1010
And the header "Etag" should be equal to '"032297ac74d75a50"'
1111
And the header "Cache-Control" should be equal to "max-age=60, public, s-maxage=3600"
12-
And the header "Vary" should be equal to "Accept, Cookie"
12+
And the header "Vary" should be equal to "Accept, Cookie, Accept-Language"

src/Doctrine/Orm/Extension/ParameterExtension.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use ApiPlatform\Doctrine\Common\Filter\LoggerAwareInterface;
1717
use ApiPlatform\Doctrine\Common\Filter\ManagerRegistryAwareInterface;
18+
use ApiPlatform\Doctrine\Common\Filter\PropertyAwareFilterInterface;
1819
use ApiPlatform\Doctrine\Common\ParameterValueExtractorTrait;
1920
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
2021
use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
@@ -75,19 +76,21 @@ private function applyFilter(QueryBuilder $queryBuilder, QueryNameGeneratorInter
7576
$filter->setLogger($this->logger);
7677
}
7778

78-
if ($filter instanceof AbstractFilter && !$filter->getProperties()) {
79+
if ($filter instanceof PropertyAwareFilterInterface) {
80+
$properties = [];
7981
$propertyKey = $parameter->getProperty() ?? $parameter->getKey();
80-
81-
if (str_contains($propertyKey, ':property')) {
82-
$extraProperties = $parameter->getExtraProperties()['_properties'] ?? [];
83-
foreach (array_keys($extraProperties) as $property) {
84-
$properties[$property] = $parameter->getFilterContext();
82+
if ($filter instanceof AbstractFilter) {
83+
$properties = $filter->getProperties() ?? [];
84+
85+
if (str_contains($propertyKey, ':property')) {
86+
$extraProperties = $parameter->getExtraProperties()['_properties'] ?? [];
87+
foreach (array_keys($extraProperties) as $property) {
88+
$properties[$property] = $parameter->getFilterContext();
89+
}
8590
}
86-
} else {
87-
$properties = [$propertyKey => $parameter->getFilterContext()];
8891
}
8992

90-
$filter->setProperties($properties ?? []);
93+
$filter->setProperties($properties + [$propertyKey => $parameter->getFilterContext()]);
9194
}
9295

9396
$filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation,

src/GraphQl/Type/FieldsBuilder.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use ApiPlatform\Metadata\Util\PropertyInfoToTypeInfoHelper;
3232
use ApiPlatform\Metadata\Util\TypeHelper;
3333
use ApiPlatform\State\Pagination\Pagination;
34+
use ApiPlatform\State\Util\StateOptionsTrait;
3435
use GraphQL\Type\Definition\InputObjectType;
3536
use GraphQL\Type\Definition\ListOfType;
3637
use GraphQL\Type\Definition\NonNull;
@@ -55,6 +56,8 @@
5556
*/
5657
final class FieldsBuilder implements FieldsBuilderEnumInterface
5758
{
59+
use StateOptionsTrait;
60+
5861
private readonly ContextAwareTypeBuilderInterface $typeBuilder;
5962

6063
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly TypesContainerInterface $typesContainer, ContextAwareTypeBuilderInterface $typeBuilder, private readonly TypeConverterInterface $typeConverter, private readonly ResolverFactoryInterface $resolverFactory, private readonly ContainerInterface $filterLocator, private readonly Pagination $pagination, private readonly ?NameConverterInterface $nameConverter, private readonly string $nestingSeparator, private readonly ?InflectorInterface $inflector = new Inflector())
@@ -85,7 +88,7 @@ public function getItemQueryFields(string $resourceClass, Operation $operation,
8588
return [];
8689
}
8790

88-
$fieldName = lcfirst('item_query' === $operation->getName() ? $operation->getShortName() : $operation->getName().$operation->getShortName());
91+
$fieldName = lcfirst('item_query' === $operation->getName() ? ($operation->getShortName() ?? $operation->getName()) : $operation->getName().$operation->getShortName());
8992

9093
if ($fieldConfiguration = $this->getResourceFieldConfiguration(null, $operation->getDescription(), $operation->getDeprecationReason(), Type::nullable(Type::object($resourceClass)), $resourceClass, false, $operation)) {
9194
$args = $this->resolveResourceArgs($configuration['args'] ?? [], $operation);
@@ -606,7 +609,8 @@ private function getFilterArgs(array $args, ?string $resourceClass, string $root
606609
continue;
607610
}
608611

609-
foreach ($this->filterLocator->get($filterId)->getDescription($resourceClass) as $key => $description) {
612+
$entityClass = $this->getStateOptionsClass($resourceOperation, $resourceOperation->getClass());
613+
foreach ($this->filterLocator->get($filterId)->getDescription($entityClass) as $key => $description) {
610614
$filterType = \in_array($description['type'], TypeIdentifier::values(), true) ? Type::builtin($description['type']) : Type::object($description['type']);
611615
if (!($description['required'] ?? false)) {
612616
$filterType = Type::nullable($filterType);

src/Laravel/ApiPlatformProvider.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
use ApiPlatform\Laravel\Eloquent\PropertyAccess\PropertyAccessor as EloquentPropertyAccessor;
9090
use ApiPlatform\Laravel\Eloquent\PropertyInfo\EloquentExtractor;
9191
use ApiPlatform\Laravel\Eloquent\Serializer\EloquentNameConverter;
92+
use ApiPlatform\Laravel\Eloquent\Serializer\Mapping\Loader\AttributeLoader as EloquentAttributeLoader;
93+
use ApiPlatform\Laravel\Eloquent\Serializer\Mapping\Loader\RelationMetadataLoader;
9294
use ApiPlatform\Laravel\Eloquent\Serializer\SerializerContextBuilder as EloquentSerializerContextBuilder;
9395
use ApiPlatform\Laravel\GraphQl\Controller\EntrypointController as GraphQlEntrypointController;
9496
use ApiPlatform\Laravel\GraphQl\Controller\GraphiQlController;
@@ -174,7 +176,6 @@
174176
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
175177
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
176178
use Symfony\Component\Serializer\Mapping\Loader\LoaderChain;
177-
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
178179
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
179180
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
180181
use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter;
@@ -216,7 +217,6 @@ public function register(): void
216217
return new ModelMetadata();
217218
});
218219

219-
$this->app->bind(LoaderInterface::class, AttributeLoader::class);
220220
$this->app->bind(ClassMetadataFactoryInterface::class, ClassMetadataFactory::class);
221221
$this->app->singleton(ClassMetadataFactory::class, function (Application $app) {
222222
/** @var ConfigRepository */
@@ -232,8 +232,8 @@ public function register(): void
232232
$app->make(PropertyNameCollectionFactoryInterface::class),
233233
$nameConverter
234234
),
235-
new AttributeLoader(),
236-
// new RelationMetadataLoader($app->make(ModelMetadata::class)),
235+
new EloquentAttributeLoader(new AttributeLoader()),
236+
new RelationMetadataLoader($app->make(ModelMetadata::class)),
237237
])
238238
);
239239
});
@@ -330,8 +330,8 @@ public function register(): void
330330
);
331331
});
332332

333-
$this->app->bind(PropertyAccessorInterface::class, function () {
334-
return new EloquentPropertyAccessor();
333+
$this->app->bind(PropertyAccessorInterface::class, function (Application $app) {
334+
return new EloquentPropertyAccessor(null, $app->make(ModelMetadata::class));
335335
});
336336

337337
$this->app->bind(NameConverterInterface::class, function (Application $app) {
@@ -467,7 +467,7 @@ public function register(): void
467467
$config = $app['config'];
468468
$defaultContext = $config->get('api-platform.serializer', []);
469469

470-
return new ObjectNormalizer(defaultContext: $defaultContext);
470+
return new ObjectNormalizer($app->make(ClassMetadataFactoryInterface::class), defaultContext: $defaultContext);
471471
});
472472

473473
$this->app->singleton(DateTimeNormalizer::class, function (Application $app) {

0 commit comments

Comments
 (0)