Skip to content

Commit da476c1

Browse files
committed
fix issue with array notation filter name
1 parent 2367a47 commit da476c1

File tree

5 files changed

+162
-2
lines changed

5 files changed

+162
-2
lines changed

features/filter/filter_validation.feature

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,32 @@ Feature: Validate filters based upon filter description
88
When I am on "/filter_validators?required="
99
Then the response status code should be 200
1010

11-
@dropSchema
1211
Scenario: Required filter should throw an error if not set
1312
When I am on "/filter_validators"
1413
Then the response status code should be 400
1514
And the JSON node "detail" should be equal to 'Query parameter "required" is required'
15+
16+
Scenario: Required filter should not throw an error if set
17+
When I am on "/array_filter_validators?arrayRequired[]=foo&indexedArrayRequired[foo]=foo"
18+
Then the response status code should be 200
19+
20+
Scenario: Required filter should throw an error if not set
21+
When I am on "/array_filter_validators"
22+
Then the response status code should be 400
23+
And the JSON node "detail" should be equal to 'Query parameter "arrayRequired[]" is required'
24+
25+
When I am on "/array_filter_validators?arrayRequired=foo&indexedArrayRequired[foo]=foo"
26+
Then the response status code should be 400
27+
And the JSON node "detail" should be equal to 'Query parameter "arrayRequired[]" is required'
28+
29+
When I am on "/array_filter_validators?arrayRequired[foo]=foo"
30+
Then the response status code should be 400
31+
And the JSON node "detail" should be equal to 'Query parameter "arrayRequired[]" is required'
32+
33+
When I am on "/array_filter_validators?arrayRequired[]=foo"
34+
Then the response status code should be 400
35+
And the JSON node "detail" should be equal to 'Query parameter "indexedArrayRequired[foo]" is required'
36+
37+
When I am on "/array_filter_validators?arrayRequired[]=foo&indexedArrayRequired[bar]=bar"
38+
Then the response status code should be 400
39+
And the JSON node "detail" should be equal to 'Query parameter "indexedArrayRequired[foo]" is required'

src/Filter/QueryParameterValidateListener.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,15 @@ public function onKernelRequest(GetResponseEvent $event)
6060
foreach ($filter->getDescription($attributes['resource_class']) as $name => $data) {
6161
$errorList = [];
6262

63-
if (($data['required'] ?? false) && null === $request->query->get($name)) {
63+
if (!($data['required'] ?? false)) { // property is not required
64+
continue;
65+
}
66+
67+
if (false !== strpos($name, '[')) { // array notation of filter
68+
if (!$this->isArrayNotationFilterValid($name, $request)) {
69+
$errorList[] = sprintf('Query parameter "%s" is required', $name);
70+
}
71+
} elseif (null === $request->query->get($name)) {
6472
$errorList[] = sprintf('Query parameter "%s" is required', $name);
6573
}
6674

@@ -70,4 +78,15 @@ public function onKernelRequest(GetResponseEvent $event)
7078
}
7179
}
7280
}
81+
82+
private function isArrayNotationFilterValid($name, $request): bool
83+
{
84+
$matches = [];
85+
preg_match('/([^[]+)\[(.*)\]/', $name, $matches);
86+
list(, $rootName, $keyName) = $matches;
87+
$keyName = $keyName ?: 0; // array without index should test the first key
88+
$queryParameter = $request->query->get($rootName);
89+
90+
return is_array($queryParameter) && isset($queryParameter[$keyName]);
91+
}
7392
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\Core\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Core\Annotation\ApiProperty;
17+
use ApiPlatform\Core\Annotation\ApiResource;
18+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\ArrayRequiredFilter;
19+
use Doctrine\ORM\Mapping as ORM;
20+
21+
/**
22+
* Filter Validator entity.
23+
*
24+
* @author Julien Deniau <[email protected]>
25+
*
26+
* @ApiResource(attributes={
27+
* "filters"={
28+
* ArrayRequiredFilter::class
29+
* }
30+
* })
31+
* @ORM\Entity
32+
*/
33+
class ArrayFilterValidator
34+
{
35+
/**
36+
* @var int The id
37+
*
38+
* @ORM\Column(type="integer")
39+
* @ORM\Id
40+
* @ORM\GeneratedValue(strategy="AUTO")
41+
*/
42+
private $id;
43+
44+
/**
45+
* @var string A name
46+
*
47+
* @ORM\Column
48+
* @ApiProperty(iri="http://schema.org/name")
49+
*/
50+
private $name;
51+
52+
public function getId()
53+
{
54+
return $this->id;
55+
}
56+
57+
public function setId($id)
58+
{
59+
$this->id = $id;
60+
}
61+
62+
public function setName($name)
63+
{
64+
$this->name = $name;
65+
}
66+
67+
public function getName()
68+
{
69+
return $this->name;
70+
}
71+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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\Core\Tests\Fixtures\TestBundle\Filter;
15+
16+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
17+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
18+
use Doctrine\ORM\QueryBuilder;
19+
20+
class ArrayRequiredFilter extends AbstractFilter
21+
{
22+
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
23+
{
24+
}
25+
26+
// This function is only used to hook in documentation generators (supported by Swagger and Hydra)
27+
public function getDescription(string $resourceClass): array
28+
{
29+
return [
30+
'arrayRequired[]' => [
31+
'property' => 'arrayRequired',
32+
'type' => 'string',
33+
'required' => true,
34+
],
35+
'indexedArrayRequired[foo]' => [
36+
'property' => 'indexedArrayRequired',
37+
'type' => 'string',
38+
'required' => true,
39+
],
40+
];
41+
}
42+
}

tests/Fixtures/app/config/config_test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ services:
169169
arguments: [ '@doctrine' ]
170170
tags: [ 'api_platform.filter' ]
171171

172+
ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\ArrayRequiredFilter:
173+
arguments: [ '@doctrine' ]
174+
tags: [ 'api_platform.filter' ]
175+
172176
app.config_dummy_resource.action:
173177
class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Action\ConfigCustom'
174178
arguments: ['@api_platform.item_data_provider']

0 commit comments

Comments
 (0)