From 77f6d757102a1842556b26d4b27ef7e26319e84f Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 13 Nov 2026 11:45:02 +0100 Subject: [PATCH 1/2] test(state): unresolvable IRI on IriFilter Co-authored-by: CRC <46287826+pysocrates@users.noreply.github.com> --- tests/Functional/Parameters/IriFilterTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Functional/Parameters/IriFilterTest.php b/tests/Functional/Parameters/IriFilterTest.php index e91ecb88b08..4a1c5e9267c 100644 --- a/tests/Functional/Parameters/IriFilterTest.php +++ b/tests/Functional/Parameters/IriFilterTest.php @@ -69,6 +69,13 @@ public function testIriFilter(): void $this->assertCount(0, $res['member']); } + public function testIriFilterWithUnresolvableIri(): void + { + $res = self::createClient()->request('GET', '/chickens?chickenCoop=/invalid/iri')->toArray(false); + $this->assertResponseIsSuccessful(); + $this->assertCount(0, $res['member']); + } + public function testIriFilterMultiple(): void { $iris = $this->getIris(); From b5068817e4bf00f3899287593009d64ae09d69e7 Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 13 Nov 2026 11:45:51 +0100 Subject: [PATCH 2/2] fix(state): ignore unresolvable IRIs in IriConverterParameterProvider Co-authored-by: CRC <46287826+pysocrates@users.noreply.github.com> --- src/Doctrine/Odm/Filter/IriFilter.php | 22 +++----- .../IriConverterParameterProvider.php | 54 ++++++------------- 2 files changed, 25 insertions(+), 51 deletions(-) diff --git a/src/Doctrine/Odm/Filter/IriFilter.php b/src/Doctrine/Odm/Filter/IriFilter.php index 5ab86ef92fb..4eb9acb4d7f 100644 --- a/src/Doctrine/Odm/Filter/IriFilter.php +++ b/src/Doctrine/Odm/Filter/IriFilter.php @@ -92,13 +92,17 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, ?Opera $method = $classMetadata->isSingleValuedAssociation($leafProperty) ? 'references' : 'includesReferenceTo'; if (is_iterable($value)) { + $value = \is_array($value) ? $value : iterator_to_array($value); + + if ([] === $value) { + $match->{$operator}($aggregationBuilder->matchExpr()->field($matchField)->in([])); + + return; + } + $or = $aggregationBuilder->matchExpr(); foreach ($value as $v) { - if (!\is_object($v)) { - continue; - } - $or->addOr($aggregationBuilder->matchExpr()->field($matchField)->{$method}($v)); } @@ -107,16 +111,6 @@ public function apply(Builder $aggregationBuilder, string $resourceClass, ?Opera return; } - // The IRI did not resolve to a resource: emit an always-false clause so the query - // returns no result rather than attempting to match against a raw IRI string. - if (!\is_object($value)) { - $match->{$operator}( - $aggregationBuilder->matchExpr()->field($matchField)->in([]) - ); - - return; - } - $match ->{$operator}( $aggregationBuilder diff --git a/src/State/ParameterProvider/IriConverterParameterProvider.php b/src/State/ParameterProvider/IriConverterParameterProvider.php index e7b2309e484..a1fe8c5cde0 100644 --- a/src/State/ParameterProvider/IriConverterParameterProvider.php +++ b/src/State/ParameterProvider/IriConverterParameterProvider.php @@ -36,7 +36,8 @@ public function __construct( public function provide(Parameter $parameter, array $parameters = [], array $context = []): ?Operation { $operation = $context['operation'] ?? null; - if (!($value = $parameter->getValue()) || $value instanceof ParameterNotFound) { + $value = $parameter->getValue(); + if (null === $value || $value instanceof ParameterNotFound) { return $operation; } @@ -46,47 +47,26 @@ public function provide(Parameter $parameter, array $parameters = [], array $con 'map_resource' => $extraProperties['map_resource'] ?? (null === $parameter->getFilter()), ]; - if (\is_array($value)) { - $entities = []; - foreach ($value as $v) { - try { - $entities[] = $this->iriConverter->getResourceFromIri($v, $iriConverterContext); - } catch (InvalidArgumentException|ItemNotFoundException $exception) { - if ($exception instanceof ItemNotFoundException && true === ($extraProperties['throw_not_found'] ?? false)) { - throw $exception; - } - - $this->logger->error( - message: 'Operation failed due to an invalid argument or a missing item', - context: [ - 'exception' => $exception->getMessage(), - ] - ); - - break; + $resources = []; + foreach (\is_array($value) ? $value : [$value] as $iri) { + try { + $resources[] = $this->iriConverter->getResourceFromIri((string) $iri, $iriConverterContext); + } catch (InvalidArgumentException $exception) { + if ($exception instanceof ItemNotFoundException && true === ($extraProperties['throw_not_found'] ?? false)) { + throw $exception; } - } - $parameter->setValue($entities); - - return $operation; - } - - try { - $parameter->setValue($this->iriConverter->getResourceFromIri($value, $iriConverterContext)); - } catch (InvalidArgumentException|ItemNotFoundException $exception) { - if ($exception instanceof ItemNotFoundException && true === ($extraProperties['throw_not_found'] ?? false)) { - throw $exception; + $this->logger->notice( + message: 'Unable to resolve an IRI parameter value', + context: [ + 'exception' => $exception->getMessage(), + ] + ); } - - $this->logger->error( - message: 'Operation failed due to an invalid argument or a missing item', - context: [ - 'exception' => $exception->getMessage(), - ] - ); } + $parameter->setValue(\is_array($value) || [] === $resources ? $resources : $resources[0]); + return $operation; } }