Skip to content

Commit 6cd4adf

Browse files
committed
feat(doctrine): searchfilter using id instead of full IRI when it's not an int
1 parent a774f4c commit 6cd4adf

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

features/doctrine/search_filter.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ Feature: Search filter on collections
10481048
Scenario: Filters can use UUIDs
10491049
Given there is a group object with uuid "61817181-0ecc-42fb-a6e7-d97f2ddcb344" and 2 users
10501050
And there is a group object with uuid "32510d53-f737-4e70-8d9d-58e292c871f8" and 1 users
1051-
When I send a "GET" request to "/issue5735/issue5735_users?groups[]=/issue5735/groups/61817181-0ecc-42fb-a6e7-d97f2ddcb344&groups[]=/issue5735/groups/32510d53-f737-4e70-8d9d-58e292c871f8"
1051+
When I send a "GET" request to "/issue5735/issue5735_users?groups[]=61817181-0ecc-42fb-a6e7-d97f2ddcb344&groups[]=/issue5735/groups/32510d53-f737-4e70-8d9d-58e292c871f8"
10521052
Then the response status code should be 200
10531053
And the response should be in JSON
10541054
And the JSON node "hydra:totalItems" should be equal to 3

src/Doctrine/Orm/Filter/SearchFilter.php

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
2222
use ApiPlatform\Exception\InvalidArgumentException;
2323
use ApiPlatform\Metadata\Operation;
24+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
25+
use ApiPlatform\Metadata\ResourceClassResolverInterface;
26+
use ApiPlatform\Metadata\Util\ResourceClassInfoTrait;
2427
use Doctrine\DBAL\Types\Types;
2528
use Doctrine\ORM\Query\Expr\Join;
2629
use Doctrine\ORM\QueryBuilder;
@@ -133,17 +136,29 @@
133136
*/
134137
final class SearchFilter extends AbstractFilter implements SearchFilterInterface
135138
{
139+
use ResourceClassInfoTrait;
136140
use SearchFilterTrait;
137141

138142
public const DOCTRINE_INTEGER_TYPE = Types::INTEGER;
139143

140-
public function __construct(ManagerRegistry $managerRegistry, IriConverterInterface $iriConverter, PropertyAccessorInterface $propertyAccessor = null, LoggerInterface $logger = null, array $properties = null, IdentifiersExtractorInterface $identifiersExtractor = null, NameConverterInterface $nameConverter = null)
141-
{
144+
public function __construct(
145+
ManagerRegistry $managerRegistry,
146+
IriConverterInterface $iriConverter,
147+
PropertyAccessorInterface $propertyAccessor = null,
148+
LoggerInterface $logger = null,
149+
array $properties = null,
150+
IdentifiersExtractorInterface $identifiersExtractor = null,
151+
NameConverterInterface $nameConverter = null,
152+
ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null,
153+
ResourceClassResolverInterface $resourceClassResolver = null
154+
) {
142155
parent::__construct($managerRegistry, $logger, $properties, $nameConverter);
143156

144157
$this->iriConverter = $iriConverter;
145158
$this->identifiersExtractor = $identifiersExtractor;
146159
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
160+
$this->resourceMetadataFactory = $resourceMetadataFactory;
161+
$this->resourceClassResolver = $resourceClassResolver;
147162
}
148163

149164
protected function getIriConverter(): IriConverterInterface
@@ -221,9 +236,25 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
221236
$associationResourceClass = $metadata->getAssociationTargetClass($field);
222237
$associationMetadata = $this->getClassMetadata($associationResourceClass);
223238
$associationFieldIdentifier = $associationMetadata->getIdentifierFieldNames()[0];
239+
$associationResourceApiIdField = $associationFieldIdentifier;
240+
241+
// dig into the subResource metadata to find out which field we're looking at
242+
if ($this->isResourceClass($associationResourceClass) && $this->resourceMetadataFactory) {
243+
$associationApiMetadata = $this->resourceMetadataFactory->create($associationResourceClass);
244+
$it = $associationApiMetadata->getIterator();
245+
$current = $it->current();
246+
$variables = $current->getUriVariables();
247+
if (1 === \count($variables)) { // otherwise let's just give up at this point, too complicated
248+
$varName = array_key_first($variables);
249+
$link = $variables[$varName];
250+
$associationResourceApiIdField = $link->getIdentifiers()[0]; // this will be needed
251+
}
252+
}
253+
224254
$doctrineTypeField = $this->getDoctrineFieldType($associationFieldIdentifier, $associationResourceClass);
255+
$associationRepository = $this->managerRegistry->getManagerForClass($associationResourceClass)?->getRepository($associationResourceClass);
225256

226-
$values = array_map(function ($value) use ($associationFieldIdentifier, $doctrineTypeField) {
257+
$values = array_map(function ($value) use ($associationFieldIdentifier, $associationResourceApiIdField, $doctrineTypeField, $associationRepository) {
227258
if (is_numeric($value)) {
228259
return $value;
229260
}
@@ -232,11 +263,15 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB
232263

233264
return $this->propertyAccessor->getValue($item, $associationFieldIdentifier);
234265
} catch (InvalidArgumentException) {
235-
/*
236-
* Can we do better? This is not the ApiResource the call was made on,
237-
* so we don't get any kind of api metadata for it without (a lot of?) work elsewhere...
238-
* Let's just pretend it's always the ORM id for now.
239-
*/
266+
// replace the value of $associationResourceApiIdField by that of $associationFieldIdentifier if necessary!
267+
if ($associationFieldIdentifier !== $associationResourceApiIdField && $associationRepository) {
268+
// use $associationResourceApiIdField to fetch the entity
269+
$entity = $associationRepository->findOneBy([
270+
$associationResourceApiIdField => $value,
271+
]);
272+
$value = $this->getPropertyAccessor()->getValue($entity, $associationFieldIdentifier);
273+
}
274+
240275
if (!$this->hasValidValues([$value], $doctrineTypeField)) {
241276
$this->logger->notice('Invalid filter ignored', [
242277
'exception' => new InvalidArgumentException(sprintf('Values for field "%s" are not valid according to the doctrine type.', $associationFieldIdentifier)),

0 commit comments

Comments
 (0)