Skip to content

Commit de69323

Browse files
committed
wip fix placeholder on exact and partial filters
1 parent c28c624 commit de69323

File tree

3 files changed

+180
-2
lines changed

3 files changed

+180
-2
lines changed

src/Metadata/Resource/Factory/ParameterResourceMetadataCollectionFactory.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,16 @@ private function getDefaultParameters(Operation $operation, string $resourceClas
129129
$parameters->add($key, $parameter->withProvider($f->getParameterProvider()));
130130
}
131131

132-
if (':property' === $key) {
132+
if (str_contains($key, ':property')) {
133133
foreach ($propertyNames as $property) {
134134
$converted = $this->nameConverter?->denormalize($property) ?? $property;
135135
$propertyParameter = $this->setDefaults($converted, $parameter, $resourceClass, $properties, $operation);
136136
$priority = $propertyParameter->getPriority() ?? $internalPriority--;
137-
$parameters->add($converted, $propertyParameter->withPriority($priority)->withKey($converted));
137+
$finalKey = str_replace(':property', $converted, $key);
138+
$parameters->add(
139+
$finalKey,
140+
$propertyParameter->withProperty($converted)->withPriority($priority)->withKey($finalKey)
141+
);
138142
}
139143

140144
$parameters->remove($key, $parameter::class);
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\Tests\Fixtures\TestBundle\Entity;
15+
16+
use ApiPlatform\Doctrine\Orm\Filter\PartialSearchFilter;
17+
use ApiPlatform\Metadata\ApiResource;
18+
use ApiPlatform\Metadata\QueryParameter;
19+
use Doctrine\ORM\Mapping as ORM;
20+
21+
#[ApiResource(
22+
normalizationContext: ['hydra_prefix' => false],
23+
parameters: [
24+
'search[:property]' => new QueryParameter(
25+
filter: new PartialSearchFilter(),
26+
properties: ['description', 'name']
27+
),
28+
]
29+
)]
30+
#[ORM\Entity]
31+
class SuperHero
32+
{
33+
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
34+
public ?int $id = null;
35+
36+
#[ORM\Column]
37+
public string $name = '';
38+
39+
#[ORM\Column]
40+
public string $description = '';
41+
42+
#[ORM\Column]
43+
public string $secret = '';
44+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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\Tests\Functional\Parameters;
15+
16+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\SuperHero;
18+
use ApiPlatform\Tests\RecreateSchemaTrait;
19+
use ApiPlatform\Tests\SetupClassResourcesTrait;
20+
use PHPUnit\Framework\Attributes\DataProvider;
21+
22+
/**
23+
* @author Votre Nom <[email protected]>
24+
*/
25+
final class PartialSearchFilterPlaceholderTest extends ApiTestCase
26+
{
27+
use RecreateSchemaTrait;
28+
use SetupClassResourcesTrait;
29+
30+
protected static ?bool $alwaysBootKernel = false;
31+
32+
/**
33+
* @return class-string[]
34+
*/
35+
public static function getResources(): array
36+
{
37+
return [SuperHero::class];
38+
}
39+
40+
/**
41+
* @throws \Throwable
42+
*/
43+
protected function setUp(): void
44+
{
45+
$this->recreateSchema([SuperHero::class]);
46+
$this->loadFixtures();
47+
}
48+
49+
#[DataProvider('partialFilterParameterProvider')]
50+
public function testPartialFilterPlaceholder(string $url, int $expectedCount, array $expectedNames): void
51+
{
52+
53+
$response = self::createClient()->request('GET', $url);
54+
55+
$this->assertResponseIsSuccessful();
56+
57+
$responseData = $response->toArray();
58+
$filteredItems = $responseData['member'];
59+
60+
61+
$this->assertCount($expectedCount, $filteredItems, \sprintf('Expected %d items for URL %s', $expectedCount, $url));
62+
63+
$names = array_map(fn ($item) => $item['name'], $filteredItems);
64+
sort($names);
65+
sort($expectedNames);
66+
67+
$this->assertSame($expectedNames, $names, 'The names do not match the expected values.');
68+
}
69+
70+
public static function partialFilterParameterProvider(): \Generator
71+
{
72+
// Fixtures Recap:
73+
// 1. Name: "Batman", Description: "The Dark Knight", Secret: "Bruce"
74+
// 2. Name: "Batgirl", Description: "Oracle", Secret: "Barbara"
75+
// 3. Name: "Superman", Description: "Man of Steel", Secret: "Clark"
76+
77+
yield 'partial match on name (Bat -> Batman, Batgirl)' => [
78+
'/super_heroes?search[name]=Bat',
79+
2,
80+
['Batgirl', 'Batman'],
81+
];
82+
83+
yield 'partial match on description (Steel -> Superman)' => [
84+
'/super_heroes?search[description]=Steel',
85+
1,
86+
['Superman'],
87+
];
88+
89+
yield 'partial match case insensitive (bat -> Batman, Batgirl)' => [
90+
'/super_heroes?search[name]=bat',
91+
2,
92+
['Batgirl', 'Batman'],
93+
];
94+
95+
yield 'property "secret" is NOT in allow-list (filter ignored)' => [
96+
'/super_heroes?search[secret]=Bruce',
97+
3,
98+
['Batgirl', 'Batman', 'Superman'],
99+
];
100+
}
101+
102+
/**
103+
* @throws \Throwable
104+
*/
105+
private function loadFixtures(): void
106+
{
107+
$manager = $this->getManager();
108+
109+
$item1 = new SuperHero();
110+
$item1->name = 'Batman';
111+
$item1->description = 'The Dark Knight';
112+
$item1->secret = 'Bruce';
113+
114+
$item2 = new SuperHero();
115+
$item2->name = 'Batgirl';
116+
$item2->description = 'Oracle';
117+
$item2->secret = 'Barbara';
118+
119+
$item3 = new SuperHero();
120+
$item3->name = 'Superman';
121+
$item3->description = 'Man of Steel';
122+
$item3->secret = 'Clark';
123+
124+
$manager->persist($item1);
125+
$manager->persist($item2);
126+
$manager->persist($item3);
127+
128+
$manager->flush();
129+
}
130+
}

0 commit comments

Comments
 (0)