Skip to content

[RFC] Native UID support in search filter #5618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions features/doctrine/search_filter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,45 @@ Feature: Search filter on collections
}
"""

@createSchema
Scenario: Get collection by ulid 01H2ZS93NBKJW5W4Y01S8TZ43M
Given there is a UidBasedId resource with id "01H2ZS93NBKJW5W4Y01S8TZ43M"
When I send a "GET" request to "/uid_based_ids?id=/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be valid according to this schema:
"""
{
"type": "object",
"properties": {
"@context": {"pattern": "^/contexts/UidBasedId"},
"@id": {"pattern": "^/uid_based_ids"},
"@type": {"pattern": "^hydra:Collection$"},
"hydra:member": {
"type": "array",
"items": {
"type": "object",
"properties": {
"@id": {
"oneOf": [
{"pattern": "^/uid_based_ids/01H2ZS93NBKJW5W4Y01S8TZ43M"}
]
}
}
}
},
"hydra:view": {
"type": "object",
"properties": {
"@id": {"pattern": "^/uid_based_ids\\?id=%2Fuid_based_ids%2F01H2ZS93NBKJW5W4Y01S8TZ43M"},
"@type": {"pattern": "^hydra:PartialCollectionView$"}
}
}
}
}
"""

@createSchema
Scenario: Get collection ordered by a non valid properties
When I send a "GET" request to "/dummies?unknown=0"
Expand Down
18 changes: 16 additions & 2 deletions src/Doctrine/Common/Filter/SearchFilterTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use ApiPlatform\Metadata\IriConverterInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Uid\AbstractUid;

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

if (null === $this->identifiersExtractor) {
return $this->getPropertyAccessor()->getValue($item, 'id');
$id = $this->getPropertyAccessor()->getValue($item, 'id');

return $this->toUuidValue($id);
}

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

return 1 === \count($identifiers) ? array_pop($identifiers) : $identifiers;
if (1 === \count($identifiers)) {
$id = array_pop($identifiers);

return $this->toUuidValue($id);
}

return array_map([$this, 'toUuidValue'], $identifiers);
} catch (InvalidArgumentException) {
// Do nothing, return the raw value
}

return $value;
}

private function toUuidValue(mixed $id): mixed
{
return ($id instanceof AbstractUid) ? $id->toBinary() : $id;
}

/**
* Normalize the values array.
*/
Expand Down
14 changes: 14 additions & 0 deletions tests/Behat/DoctrineContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Document\SoMany as SoManyDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\Taxon as TaxonDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\ThirdLevel as ThirdLevelDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UidBasedId as UidBasedIdDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\UrlEncodedId as UrlEncodedIdDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\User as UserDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\VideoGame as VideoGameDocument;
Expand Down Expand Up @@ -200,6 +201,7 @@
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Taxon;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\ThirdLevel;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\TreeDummy;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UidBasedId;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UrlEncodedId;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\User;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\UuidIdentifierDummy;
Expand All @@ -217,6 +219,7 @@
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Uid\Ulid;
use Symfony\Component\Uid\Uuid as SymfonyUuid;

/**
Expand Down Expand Up @@ -1436,6 +1439,17 @@ public function thereIsAUrlEncodedIdResource(): void
$this->manager->clear();
}

/**
* @Given there is a UidBasedId resource with id :id
*/
public function thereIsAUidBasedIdResource(string $id): void
{
$uidBasedIdResource = ($this->isOrm() ? new UidBasedId(Ulid::fromBase32($id)) : new UidBasedIdDocument(Ulid::fromBase32($id)));
$this->manager->persist($uidBasedIdResource);
$this->manager->flush();
$this->manager->clear();
}

/**
* @Given there is a Program
*/
Expand Down
42 changes: 42 additions & 0 deletions tests/Fixtures/TestBundle/Document/UidBasedId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Uid\Ulid;

/**
* @author Beno!t POLASZEK <bpolaszek@gmail.com>
*
* Resource with an Uid-based ID
*/
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
#[ODM\Document]
class UidBasedId
{
#[ODM\Id(strategy: 'none')]
public Ulid $id;

public function __construct(?Ulid $id)
{
$this->id = $id ?? new Ulid();
}
}
44 changes: 44 additions & 0 deletions tests/Fixtures/TestBundle/Entity/UidBasedId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Ulid;

/**
* @author Beno!t POLASZEK <bpolaszek@gmail.com>
*
* Resource with an Uid-based ID
*/
#[ApiResource(operations: [new Get(), new Post(), new GetCollection()])]
#[ApiFilter(SearchFilter::class, properties: ['id' => 'exact'])]
#[ORM\Entity]
class UidBasedId
{
#[ORM\Column(type: 'ulid')]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'NONE')]
public Ulid $id;

public function __construct(?Ulid $id)
{
$this->id = $id ?? new Ulid();
}
}