Skip to content

Commit 6c085ee

Browse files
bpolaszeksoyuka
authored andcommitted
feat(doctrine): uid search filter support
1 parent 1745ec2 commit 6c085ee

File tree

5 files changed

+155
-2
lines changed

5 files changed

+155
-2
lines changed

features/doctrine/search_filter.feature

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,45 @@ Feature: Search filter on collections
729729
}
730730
"""
731731

732+
@createSchema
733+
Scenario: Get collection by ulid 01H2ZS93NBKJW5W4Y01S8TZ43M
734+
Given there is a UidBasedId resource with id "01H2ZS93NBKJW5W4Y01S8TZ43M"
735+
When I send a "GET" request to "/uid_based_ids?id=/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"
736+
Then the response status code should be 200
737+
And the response should be in JSON
738+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
739+
And the JSON should be valid according to this schema:
740+
"""
741+
{
742+
"type": "object",
743+
"properties": {
744+
"@context": {"pattern": "^/contexts/UidBasedId"},
745+
"@id": {"pattern": "^/uid_based_ids"},
746+
"@type": {"pattern": "^hydra:Collection$"},
747+
"hydra:member": {
748+
"type": "array",
749+
"items": {
750+
"type": "object",
751+
"properties": {
752+
"@id": {
753+
"oneOf": [
754+
{"pattern": "^/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"}
755+
]
756+
}
757+
}
758+
}
759+
},
760+
"hydra:view": {
761+
"type": "object",
762+
"properties": {
763+
"@id": {"pattern": "^/uid_based_ids\\?id=%2Fuid_based_ids%2F01H2ZS93NBKJW5W4Y01S8TZ43M"},
764+
"@type": {"pattern": "^hydra:PartialCollectionView$"}
765+
}
766+
}
767+
}
768+
}
769+
"""
770+
732771
@createSchema
733772
Scenario: Get collection ordered by a non valid properties
734773
When I send a "GET" request to "/dummies?unknown=0"

src/Doctrine/Common/Filter/SearchFilterTrait.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use ApiPlatform\Metadata\IriConverterInterface;
2222
use Psr\Log\LoggerInterface;
2323
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
24+
use Symfony\Component\Uid\AbstractUid;
2425

2526
/**
2627
* Trait for filtering the collection by given properties.
@@ -127,19 +128,32 @@ protected function getIdFromValue(string $value): mixed
127128
$item = $iriConverter->getResourceFromIri($value, ['fetch_data' => false]);
128129

129130
if (null === $this->identifiersExtractor) {
130-
return $this->getPropertyAccessor()->getValue($item, 'id');
131+
$id = $this->getPropertyAccessor()->getValue($item, 'id');
132+
133+
return $this->toUuidValue($id);
131134
}
132135

133136
$identifiers = $this->identifiersExtractor->getIdentifiersFromItem($item);
134137

135-
return 1 === \count($identifiers) ? array_pop($identifiers) : $identifiers;
138+
if (1 === \count($identifiers)) {
139+
$id = array_pop($identifiers);
140+
141+
return $this->toUuidValue($id);
142+
}
143+
144+
return array_map([$this, 'toUuidValue'], $identifiers);
136145
} catch (InvalidArgumentException) {
137146
// Do nothing, return the raw value
138147
}
139148

140149
return $value;
141150
}
142151

152+
private function toUuidValue(mixed $id): mixed
153+
{
154+
return ($id instanceof AbstractUid) ? $id->toBinary() : $id;
155+
}
156+
143157
/**
144158
* Normalize the values array.
145159
*/

tests/Behat/DoctrineContext.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
use ApiPlatform\Tests\Fixtures\TestBundle\Document\SoMany as SoManyDocument;
9898
use ApiPlatform\Tests\Fixtures\TestBundle\Document\Taxon as TaxonDocument;
9999
use ApiPlatform\Tests\Fixtures\TestBundle\Document\ThirdLevel as ThirdLevelDocument;
100+
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UidBasedId as UidBasedIdDocument;
100101
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UrlEncodedId as UrlEncodedIdDocument;
101102
use ApiPlatform\Tests\Fixtures\TestBundle\Document\User as UserDocument;
102103
use ApiPlatform\Tests\Fixtures\TestBundle\Document\VideoGame as VideoGameDocument;
@@ -200,6 +201,7 @@
200201
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Taxon;
201202
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\ThirdLevel;
202203
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\TreeDummy;
204+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UidBasedId;
203205
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UrlEncodedId;
204206
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\User;
205207
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UuidIdentifierDummy;
@@ -217,6 +219,7 @@
217219
use Doctrine\Persistence\ManagerRegistry;
218220
use Doctrine\Persistence\ObjectManager;
219221
use Ramsey\Uuid\Uuid;
222+
use Symfony\Component\Uid\Ulid;
220223
use Symfony\Component\Uid\Uuid as SymfonyUuid;
221224

222225
/**
@@ -1436,6 +1439,17 @@ public function thereIsAUrlEncodedIdResource(): void
14361439
$this->manager->clear();
14371440
}
14381441

1442+
/**
1443+
* @Given there is a UidBasedId resource with id :id
1444+
*/
1445+
public function thereIsAUidBasedIdResource(string $id): void
1446+
{
1447+
$uidBasedIdResource = ($this->isOrm() ? new UidBasedId(Ulid::fromBase32($id)) : new UidBasedIdDocument(Ulid::fromBase32($id)));
1448+
$this->manager->persist($uidBasedIdResource);
1449+
$this->manager->flush();
1450+
$this->manager->clear();
1451+
}
1452+
14391453
/**
14401454
* @Given there is a Program
14411455
*/
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\Tests\Fixtures\TestBundle\Document;
15+
16+
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
17+
use ApiPlatform\Metadata\ApiFilter;
18+
use ApiPlatform\Metadata\ApiResource;
19+
use ApiPlatform\Metadata\Get;
20+
use ApiPlatform\Metadata\GetCollection;
21+
use ApiPlatform\Metadata\Post;
22+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
23+
use Symfony\Component\Uid\Ulid;
24+
25+
/**
26+
* @author Beno!t POLASZEK <[email protected]>
27+
*
28+
* Resource with an Uid-based ID
29+
*/
30+
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
31+
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
32+
#[ODM\Document]
33+
class UidBasedId
34+
{
35+
#[ODM\Id(strategy: 'none')]
36+
public Ulid $id;
37+
38+
public function __construct(?Ulid $id)
39+
{
40+
$this->id = $id ?? new Ulid();
41+
}
42+
}
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\SearchFilter;
17+
use ApiPlatform\Metadata\ApiFilter;
18+
use ApiPlatform\Metadata\ApiResource;
19+
use ApiPlatform\Metadata\Get;
20+
use ApiPlatform\Metadata\GetCollection;
21+
use ApiPlatform\Metadata\Post;
22+
use Doctrine\ORM\Mapping as ORM;
23+
use Symfony\Component\Uid\Ulid;
24+
25+
/**
26+
* @author Beno!t POLASZEK <[email protected]>
27+
*
28+
* Resource with an Uid-based ID
29+
*/
30+
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
31+
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
32+
#[ORM\Entity]
33+
class UidBasedId
34+
{
35+
#[ORM\Column(type: 'ulid')]
36+
#[ORM\Id]
37+
#[ORM\GeneratedValue(strategy: 'NONE')]
38+
public Ulid $id;
39+
40+
public function __construct(?Ulid $id)
41+
{
42+
$this->id = $id ?? new Ulid();
43+
}
44+
}

0 commit comments

Comments
 (0)