Skip to content

Commit 186c400

Browse files
committed
feat(doctrine): boolean filter like laravel filters
1 parent 1ac0c3d commit 186c400

File tree

13 files changed

+477
-26
lines changed

13 files changed

+477
-26
lines changed

src/Doctrine/Odm/Extension/ParameterExtension.php

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@
1414
namespace ApiPlatform\Doctrine\Odm\Extension;
1515

1616
use ApiPlatform\Doctrine\Common\ParameterValueExtractorTrait;
17+
use ApiPlatform\Doctrine\Odm\Filter\AbstractFilter;
1718
use ApiPlatform\Doctrine\Odm\Filter\FilterInterface;
1819
use ApiPlatform\Metadata\Operation;
1920
use ApiPlatform\State\ParameterNotFound;
21+
use Doctrine\Bundle\MongoDBBundle\ManagerRegistry;
2022
use Doctrine\ODM\MongoDB\Aggregation\Builder;
23+
use Psr\Container\ContainerExceptionInterface;
2124
use Psr\Container\ContainerInterface;
25+
use Psr\Container\NotFoundExceptionInterface;
2226

2327
/**
2428
* Reads operation parameters and execute its filter.
@@ -29,14 +33,20 @@ final class ParameterExtension implements AggregationCollectionExtensionInterfac
2933
{
3034
use ParameterValueExtractorTrait;
3135

32-
public function __construct(private readonly ContainerInterface $filterLocator)
33-
{
36+
public function __construct(
37+
private readonly ContainerInterface $filterLocator,
38+
private readonly ?ManagerRegistry $managerRegistry = null,
39+
) {
3440
}
3541

42+
/**
43+
* @throws ContainerExceptionInterface
44+
* @throws NotFoundExceptionInterface
45+
*/
3646
private function applyFilter(Builder $aggregationBuilder, ?string $resourceClass = null, ?Operation $operation = null, array &$context = []): void
3747
{
3848
foreach ($operation->getParameters() ?? [] as $parameter) {
39-
if (!($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
49+
if (null === ($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
4050
continue;
4151
}
4252

@@ -45,7 +55,8 @@ private function applyFilter(Builder $aggregationBuilder, ?string $resourceClass
4555
continue;
4656
}
4757

48-
$filter = $this->filterLocator->has($filterId) ? $this->filterLocator->get($filterId) : null;
58+
$filter = $this->getFilter($filterId);
59+
4960
if ($filter instanceof FilterInterface) {
5061
$filterContext = ['filters' => $values, 'parameter' => $parameter];
5162
$filter->apply($aggregationBuilder, $resourceClass, $operation, $filterContext);
@@ -72,4 +83,26 @@ public function applyToItem(Builder $aggregationBuilder, string $resourceClass,
7283
{
7384
$this->applyFilter($aggregationBuilder, $resourceClass, $operation, $context);
7485
}
86+
87+
/**
88+
* @param $values array<string, string>
89+
*
90+
* @throws ContainerExceptionInterface
91+
* @throws NotFoundExceptionInterface
92+
*/
93+
public function getFilter(AbstractFilter|string|null $filterId, array $values = []): ?FilterInterface
94+
{
95+
if ($filterId instanceof AbstractFilter) {
96+
$filterId->setManagerRegistry($this->managerRegistry);
97+
$filterId->setProperties($values);
98+
99+
return $filterId;
100+
}
101+
102+
if (\is_string($filterId) && $this->filterLocator->has($filterId)) {
103+
return $this->filterLocator->get($filterId);
104+
}
105+
106+
return null;
107+
}
75108
}

src/Doctrine/Odm/Filter/AbstractFilter.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ abstract class AbstractFilter implements FilterInterface, PropertyAwareFilterInt
3636
use PropertyHelperTrait;
3737
protected LoggerInterface $logger;
3838

39-
public function __construct(protected ManagerRegistry $managerRegistry, ?LoggerInterface $logger = null, protected ?array $properties = null, protected ?NameConverterInterface $nameConverter = null)
40-
{
39+
public function __construct(
40+
protected ?ManagerRegistry $managerRegistry = null,
41+
?LoggerInterface $logger = null,
42+
protected ?array $properties = null,
43+
protected ?NameConverterInterface $nameConverter = null,
44+
) {
4145
$this->logger = $logger ?? new NullLogger();
4246
}
4347

@@ -56,11 +60,16 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, ?Opera
5660
*/
5761
abstract protected function filterProperty(string $property, $value, Builder $aggregationBuilder, string $resourceClass, ?Operation $operation = null, array &$context = []): void;
5862

59-
protected function getManagerRegistry(): ManagerRegistry
63+
protected function getManagerRegistry(): ?ManagerRegistry
6064
{
6165
return $this->managerRegistry;
6266
}
6367

68+
public function setManagerRegistry(?ManagerRegistry $managerRegistry): ?ManagerRegistry
69+
{
70+
return $this->managerRegistry = $managerRegistry;
71+
}
72+
6473
protected function getProperties(): ?array
6574
{
6675
return $this->properties;

src/Doctrine/Odm/Filter/BooleanFilter.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
namespace ApiPlatform\Doctrine\Odm\Filter;
1515

1616
use ApiPlatform\Doctrine\Common\Filter\BooleanFilterTrait;
17+
use ApiPlatform\Metadata\JsonSchemaFilterInterface;
18+
use ApiPlatform\Metadata\OpenApiParameterFilterInterface;
1719
use ApiPlatform\Metadata\Operation;
20+
use ApiPlatform\Metadata\Parameter;
21+
use ApiPlatform\Metadata\QueryParameter;
22+
use ApiPlatform\OpenApi\Model\Parameter as OpenApiParameter;
1823
use Doctrine\ODM\MongoDB\Aggregation\Builder;
1924
use Doctrine\ODM\MongoDB\Types\Type as MongoDbType;
2025

@@ -104,7 +109,7 @@
104109
* @author Teoh Han Hui <[email protected]>
105110
* @author Alan Poulain <[email protected]>
106111
*/
107-
final class BooleanFilter extends AbstractFilter
112+
final class BooleanFilter extends AbstractFilter implements OpenApiParameterFilterInterface, JsonSchemaFilterInterface
108113
{
109114
use BooleanFilterTrait;
110115

@@ -139,4 +144,27 @@ protected function filterProperty(string $property, $value, Builder $aggregation
139144

140145
$aggregationBuilder->match()->field($matchField)->equals($value);
141146
}
147+
148+
/**
149+
* @return array<string, string>
150+
*/
151+
public function getSchema(Parameter $parameter): array
152+
{
153+
return $parameter->getSchema() ?? ['type' => 'boolean'];
154+
}
155+
156+
public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null
157+
{
158+
$in = $parameter instanceof QueryParameter ? 'query' : 'header';
159+
$key = $parameter->getKey();
160+
161+
return [
162+
new OpenApiParameter(
163+
name: $key,
164+
in: $in,
165+
required: false,
166+
schema: ['type' => 'boolean'],
167+
),
168+
];
169+
}
142170
}

src/Doctrine/Odm/PropertyHelperTrait.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*/
2828
trait PropertyHelperTrait
2929
{
30-
abstract protected function getManagerRegistry(): ManagerRegistry;
30+
abstract protected function getManagerRegistry(): ?ManagerRegistry;
3131

3232
/**
3333
* Splits the given property into parts.
@@ -41,7 +41,7 @@ protected function getClassMetadata(string $resourceClass): ClassMetadata
4141
{
4242
$manager = $this
4343
->getManagerRegistry()
44-
->getManagerForClass($resourceClass);
44+
?->getManagerForClass($resourceClass);
4545

4646
if ($manager) {
4747
return $manager->getClassMetadata($resourceClass);

src/Doctrine/Orm/Extension/ParameterExtension.php

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
namespace ApiPlatform\Doctrine\Orm\Extension;
1515

1616
use ApiPlatform\Doctrine\Common\ParameterValueExtractorTrait;
17+
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
1718
use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
1819
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
1920
use ApiPlatform\Metadata\Operation;
2021
use ApiPlatform\State\ParameterNotFound;
2122
use Doctrine\ORM\QueryBuilder;
23+
use Psr\Container\ContainerExceptionInterface;
2224
use Psr\Container\ContainerInterface;
25+
use Psr\Container\NotFoundExceptionInterface;
26+
use Symfony\Bridge\Doctrine\ManagerRegistry;
2327

2428
/**
2529
* Reads operation parameters and execute its filter.
@@ -30,17 +34,22 @@ final class ParameterExtension implements QueryCollectionExtensionInterface, Que
3034
{
3135
use ParameterValueExtractorTrait;
3236

33-
public function __construct(private readonly ContainerInterface $filterLocator)
34-
{
37+
public function __construct(
38+
private readonly ContainerInterface $filterLocator,
39+
private readonly ?ManagerRegistry $managerRegistry = null,
40+
) {
3541
}
3642

3743
/**
3844
* @param array<string, mixed> $context
45+
*
46+
* @throws ContainerExceptionInterface
47+
* @throws NotFoundExceptionInterface
3948
*/
4049
private function applyFilter(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
4150
{
4251
foreach ($operation?->getParameters() ?? [] as $parameter) {
43-
if (!($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
52+
if (null === ($v = $parameter->getValue()) || $v instanceof ParameterNotFound) {
4453
continue;
4554
}
4655

@@ -49,9 +58,12 @@ private function applyFilter(QueryBuilder $queryBuilder, QueryNameGeneratorInter
4958
continue;
5059
}
5160

52-
$filter = $this->filterLocator->has($filterId) ? $this->filterLocator->get($filterId) : null;
61+
$filter = $this->getFilter($filterId, $values);
62+
5363
if ($filter instanceof FilterInterface) {
54-
$filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $values, 'parameter' => $parameter] + $context);
64+
$filter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation,
65+
['filters' => $values, 'parameter' => $parameter] + $context
66+
);
5567
}
5668
}
5769
}
@@ -71,4 +83,26 @@ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterf
7183
{
7284
$this->applyFilter($queryBuilder, $queryNameGenerator, $resourceClass, $operation, $context);
7385
}
86+
87+
/**
88+
* @param $values array<string, string>
89+
*
90+
* @throws ContainerExceptionInterface
91+
* @throws NotFoundExceptionInterface
92+
*/
93+
private function getFilter(AbstractFilter|string|null $filterId, array $values = []): ?FilterInterface
94+
{
95+
if ($filterId instanceof AbstractFilter) {
96+
$filterId->setManagerRegistry($this->managerRegistry);
97+
$filterId->setProperties($values);
98+
99+
return $filterId;
100+
}
101+
102+
if (\is_string($filterId) && $this->filterLocator->has($filterId)) {
103+
return $this->filterLocator->get($filterId);
104+
}
105+
106+
return null;
107+
}
74108
}

src/Doctrine/Orm/Filter/AbstractFilter.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ abstract class AbstractFilter implements FilterInterface, PropertyAwareFilterInt
3030
use PropertyHelperTrait;
3131
protected LoggerInterface $logger;
3232

33-
public function __construct(protected ManagerRegistry $managerRegistry, ?LoggerInterface $logger = null, protected ?array $properties = null, protected ?NameConverterInterface $nameConverter = null)
34-
{
33+
public function __construct(
34+
protected ?ManagerRegistry $managerRegistry = null,
35+
?LoggerInterface $logger = null,
36+
protected ?array $properties = null,
37+
protected ?NameConverterInterface $nameConverter = null,
38+
) {
3539
$this->logger = $logger ?? new NullLogger();
3640
}
3741

@@ -53,19 +57,19 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q
5357
*/
5458
abstract protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void;
5559

56-
protected function getManagerRegistry(): ManagerRegistry
60+
protected function getManagerRegistry(): ?ManagerRegistry
5761
{
5862
return $this->managerRegistry;
5963
}
6064

61-
protected function getProperties(): ?array
65+
public function setManagerRegistry(?ManagerRegistry $managerRegistry): ?ManagerRegistry
6266
{
63-
return $this->properties;
67+
return $this->managerRegistry = $managerRegistry;
6468
}
6569

66-
protected function getLogger(): LoggerInterface
70+
public function getProperties(): ?array
6771
{
68-
return $this->logger;
72+
return $this->properties;
6973
}
7074

7175
/**
@@ -76,6 +80,11 @@ public function setProperties(array $properties): void
7680
$this->properties = $properties;
7781
}
7882

83+
protected function getLogger(): LoggerInterface
84+
{
85+
return $this->logger;
86+
}
87+
7988
/**
8089
* Determines whether the given property is enabled.
8190
*/

src/Doctrine/Orm/Filter/BooleanFilter.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515

1616
use ApiPlatform\Doctrine\Common\Filter\BooleanFilterTrait;
1717
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
18+
use ApiPlatform\Metadata\JsonSchemaFilterInterface;
19+
use ApiPlatform\Metadata\OpenApiParameterFilterInterface;
1820
use ApiPlatform\Metadata\Operation;
21+
use ApiPlatform\Metadata\Parameter;
22+
use ApiPlatform\Metadata\QueryParameter;
23+
use ApiPlatform\OpenApi\Model\Parameter as OpenApiParameter;
1924
use Doctrine\DBAL\Types\Types;
2025
use Doctrine\ORM\Query\Expr\Join;
2126
use Doctrine\ORM\QueryBuilder;
@@ -106,7 +111,7 @@
106111
* @author Amrouche Hamza <[email protected]>
107112
* @author Teoh Han Hui <[email protected]>
108113
*/
109-
final class BooleanFilter extends AbstractFilter
114+
final class BooleanFilter extends AbstractFilter implements OpenApiParameterFilterInterface, JsonSchemaFilterInterface
110115
{
111116
use BooleanFilterTrait;
112117

@@ -145,4 +150,27 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
145150
->andWhere(\sprintf('%s.%s = :%s', $alias, $field, $valueParameter))
146151
->setParameter($valueParameter, $value);
147152
}
153+
154+
/**
155+
* @return array<string, string>
156+
*/
157+
public function getSchema(Parameter $parameter): array
158+
{
159+
return $parameter->getSchema() ?? ['type' => 'boolean'];
160+
}
161+
162+
public function getOpenApiParameters(Parameter $parameter): OpenApiParameter|array|null
163+
{
164+
$in = $parameter instanceof QueryParameter ? 'query' : 'header';
165+
$key = $parameter->getKey();
166+
167+
return [
168+
new OpenApiParameter(
169+
name: $key,
170+
in: $in,
171+
required: false,
172+
schema: ['type' => 'boolean'],
173+
),
174+
];
175+
}
148176
}

src/Doctrine/Orm/PropertyHelperTrait.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
*/
3030
trait PropertyHelperTrait
3131
{
32-
abstract protected function getManagerRegistry(): ManagerRegistry;
32+
abstract protected function getManagerRegistry(): ?ManagerRegistry;
3333

3434
/**
3535
* Splits the given property into parts.
@@ -43,7 +43,7 @@ protected function getClassMetadata(string $resourceClass): ClassMetadata
4343
{
4444
$manager = $this
4545
->getManagerRegistry()
46-
->getManagerForClass($resourceClass);
46+
?->getManagerForClass($resourceClass);
4747

4848
if ($manager) {
4949
return $manager->getClassMetadata($resourceClass);

src/Symfony/Bundle/Resources/config/doctrine_mongodb_odm.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137

138138
<service id="api_platform.doctrine_mongodb.odm.extension.parameter_extension" class="ApiPlatform\Doctrine\Odm\Extension\ParameterExtension" public="false">
139139
<argument type="service" id="api_platform.filter_locator" />
140-
140+
<argument type="service" id="doctrine_mongodb" on-invalid="null"/>
141141
<tag name="api_platform.doctrine_mongodb.odm.aggregation_extension.collection" priority="32" />
142142
<tag name="api_platform.doctrine_mongodb.odm.aggregation_extension.item"/>
143143
</service>

src/Symfony/Bundle/Resources/config/doctrine_orm.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150

151151
<service id="api_platform.doctrine.orm.extension.parameter_extension" class="ApiPlatform\Doctrine\Orm\Extension\ParameterExtension" public="false">
152152
<argument type="service" id="api_platform.filter_locator" />
153+
<argument type="service" id="doctrine" on-invalid="null"/>
153154
<tag name="api_platform.doctrine.orm.query_extension.collection" priority="-16" />
154155
<tag name="api_platform.doctrine.orm.query_extension.item" priority="-9" />
155156
</service>

0 commit comments

Comments
 (0)