Skip to content

Commit 02faa6a

Browse files
committed
test(laravel): filter with eloquent
1 parent 0943d57 commit 02faa6a

23 files changed

+272
-185
lines changed

src/Laravel/ApiPlatformProvider.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
use ApiPlatform\GraphQl\Type\TypesFactory;
4747
use ApiPlatform\GraphQl\Type\TypesFactoryInterface;
4848
use ApiPlatform\Hydra\JsonSchema\SchemaFactory as HydraSchemaFactory;
49-
use ApiPlatform\Hydra\Serializer\CollectionNormalizer as HydraCollectionNormalizer;
5049
use ApiPlatform\Hydra\Serializer\CollectionFiltersNormalizer as HydraFiltersCollectionNormalizer;
50+
use ApiPlatform\Hydra\Serializer\CollectionNormalizer as HydraCollectionNormalizer;
5151
use ApiPlatform\Hydra\Serializer\DocumentationNormalizer as HydraDocumentationNormalizer;
5252
use ApiPlatform\Hydra\Serializer\EntrypointNormalizer as HydraEntrypointNormalizer;
5353
use ApiPlatform\Hydra\Serializer\PartialCollectionViewNormalizer as HydraPartialCollectionViewNormalizer;
@@ -326,6 +326,7 @@ public function register(): void
326326
new EloquentResourceCollectionMetadataFactory(
327327
new ParameterResourceMetadataCollectionFactory(
328328
$this->app->make(PropertyNameCollectionFactoryInterface::class),
329+
$this->app->make(PropertyMetadataFactoryInterface::class),
329330
new AlternateUriResourceMetadataCollectionFactory(
330331
new FiltersResourceMetadataCollectionFactory(
331332
new FormatsResourceMetadataCollectionFactory(
@@ -339,8 +340,8 @@ public function register(): void
339340
$app->make(PathSegmentNameGeneratorInterface::class),
340341
new NotExposedOperationResourceMetadataCollectionFactory(
341342
$app->make(LinkFactoryInterface::class),
342-
new ConcernsResourceMetadataCollectionFactory(
343-
new AttributesResourceMetadataCollectionFactory(
343+
new AttributesResourceMetadataCollectionFactory(
344+
new ConcernsResourceMetadataCollectionFactory(
344345
null,
345346
$app->make(LoggerInterface::class),
346347
[
@@ -353,7 +354,7 @@ public function register(): void
353354
'routePrefix' => $config->get('api-platform.routes.prefix') ?? '/',
354355
],
355356
$config->get('api-platform.graphql.enabled'),
356-
)
357+
),
357358
)
358359
)
359360
)

src/Laravel/Eloquent/Extension/FilterQueryExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function apply(Builder $builder, array $uriVariables, Operation $operatio
5050

5151
$filter = $filterId instanceof FilterInterface ? $filterId : ($this->filterLocator->has($filterId) ? $this->filterLocator->get($filterId) : null);
5252
if ($filter instanceof FilterInterface) {
53-
$builder = $filter->apply($builder, $values, $parameter, $context + ($parameter->getFilterContext() ?? []));
53+
$builder = $filter->apply($builder, $values, $parameter, $context + ($parameter->getFilterContext() ?? []));
5454
}
5555
}
5656

src/Laravel/Eloquent/Filter/AfterDateFilter.php

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/Laravel/Eloquent/Filter/BeforeDateFilter.php

Lines changed: 0 additions & 36 deletions
This file was deleted.

src/Laravel/Eloquent/Filter/DateFilter.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@
1313

1414
namespace ApiPlatform\Laravel\Eloquent\Filter;
1515

16+
use ApiPlatform\Metadata\HasSchemaFilterInterface;
1617
use ApiPlatform\Metadata\Parameter;
1718
use Illuminate\Database\Eloquent\Builder;
1819
use Illuminate\Database\Eloquent\Model;
1920

20-
final class DateFilter implements FilterInterface
21+
final class DateFilter implements FilterInterface, HasSchemaFilterInterface
2122
{
23+
use QueryPropertyTrait;
24+
2225
/**
2326
* @param Builder<Model> $builder
2427
* @param array<string, mixed> $context
@@ -31,6 +34,14 @@ public function apply(Builder $builder, mixed $values, Parameter $parameter, arr
3134

3235
$datetime = new \DateTimeImmutable($values);
3336

34-
return $builder->whereDate($parameter->getProperty(), $datetime);
37+
return $builder->{($context['whereClause'] ?? 'where') . 'Date'}($this->getQueryProperty($parameter), $datetime);
38+
}
39+
40+
/**
41+
* @return array<string, mixed>
42+
*/
43+
public function getSchema(Parameter $parameter): array
44+
{
45+
return ['type' => 'date'];
3546
}
3647
}

src/Laravel/Eloquent/Filter/EndSearchFilter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
final class EndSearchFilter implements FilterInterface
2121
{
22+
use QueryPropertyTrait;
23+
2224
/**
2325
* @param Builder<Model> $builder
2426
* @param array<string, mixed> $context
2527
*/
2628
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
2729
{
28-
return $builder->where($parameter->getProperty(), 'like', '%'.$values);
30+
return $builder->{$context['whereClause'] ?? 'where'}($this->getQueryProperty($parameter), 'like', '%'.$values);
2931
}
3032
}

src/Laravel/Eloquent/Filter/EqualsFilter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
final class EqualsFilter implements FilterInterface
2121
{
22+
use QueryPropertyTrait;
23+
2224
/**
2325
* @param Builder<Model> $builder
2426
* @param array<string, mixed> $context
2527
*/
2628
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
2729
{
28-
return $builder->where($parameter->getProperty(), $values);
30+
return $builder->{$context['whereClause'] ?? 'where'}($this->getQueryProperty($parameter), $values);
2931
}
3032
}

src/Laravel/Eloquent/Filter/OrFilter.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313

1414
namespace ApiPlatform\Laravel\Eloquent\Filter;
1515

16+
use ApiPlatform\Metadata\HasOpenApiParameterFilterInterface;
17+
use ApiPlatform\Metadata\HasSchemaFilterInterface;
1618
use ApiPlatform\Metadata\Parameter;
19+
use ApiPlatform\OpenApi\Model\Parameter as OpenApiParameter;
1720
use Illuminate\Database\Eloquent\Builder;
1821
use Illuminate\Database\Eloquent\Model;
1922

20-
final readonly class OrFilter implements FilterInterface
23+
final readonly class OrFilter implements FilterInterface, HasSchemaFilterInterface, HasOpenApiParameterFilterInterface
2124
{
2225
public function __construct(private FilterInterface $filter)
2326
{
@@ -29,10 +32,27 @@ public function __construct(private FilterInterface $filter)
2932
*/
3033
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
3134
{
32-
foreach ($values as $value) {
33-
$this->filter->apply($builder, $value, $parameter, ['whereClause' => 'orWhere'] + $context);
34-
}
35+
$builder = $builder->where(function ($builder) use ($values, $parameter, $context) {
36+
foreach ($values as $value) {
37+
$this->filter->apply($builder, $value, $parameter, ['whereClause' => 'orWhere'] + $context);
38+
}
39+
});
3540

3641
return $builder;
3742
}
43+
44+
/**
45+
* @return array<string, mixed>
46+
*/
47+
public function getSchema(Parameter $parameter): array
48+
{
49+
$schema = $this->filter instanceof HasSchemaFilterInterface ? $this->filter->getSchema($parameter) : ['type' => 'string'];
50+
51+
return ['type' => 'array', 'items' => $schema];
52+
}
53+
54+
public function getOpenApiParameter(Parameter $parameter): ?OpenApiParameter
55+
{
56+
return new OpenApiParameter(name: $parameter->getKey() . '[]', in: 'query', style: 'deepObject', explode: true);
57+
}
3858
}

src/Laravel/Eloquent/Filter/OrderFilter.php

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,63 @@
1313

1414
namespace ApiPlatform\Laravel\Eloquent\Filter;
1515

16+
use ApiPlatform\Metadata\HasOpenApiParameterFilterInterface;
17+
use ApiPlatform\Metadata\HasSchemaFilterInterface;
1618
use ApiPlatform\Metadata\Parameter;
19+
use ApiPlatform\OpenApi\Model\Parameter as OpenApiParameter;
1720
use Illuminate\Database\Eloquent\Builder;
1821
use Illuminate\Database\Eloquent\Model;
1922

20-
final class OrderFilter implements FilterInterface
23+
final class OrderFilter implements FilterInterface, HasSchemaFilterInterface, HasOpenApiParameterFilterInterface
2124
{
25+
use QueryPropertyTrait;
26+
2227
/**
2328
* @param Builder<Model> $builder
2429
* @param array<string, mixed> $context
2530
*/
2631
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
2732
{
2833
if (!\is_string($values)) {
34+
$properties = $parameter->getExtraProperties()['_properties'] ?? [];
35+
36+
foreach ($values as $key => $value) {
37+
if (!isset($properties[$key])) {
38+
continue;
39+
}
40+
41+
$builder = $builder->orderBy($properties[$key], $value);
42+
}
43+
2944
return $builder;
3045
}
3146

32-
return $builder->orderBy($parameter->getProperty(), $values);
47+
return $builder->orderBy($this->getQueryProperty($parameter), $values);
48+
}
49+
50+
/**
51+
* @return array<string, mixed>
52+
*/
53+
public function getSchema(Parameter $parameter): array
54+
{
55+
if (str_contains($parameter->getKey(), ':property')) {
56+
$properties = [];
57+
foreach (array_keys($parameter->getExtraProperties()['_properties'] ?? []) as $property) {
58+
$properties[$property] = ['type' => 'string', 'enum' => ['asc', 'desc']];
59+
}
60+
61+
return ['type' => 'object', 'properties' => $properties, 'required' => []];
62+
}
63+
64+
return ['type' => 'string', 'enum' => ['asc', 'desc']];
65+
}
66+
67+
public function getOpenApiParameter(Parameter $parameter): ?OpenApiParameter
68+
{
69+
if (str_contains($parameter->getKey(), ':property')) {
70+
return new OpenApiParameter(name: str_replace('[:property]', '', $parameter->getKey()), in: 'query', style: 'deepObject', explode: true);
71+
}
72+
73+
return null;
3374
}
3475
}

src/Laravel/Eloquent/Filter/PartialSearchFilter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
final class PartialSearchFilter implements FilterInterface
2121
{
22+
use QueryPropertyTrait;
23+
2224
/**
2325
* @param Builder<Model> $builder
2426
* @param array<string, mixed> $context
2527
*/
2628
public function apply(Builder $builder, mixed $values, Parameter $parameter, array $context = []): Builder
2729
{
28-
return $builder->{$context['whereClause'] ?? 'where'}($parameter->getProperty(), 'like', '%'.$values.'%');
30+
return $builder->{$context['whereClause'] ?? 'where'}($this->getQueryProperty($parameter), 'like', '%'.$values.'%');
2931
}
3032
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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\Laravel\Eloquent\Filter;
15+
16+
use ApiPlatform\Metadata\Parameter;
17+
18+
/**
19+
* @internal
20+
*/
21+
trait QueryPropertyTrait
22+
{
23+
private function getQueryProperty(Parameter $parameter): ?string
24+
{
25+
return $parameter->getExtraProperties()['_query_property'] ?? $parameter->getProperty() ?? null;
26+
}
27+
}

0 commit comments

Comments
 (0)