diff --git a/src/Doctrine/Orm/Filter/IriFilter.php b/src/Doctrine/Orm/Filter/IriFilter.php index e340eb01301..a8e3f637f3e 100644 --- a/src/Doctrine/Orm/Filter/IriFilter.php +++ b/src/Doctrine/Orm/Filter/IriFilter.php @@ -68,6 +68,16 @@ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $q return; } + if (is_iterable($value)) { + $value = array_values(array_filter(\is_array($value) ? $value : iterator_to_array($value), is_object(...))); + } + + if ([] === $value || (!\is_array($value) && !\is_object($value))) { + $queryBuilder->{$context['whereClause'] ?? 'andWhere'}('1 = 0'); + + return; + } + // Collection associations (OneToMany/ManyToMany) require a JOIN to compare individual elements. if ($ormLeafMetadata['is_collection_valued']) { if (Join::LEFT_JOIN === $this->joinTypeFor($context)) { diff --git a/src/Doctrine/Orm/Tests/Filter/NestedPropertyJoinTypeTest.php b/src/Doctrine/Orm/Tests/Filter/NestedPropertyJoinTypeTest.php index 6d9ead76b20..3c8f8908419 100644 --- a/src/Doctrine/Orm/Tests/Filter/NestedPropertyJoinTypeTest.php +++ b/src/Doctrine/Orm/Tests/Filter/NestedPropertyJoinTypeTest.php @@ -63,7 +63,7 @@ public function testIriFilterCollectionJoinTypeDependsOnWhereClause(string $wher $queryBuilder = (new QueryBuilder($entityManager))->select('o')->from(Dummy::class, 'o'); $parameter = (new QueryParameter(key: 'relatedDummies', property: 'relatedDummies')) ->withExtraProperties([ - '_api_values' => 'x', + '_api_values' => new RelatedDummy(), 'nested_properties_info' => ['relatedDummies' => [ 'relation_classes' => [], 'converted_relation_segments' => [], diff --git a/tests/Fixtures/TestBundle/Entity/Issue8834Resource.php b/tests/Fixtures/TestBundle/Entity/Issue8834Resource.php new file mode 100644 index 00000000000..c652e72d8da --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue8834Resource.php @@ -0,0 +1,36 @@ + + * + * 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\IriFilter; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\QueryParameter; +use Doctrine\ORM\Mapping as ORM; + +#[ORM\Entity] +#[ApiResource(operations: [new Get(), new GetCollection(parameters: [ + 'related' => new QueryParameter(filter: new IriFilter()), +])])] +class Issue8834Resource +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column] + public ?int $id = null; + + #[ORM\ManyToOne(targetEntity: self::class)] + public ?self $related = null; +} diff --git a/tests/Functional/Parameters/Issue8834InvalidIriTest.php b/tests/Functional/Parameters/Issue8834InvalidIriTest.php new file mode 100644 index 00000000000..bca50e6c21d --- /dev/null +++ b/tests/Functional/Parameters/Issue8834InvalidIriTest.php @@ -0,0 +1,57 @@ + + * + * 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\Functional\Parameters; + +use ApiPlatform\Test\ApiTestCase; +use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue8834Resource; +use ApiPlatform\Tests\RecreateSchemaTrait; +use ApiPlatform\Tests\SetupClassResourcesTrait; + +final class Issue8834InvalidIriTest extends ApiTestCase +{ + use RecreateSchemaTrait; + use SetupClassResourcesTrait; + + protected static ?bool $alwaysBootKernel = false; + + public static function getResources(): array + { + return [Issue8834Resource::class]; + } + + protected function setUp(): void + { + if ('mongodb' === ($_SERVER['APP_ENV'] ?? null)) { + $this->markTestSkipped('This test uses a Doctrine ORM fixture.'); + } + + $this->recreateSchema(self::getResources()); + } + + public function testInvalidIriIsIgnored(): void + { + $manager = $this->getContainer()->get('doctrine')->getManager(); + $related = new Issue8834Resource(); + $manager->persist($related); + $resource = new Issue8834Resource(); + $resource->related = $related; + $manager->persist($resource); + $manager->flush(); + + self::createClient()->request('GET', '/issue8834_resources?related=/invalid/iri'); + + self::assertResponseStatusCodeSame(200); + self::assertJsonContains(['hydra:totalItems' => 0]); + } +}