Skip to content

Commit b350647

Browse files
authored
Merge pull request #2897 from antograssiot/nested-filtering-name-converter
Fix nested filtering with name converters
2 parents efa341c + c1e5008 commit b350647

File tree

9 files changed

+323
-5
lines changed

9 files changed

+323
-5
lines changed

features/bootstrap/DoctrineContext.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedBoolean as ConvertedBoolDocument;
2121
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedDate as ConvertedDateDocument;
2222
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedInteger as ConvertedIntegerDocument;
23+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedOwner as ConvertedOwnerDocument;
24+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedRelated as ConvertedRelatedDocument;
2325
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConvertedString as ConvertedStringDocument;
2426
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\Customer as CustomerDocument;
2527
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\Dummy as DummyDocument;
@@ -72,6 +74,8 @@
7274
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedBoolean;
7375
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedDate;
7476
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedInteger;
77+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedOwner;
78+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedRelated;
7579
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConvertedString;
7680
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Customer;
7781
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
@@ -1459,6 +1463,25 @@ public function thereIsTheFollowingProduct(PyStringNode $dataNode): void
14591463
$this->manager->flush();
14601464
}
14611465

1466+
/**
1467+
* @Given there are :nb convertedOwner objects with convertedRelated
1468+
*/
1469+
public function thereAreConvertedOwnerObjects(int $nb)
1470+
{
1471+
for ($i = 1; $i <= $nb; ++$i) {
1472+
$related = $this->buildConvertedRelated();
1473+
$related->nameConverted = 'Converted '.$i;
1474+
1475+
$owner = $this->buildConvertedOwner();
1476+
$owner->nameConverted = $related;
1477+
1478+
$this->manager->persist($related);
1479+
$this->manager->persist($owner);
1480+
}
1481+
1482+
$this->manager->flush();
1483+
}
1484+
14621485
private function isOrm(): bool
14631486
{
14641487
return null !== $this->schemaTool;
@@ -1812,4 +1835,20 @@ private function buildConvertedString()
18121835
{
18131836
return $this->isOrm() ? new ConvertedString() : new ConvertedStringDocument();
18141837
}
1838+
1839+
/**
1840+
* @return ConvertedOwner|ConvertedOwnerDocument
1841+
*/
1842+
private function buildConvertedOwner()
1843+
{
1844+
return $this->isOrm() ? new ConvertedOwner() : new ConvertedOwnerDocument();
1845+
}
1846+
1847+
/**
1848+
* @return ConvertedRelated|ConvertedRelatedDocument
1849+
*/
1850+
private function buildConvertedRelated()
1851+
{
1852+
return $this->isOrm() ? new ConvertedRelated() : new ConvertedRelatedDocument();
1853+
}
18151854
}

features/doctrine/search_filter.feature

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,6 @@ Feature: Search filter on collections
657657
Given there is a dummy object with a fourth level relation
658658
When I send a "GET" request to "/dummies?relatedDummy.thirdLevel.level=3"
659659
Then the response status code should be 200
660-
And the response should be in JSON
661660
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
662661
And the JSON should be valid according to this schema:
663662
"""
@@ -797,3 +796,81 @@ Feature: Search filter on collections
797796
}
798797
}
799798
"""
799+
800+
801+
@createSchema
802+
Scenario: Search collection on a property using a nested name converted
803+
Given there are 30 convertedOwner objects with convertedRelated
804+
When I send a "GET" request to "/converted_owners?name_converted.name_converted=Converted 3"
805+
Then the response status code should be 200
806+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
807+
Then print last JSON response
808+
And the JSON should be valid according to this schema:
809+
"""
810+
{
811+
"type": "object",
812+
"properties": {
813+
"@context": {"pattern": "^/contexts/ConvertedOwner$"},
814+
"@id": {"pattern": "^/converted_owners$"},
815+
"@type": {"pattern": "^hydra:Collection$"},
816+
"hydra:member": {
817+
"type": "array",
818+
"items": {
819+
"type": "object",
820+
"properties": {
821+
"@id": {
822+
"oneOf": [
823+
{"pattern": "^/converted_owners/3$"},
824+
{"pattern": "^/converted_owners/30$"}
825+
]
826+
},
827+
"name_converted": {
828+
"oneOf": [
829+
{"pattern": "^/converted_relateds/3$"},
830+
{"pattern": "^/converted_relateds/30$"}
831+
]
832+
},
833+
"required": ["@id", "name_converted"]
834+
}
835+
},
836+
"minItems": 2,
837+
"maxItems": 2
838+
},
839+
"hydra:view": {
840+
"type": "object",
841+
"properties": {
842+
"@id": {"pattern": "^/converted_owners\\?name_converted.name_converted=Converted%203"},
843+
"@type": {"pattern": "^hydra:PartialCollectionView$"}
844+
}
845+
},
846+
"hydra:search": {
847+
"type": "object",
848+
"properties": {
849+
"@type": {"pattern": "^hydra:IriTemplate$"},
850+
"hydra:template": {"pattern": "^/converted_owners\\{\\?.*name_converted\\.name_converted.*\\}$"},
851+
"hydra:variableRepresentation": {"pattern": "^BasicRepresentation$"},
852+
"hydra:mapping": {
853+
"type": "array",
854+
"items": {
855+
"type": "object",
856+
"properties": {
857+
"@type": {"pattern": "^IriTemplateMapping$"},
858+
"variable": {"pattern": "^name_converted\\.name_converted"},
859+
"property": {"pattern": "^name_converted\\.name_converted$"},
860+
"required": {"type": "boolean"}
861+
},
862+
"required": ["@type", "variable", "property", "required"],
863+
"additionalProperties": false
864+
},
865+
"additionalItems": true,
866+
"uniqueItems": true
867+
}
868+
},
869+
"additionalProperties": false,
870+
"required": ["@type", "hydra:template", "hydra:variableRepresentation", "hydra:mapping"]
871+
},
872+
"additionalProperties": false,
873+
"required": ["@context", "@id", "@type", "hydra:member", "hydra:totalItems", "hydra:view", "hydra:search"]
874+
}
875+
}
876+
"""

phpstan.neon.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ parameters:
8484
message: '#Binary operation "\+" between (float\|int\|)?string and 0 results in an error\.#'
8585
path: %currentWorkingDirectory%/src/Bridge/Doctrine/Common/Filter/RangeFilterTrait.php
8686
- '#Parameter \#1 \$objectValue of method GraphQL\\Type\\Definition\\InterfaceType::resolveType\(\) expects object, array(<string, string>)? given.#'
87+
-
88+
message: '#Parameter \#1 $callback of function array_map expects (callable)|null, array<int, mixed> given\.#'
89+
path: %currentWorkingDirectory%/src/Bridge/Doctrine/*/Filter/AbstractFilter.php
8790

8891
# Expected, due to deprecations
8992
- '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\QueryResult(Item|Collection)ExtensionInterface::getResult\(\) invoked with 4 parameters, 1 required\.#'

src/Bridge/Doctrine/MongoDbOdm/Filter/AbstractFilter.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,19 @@ protected function isPropertyEnabled(string $property, string $resourceClass): b
9393

9494
protected function denormalizePropertyName($property)
9595
{
96-
return null !== $this->nameConverter ? $this->nameConverter->denormalize($property) : $property;
96+
if (null === $this->nameConverter) {
97+
return $property;
98+
}
99+
100+
return implode('.', array_map([$this->nameConverter, 'denormalize'], explode('.', $property)));
97101
}
98102

99103
protected function normalizePropertyName($property)
100104
{
101-
return null !== $this->nameConverter ? $this->nameConverter->normalize($property) : $property;
105+
if (null === $this->nameConverter) {
106+
return $property;
107+
}
108+
109+
return implode('.', array_map([$this->nameConverter, 'normalize'], explode('.', $property)));
102110
}
103111
}

src/Bridge/Doctrine/Orm/Filter/AbstractFilter.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,19 @@ protected function extractProperties(Request $request/*, string $resourceClass*/
144144

145145
protected function denormalizePropertyName($property)
146146
{
147-
return null !== $this->nameConverter ? $this->nameConverter->denormalize($property) : $property;
147+
if (null === $this->nameConverter) {
148+
return $property;
149+
}
150+
151+
return implode('.', array_map([$this->nameConverter, 'denormalize'], explode('.', $property)));
148152
}
149153

150154
protected function normalizePropertyName($property)
151155
{
152-
return null !== $this->nameConverter ? $this->nameConverter->normalize($property) : $property;
156+
if (null === $this->nameConverter) {
157+
return $property;
158+
}
159+
160+
return implode('.', array_map([$this->nameConverter, 'normalize'], explode('.', $property)));
153161
}
154162
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\Document;
15+
16+
use ApiPlatform\Core\Annotation\ApiFilter;
17+
use ApiPlatform\Core\Annotation\ApiResource;
18+
use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\SearchFilter;
19+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
20+
21+
/**
22+
* @ApiResource
23+
* @ODM\Document
24+
* @ApiFilter(SearchFilter::class, properties={"nameConverted.nameConverted"="partial"})
25+
*/
26+
class ConvertedOwner
27+
{
28+
/**
29+
* @var int
30+
*
31+
* @ODM\Id(strategy="INCREMENT", type="integer")
32+
*/
33+
private $id;
34+
35+
/**
36+
* @var ConvertedRelated
37+
*
38+
* @ODM\ReferenceOne(targetDocument=ConvertedRelated::class, storeAs="id", nullable=true)
39+
*/
40+
public $nameConverted;
41+
42+
public function getId()
43+
{
44+
return $this->id;
45+
}
46+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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\Document;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
18+
19+
/**
20+
* @ApiResource
21+
* @
22+
* @ODM\Document
23+
*/
24+
class ConvertedRelated
25+
{
26+
/**
27+
* @var int
28+
*
29+
* @ODM\Id(strategy="INCREMENT", type="integer")
30+
*/
31+
private $id;
32+
33+
/**
34+
* @var string
35+
*
36+
* @ODM\Field
37+
*/
38+
public $nameConverted;
39+
40+
public function getId()
41+
{
42+
return $this->id;
43+
}
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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\ApiFilter;
17+
use ApiPlatform\Core\Annotation\ApiResource;
18+
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
19+
use Doctrine\ORM\Mapping as ORM;
20+
21+
/**
22+
* @ApiResource
23+
* @ORM\Entity
24+
* @ApiFilter(SearchFilter::class, properties={"nameConverted.nameConverted"="partial"})
25+
*/
26+
class ConvertedOwner
27+
{
28+
/**
29+
* @var int
30+
*
31+
* @ORM\Column(type="integer", nullable=true)
32+
* @ORM\Id
33+
* @ORM\GeneratedValue(strategy="AUTO")
34+
*/
35+
private $id;
36+
37+
/**
38+
* @var ConvertedRelated
39+
*
40+
* @ORM\ManyToOne(targetEntity="ConvertedRelated")
41+
*/
42+
public $nameConverted;
43+
44+
public function getId()
45+
{
46+
return $this->id;
47+
}
48+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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\ApiResource;
17+
use Doctrine\ORM\Mapping as ORM;
18+
19+
/**
20+
* @ApiResource
21+
* @ORM\Entity
22+
*/
23+
class ConvertedRelated
24+
{
25+
/**
26+
* @var int
27+
*
28+
* @ORM\Column(type="integer", nullable=true)
29+
* @ORM\Id
30+
* @ORM\GeneratedValue(strategy="AUTO")
31+
*/
32+
private $id;
33+
34+
/**
35+
* @var string
36+
*
37+
* @ORM\Column(type="string")
38+
*/
39+
public $nameConverted;
40+
41+
public function getId()
42+
{
43+
return $this->id;
44+
}
45+
}

0 commit comments

Comments
 (0)