From 620637b676cbf4a0fef08cd3caade522c5814b39 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 26 Dec 2017 15:22:04 +0100 Subject: [PATCH 1/7] Add a Fourth Level Entity in fixtures --- .../TestBundle/Entity/FourthLevel.php | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 tests/Fixtures/TestBundle/Entity/FourthLevel.php diff --git a/tests/Fixtures/TestBundle/Entity/FourthLevel.php b/tests/Fixtures/TestBundle/Entity/FourthLevel.php new file mode 100644 index 00000000000..c2bbe297d29 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/FourthLevel.php @@ -0,0 +1,70 @@ + + * + * 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\Core\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiResource; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; + +/** + * Fourth Level. + * + * @author Alan Poulain + * + * @ApiResource + * @ORM\Entity + */ +class FourthLevel +{ + /** + * @var int The id + * + * @ORM\Column(type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id; + + /** + * @var int + * + * @ORM\Column(type = "integer") + * @Groups({"barcelona", "chicago"}) + */ + private $level = 4; + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return int + */ + public function getLevel() + { + return $this->level; + } + + /** + * @param int $level + */ + public function setLevel($level) + { + $this->level = $level; + } +} From 8c705b701ffa0644c36218f1c52f519a496ebbfa Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 26 Dec 2017 15:36:47 +0100 Subject: [PATCH 2/7] Add the fourth level to the third level --- features/main/relation.feature | 10 ++- features/main/subresource.feature | 76 ++++++++++++++----- .../Fixtures/TestBundle/Entity/ThirdLevel.php | 24 ++++++ 3 files changed, 87 insertions(+), 23 deletions(-) diff --git a/features/main/relation.feature b/features/main/relation.feature index d31e8186201..e46678c4051 100644 --- a/features/main/relation.feature +++ b/features/main/relation.feature @@ -19,6 +19,7 @@ Feature: Relations support "@context": "/contexts/ThirdLevel", "@id": "/third_levels/1", "@type": "ThirdLevel", + "fourthLevel": null, "id": 1, "level": 3, "test": true @@ -64,7 +65,11 @@ Feature: Relations support "name": null, "symfony": "symfony", "dummyDate": null, - "thirdLevel": "/third_levels/1", + "thirdLevel": { + "@id": "/third_levels/1", + "@type": "ThirdLevel", + "fourthLevel": null + }, "relatedToDummyFriend": [], "dummyBoolean": null, "embeddedDummy": null, @@ -258,7 +263,8 @@ Feature: Relations support "thirdLevel": { "@id": "/third_levels/1", "@type": "ThirdLevel", - "level": 3 + "level": 3, + "fourthLevel": null } } } diff --git a/features/main/subresource.feature b/features/main/subresource.feature index 20de44594cc..8395012ef20 100644 --- a/features/main/subresource.feature +++ b/features/main/subresource.feature @@ -12,15 +12,15 @@ Feature: Subresource support And the JSON should be equal to: """ { - "@context": "/contexts/Answer", - "@id": "/answers/1", - "@type": "Answer", - "id": 1, - "content": "42", - "question": "/questions/1", - "relatedQuestions": [ - "/questions/1" - ] + "@context": "/contexts/Answer", + "@id": "/answers/1", + "@type": "Answer", + "id": 1, + "content": "42", + "question": "/questions/1", + "relatedQuestions": [ + "/questions/1" + ] } """ @@ -35,23 +35,43 @@ Feature: Subresource support "@id": "/questions/1/answer/related_questions", "@type": "hydra:Collection", "hydra:member": [ - { - "@id": "/questions/1", - "@type": "Question", - "content": "What's the answer to the Ultimate Question of Life, the Universe and Everything?", - "id": 1, - "answer": "/answers/1" - } + { + "@id": "/questions/1", + "@type": "Question", + "content": "What's the answer to the Ultimate Question of Life, the Universe and Everything?", + "id": 1, + "answer": "/answers/1" + } ], "hydra:totalItems": 1 } """ + Scenario: Create a fourth level + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/fourth_levels" with body: + """ + {"level": 4} + """ + Then the response status code should be 201 + 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 equal to: + """ + { + "@context": "/contexts/FourthLevel", + "@id": "/fourth_levels/1", + "@type": "FourthLevel", + "id": 1, + "level": 4 + } + """ + Scenario: Create a third level When I add "Content-Type" header equal to "application/ld+json" And I send a "POST" request to "/third_levels" with body: """ - {"level": 3} + {"level": 3, "fourthLevel": "/fourth_levels/1"} """ Then the response status code should be 201 And the response should be in JSON @@ -62,6 +82,7 @@ Feature: Subresource support "@context": "/contexts/ThirdLevel", "@id": "/third_levels/1", "@type": "ThirdLevel", + "fourthLevel": "/fourth_levels/1", "id": 1, "level": 3, "test": true @@ -125,7 +146,11 @@ Feature: Subresource support "name": "Hello", "symfony": "symfony", "dummyDate": null, - "thirdLevel": "/third_levels/1", + "thirdLevel": { + "@id": "/third_levels/1", + "@type": "ThirdLevel", + "fourthLevel": "/fourth_levels/1" + }, "relatedToDummyFriend": [], "dummyBoolean": null, "embeddedDummy": [], @@ -138,7 +163,11 @@ Feature: Subresource support "name": null, "symfony": "symfony", "dummyDate": null, - "thirdLevel": "/third_levels/1", + "thirdLevel": { + "@id": "/third_levels/1", + "@type": "ThirdLevel", + "fourthLevel": "/fourth_levels/1" + }, "relatedToDummyFriend": [], "dummyBoolean": null, "embeddedDummy": [], @@ -193,7 +222,11 @@ Feature: Subresource support "name": "Hello", "symfony": "symfony", "dummyDate": null, - "thirdLevel": "/third_levels/1", + "thirdLevel": { + "@id": "/third_levels/1", + "@type": "ThirdLevel", + "fourthLevel": "/fourth_levels/1" + }, "relatedToDummyFriend": [], "dummyBoolean": null, "embeddedDummy": [], @@ -233,7 +266,7 @@ Feature: Subresource support } """ - Scenario: Get the embedded relation collection + Scenario: Get the embedded relation collection at the third level When I send a "GET" request to "/dummies/1/related_dummies/1/third_level" And the response status code should be 200 And the response should be in JSON @@ -244,6 +277,7 @@ Feature: Subresource support "@context": "/contexts/ThirdLevel", "@id": "/third_levels/1", "@type": "ThirdLevel", + "fourthLevel": "/fourth_levels/1", "id": 1, "level": 3, "test": true diff --git a/tests/Fixtures/TestBundle/Entity/ThirdLevel.php b/tests/Fixtures/TestBundle/Entity/ThirdLevel.php index d5b39be96ed..960bb58da91 100644 --- a/tests/Fixtures/TestBundle/Entity/ThirdLevel.php +++ b/tests/Fixtures/TestBundle/Entity/ThirdLevel.php @@ -14,6 +14,7 @@ namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity; use ApiPlatform\Core\Annotation\ApiResource; +use ApiPlatform\Core\Annotation\ApiSubresource; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; @@ -51,6 +52,13 @@ class ThirdLevel */ private $test = true; + /** + * @ApiSubresource + * @ORM\ManyToOne(targetEntity="FourthLevel", cascade={"persist"}) + * @Groups({"barcelona", "chicago", "friends"}) + */ + public $fourthLevel; + /** * @return int */ @@ -90,4 +98,20 @@ public function setTest($test) { $this->test = $test; } + + /** + * @return FourthLevel|null + */ + public function getFourthLevel() + { + return $this->fourthLevel; + } + + /** + * @param FourthLevel|null $fourthLevel + */ + public function setFourthLevel(FourthLevel $fourthLevel = null) + { + $this->fourthLevel = $fourthLevel; + } } From c2cbed3d5401597dd428634f116be153e2f5b230 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 26 Dec 2017 15:37:08 +0100 Subject: [PATCH 3/7] Add the breaking scenario --- features/main/subresource.feature | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/features/main/subresource.feature b/features/main/subresource.feature index 8395012ef20..a56bceb29a9 100644 --- a/features/main/subresource.feature +++ b/features/main/subresource.feature @@ -284,6 +284,22 @@ Feature: Subresource support } """ + Scenario: Get the embedded relation collection at the fourth level + When I send a "GET" request to "/dummies/1/related_dummies/1/third_level/fourth_level" + And 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 equal to: + """ + { + "@context": "/contexts/FourthLevel", + "@id": "/fourth_levels/1", + "@type": "FourthLevel", + "id": 1, + "level": 4 + } + """ + Scenario: Get offers subresource from aggregate offers subresource Given I have a product with offers When I send a "GET" request to "/dummy_products/2/offers/1/offers" From 3ef7c9e556e03552597db393d5c52adc59675053 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 26 Dec 2017 16:43:15 +0100 Subject: [PATCH 4/7] Fix the query build of recursive subresources --- .../Doctrine/Orm/SubresourceDataProvider.php | 152 +++++++++--------- .../Orm/SubresourceDataProviderTest.php | 38 ++--- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php index a5f6842dbc4..c8b0b9876b5 100644 --- a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php +++ b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php @@ -28,6 +28,7 @@ use Doctrine\Common\Persistence\ManagerRegistry; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\QueryBuilder; /** * Subresource data provider for the Doctrine ORM. @@ -79,82 +80,10 @@ public function getSubresource(string $resourceClass, array $identifiers, array throw new ResourceClassNotSupportedException('The given resource class is not a subresource.'); } - $originAlias = 'o'; - $queryBuilder = $repository->createQueryBuilder($originAlias); $queryNameGenerator = new QueryNameGenerator(); - $previousQueryBuilder = null; - $previousAlias = null; - - $num = \count($context['identifiers']); - - while ($num--) { - list($identifier, $identifierResourceClass) = $context['identifiers'][$num]; - $previousAssociationProperty = $context['identifiers'][$num + 1][0] ?? $context['property']; - - $manager = $this->managerRegistry->getManagerForClass($identifierResourceClass); - - if (!$manager instanceof EntityManagerInterface) { - throw new RuntimeException("The manager for $identifierResourceClass must be an EntityManager."); - } - - $classMetadata = $manager->getClassMetadata($identifierResourceClass); - - if (!$classMetadata instanceof ClassMetadataInfo) { - throw new RuntimeException("The class metadata for $identifierResourceClass must be an instance of ClassMetadataInfo."); - } - - $qb = $manager->createQueryBuilder(); - $alias = $queryNameGenerator->generateJoinAlias($identifier); - $relationType = $classMetadata->getAssociationMapping($previousAssociationProperty)['type']; - $normalizedIdentifiers = isset($identifiers[$identifier]) ? $this->normalizeIdentifiers($identifiers[$identifier], $manager, $identifierResourceClass) : []; - - switch ($relationType) { - //MANY_TO_MANY relations need an explicit join so that the identifier part can be retrieved - case ClassMetadataInfo::MANY_TO_MANY: - $joinAlias = $queryNameGenerator->generateJoinAlias($previousAssociationProperty); - - $qb->select($joinAlias) - ->from($identifierResourceClass, $alias) - ->innerJoin("$alias.$previousAssociationProperty", $joinAlias); - - break; - case ClassMetadataInfo::ONE_TO_MANY: - $mappedBy = $classMetadata->getAssociationMapping($previousAssociationProperty)['mappedBy']; - - // first pass, o.property instead of alias.property - if (null === $previousQueryBuilder) { - $originAlias = "$originAlias.$mappedBy"; - } else { - $previousAlias = "$previousAlias.$mappedBy"; - } - - $qb->select($alias) - ->from($identifierResourceClass, $alias); - break; - default: - $qb->select("IDENTITY($alias.$previousAssociationProperty)") - ->from($identifierResourceClass, $alias); - } - - // Add where clause for identifiers - foreach ($normalizedIdentifiers as $key => $value) { - $placeholder = $queryNameGenerator->generateParameterName($key); - $qb->andWhere("$alias.$key = :$placeholder"); - $queryBuilder->setParameter($placeholder, $value); - } - - // recurse queries - if (null === $previousQueryBuilder) { - $previousQueryBuilder = $qb; - } else { - $previousQueryBuilder->andWhere($qb->expr()->in($previousAlias, $qb->getDQL())); - } - - $previousAlias = $alias; - } /* - * The following translate to this pseudo-dql: + * The following recursively translates to this pseudo-dql: * * SELECT thirdLevel WHERE thirdLevel IN ( * SELECT thirdLevel FROM relatedDummies WHERE relatedDummies = ? AND relatedDummies IN ( @@ -164,9 +93,7 @@ public function getSubresource(string $resourceClass, array $identifiers, array * * By using subqueries, we're forcing the SQL execution plan to go through indexes on doctrine identifiers. */ - $queryBuilder->where( - $queryBuilder->expr()->in($originAlias, $previousQueryBuilder->getDQL()) - ); + $queryBuilder = $this->buildQuery($identifiers, $context, $queryNameGenerator, $repository->createQueryBuilder($alias = 'o'), $alias, \count($context['identifiers'])); if (true === $context['collection']) { foreach ($this->collectionExtensions as $extension) { @@ -195,4 +122,77 @@ public function getSubresource(string $resourceClass, array $identifiers, array return $context['collection'] ? $query->getResult() : $query->getOneOrNullResult(); } + + /** + * @throws RuntimeException + */ + private function buildQuery(array $identifiers, array $context, QueryNameGenerator $queryNameGenerator, QueryBuilder $previousQueryBuilder, string $previousAlias, int $remainingIdentifiers, QueryBuilder $topQueryBuilder = null): QueryBuilder + { + if (!$remainingIdentifiers) { + return $previousQueryBuilder; + } + + $topQueryBuilder = $topQueryBuilder ?? $previousQueryBuilder; + + list($identifier, $identifierResourceClass) = $context['identifiers'][$remainingIdentifiers - 1]; + $previousAssociationProperty = $context['identifiers'][$remainingIdentifiers][0] ?? $context['property']; + + $manager = $this->managerRegistry->getManagerForClass($identifierResourceClass); + + if (!$manager instanceof EntityManagerInterface) { + throw new RuntimeException("The manager for $identifierResourceClass must be an EntityManager."); + } + + $classMetadata = $manager->getClassMetadata($identifierResourceClass); + + if (!$classMetadata instanceof ClassMetadataInfo) { + throw new RuntimeException( + "The class metadata for $identifierResourceClass must be an instance of ClassMetadataInfo." + ); + } + + $qb = $manager->createQueryBuilder(); + $alias = $queryNameGenerator->generateJoinAlias($identifier); + $relationType = $classMetadata->getAssociationMapping($previousAssociationProperty)['type']; + $normalizedIdentifiers = isset($identifiers[$identifier]) ? $this->normalizeIdentifiers( + $identifiers[$identifier], + $manager, + $identifierResourceClass + ) : []; + + switch ($relationType) { + // MANY_TO_MANY relations need an explicit join so that the identifier part can be retrieved + case ClassMetadataInfo::MANY_TO_MANY: + $joinAlias = $queryNameGenerator->generateJoinAlias($previousAssociationProperty); + + $qb->select($joinAlias) + ->from($identifierResourceClass, $alias) + ->innerJoin("$alias.$previousAssociationProperty", $joinAlias); + + break; + case ClassMetadataInfo::ONE_TO_MANY: + $mappedBy = $classMetadata->getAssociationMapping($previousAssociationProperty)['mappedBy']; + + $previousAlias = "$previousAlias.$mappedBy"; + + $qb->select($alias) + ->from($identifierResourceClass, $alias); + break; + default: + $qb->select("IDENTITY($alias.$previousAssociationProperty)") + ->from($identifierResourceClass, $alias); + } + + // Add where clause for identifiers + foreach ($normalizedIdentifiers as $key => $value) { + $placeholder = $queryNameGenerator->generateParameterName($key); + $qb->andWhere("$alias.$key = :$placeholder"); + $topQueryBuilder->setParameter($placeholder, $value); + } + + // Recurse queries + $qb = $this->buildQuery($identifiers, $context, $queryNameGenerator, $qb, $alias, --$remainingIdentifiers, $topQueryBuilder); + + return $previousQueryBuilder->andWhere($qb->expr()->in($previousAlias, $qb->getDQL())); + } } diff --git a/tests/Bridge/Doctrine/Orm/SubresourceDataProviderTest.php b/tests/Bridge/Doctrine/Orm/SubresourceDataProviderTest.php index 74f3cea4f0f..af25770cb52 100644 --- a/tests/Bridge/Doctrine/Orm/SubresourceDataProviderTest.php +++ b/tests/Bridge/Doctrine/Orm/SubresourceDataProviderTest.php @@ -129,11 +129,7 @@ public function testGetSubresource() $funcProphecy = $this->prophesize(Func::class); $func = $funcProphecy->reveal(); - $exprProphecy = $this->prophesize(Expr::class); - $exprProphecy->in('o', $dql)->willReturn($func)->shouldBeCalled(); - - $queryBuilder->expr()->shouldBeCalled()->willReturn($exprProphecy->reveal()); - $queryBuilder->where($func)->shouldBeCalled()->willReturn($queryBuilder); + $queryBuilder->andWhere($func)->shouldBeCalled()->willReturn($queryBuilder); $queryBuilder->getQuery()->shouldBeCalled()->willReturn($queryProphecy->reveal()); @@ -158,6 +154,11 @@ public function testGetSubresource() $qb->andWhere('id_a1.id = :id_p1')->shouldBeCalled()->willReturn($qb); $qb->getDQL()->shouldBeCalled()->willReturn($dql); + $exprProphecy = $this->prophesize(Expr::class); + $exprProphecy->in('o', $dql)->willReturn($func)->shouldBeCalled(); + + $qb->expr()->shouldBeCalled()->willReturn($exprProphecy->reveal()); + $managerProphecy->createQueryBuilder()->shouldBeCalled()->willReturn($qb->reveal()); $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); @@ -177,6 +178,8 @@ public function testGetSubSubresourceItem() { $managerRegistryProphecy = $this->prophesize(ManagerRegistry::class); $identifiers = ['id']; + $funcProphecy = $this->prophesize(Func::class); + $func = $funcProphecy->reveal(); // First manager (Dummy) $dummyDQL = 'SELECT relatedDummies_a3 FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy id_a2 INNER JOIN id_a2.relatedDummies relatedDummies_a3 WHERE id_a2.id = :id_p2'; @@ -218,6 +221,11 @@ public function testGetSubSubresourceItem() $rqb->andWhere($dummyFunc)->shouldBeCalled()->willReturn($rqb); $rqb->getDQL()->shouldBeCalled()->willReturn($relatedDQL); + $relatedExpProphecy = $this->prophesize(Expr::class); + $relatedExpProphecy->in('o', $relatedDQL)->willReturn($func)->shouldBeCalled(); + + $rqb->expr()->shouldBeCalled()->willReturn($relatedExpProphecy->reveal()); + $rClassMetadataProphecy = $this->prophesize(ClassMetadata::class); $rClassMetadataProphecy->getIdentifier()->shouldBeCalled()->willReturn($identifiers); $rClassMetadataProphecy->getTypeOfField('id')->shouldBeCalled()->willReturn('integer'); @@ -237,14 +245,7 @@ public function testGetSubSubresourceItem() $queryBuilder = $this->prophesize(QueryBuilder::class); - $funcProphecy = $this->prophesize(Func::class); - $func = $funcProphecy->reveal(); - - $exprProphecy = $this->prophesize(Expr::class); - $exprProphecy->in('o', $relatedDQL)->willReturn($func)->shouldBeCalled(); - - $queryBuilder->expr()->shouldBeCalled()->willReturn($exprProphecy->reveal()); - $queryBuilder->where($func)->shouldBeCalled()->willReturn($queryBuilder); + $queryBuilder->andWhere($func)->shouldBeCalled()->willReturn($queryBuilder); $queryBuilder->getQuery()->shouldBeCalled()->willReturn($queryProphecy->reveal()); $queryBuilder->setParameter('id_p1', 1)->shouldBeCalled()->willReturn($queryBuilder); @@ -277,11 +278,7 @@ public function testQueryResultExtension() $funcProphecy = $this->prophesize(Func::class); $func = $funcProphecy->reveal(); - $exprProphecy = $this->prophesize(Expr::class); - $exprProphecy->in('o', $dql)->willReturn($func)->shouldBeCalled(); - - $queryBuilder->expr()->shouldBeCalled()->willReturn($exprProphecy->reveal()); - $queryBuilder->where($func)->shouldBeCalled()->willReturn($queryBuilder); + $queryBuilder->andWhere($func)->shouldBeCalled()->willReturn($queryBuilder); $repositoryProphecy = $this->prophesize(EntityRepository::class); $repositoryProphecy->createQueryBuilder('o')->shouldBeCalled()->willReturn($queryBuilder->reveal()); @@ -305,6 +302,11 @@ public function testQueryResultExtension() $qb->andWhere('id_a1.id = :id_p1')->shouldBeCalled()->willReturn($qb); $qb->getDQL()->shouldBeCalled()->willReturn($dql); + $exprProphecy = $this->prophesize(Expr::class); + $exprProphecy->in('o', $dql)->willReturn($func)->shouldBeCalled(); + + $qb->expr()->shouldBeCalled()->willReturn($exprProphecy->reveal()); + $managerProphecy->createQueryBuilder()->shouldBeCalled()->willReturn($qb->reveal()); $this->assertIdentifierManagerMethodCalls($managerProphecy); From 5b23e18e4aa29d80018a7db26403ec685352f0a5 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Tue, 26 Dec 2017 18:06:25 +0100 Subject: [PATCH 5/7] Remove useless strategy in GeneratedValue annotation --- tests/Fixtures/TestBundle/Entity/FourthLevel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Fixtures/TestBundle/Entity/FourthLevel.php b/tests/Fixtures/TestBundle/Entity/FourthLevel.php index c2bbe297d29..7eeb1187bce 100644 --- a/tests/Fixtures/TestBundle/Entity/FourthLevel.php +++ b/tests/Fixtures/TestBundle/Entity/FourthLevel.php @@ -32,7 +32,7 @@ class FourthLevel * * @ORM\Column(type="integer") * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") + * @ORM\GeneratedValue */ private $id; From f71432d68537ee61bdceee9cfb3d885b86e7a635 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Wed, 27 Dec 2017 12:31:46 +0100 Subject: [PATCH 6/7] Useless new lines --- src/Bridge/Doctrine/Orm/SubresourceDataProvider.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php index c8b0b9876b5..84c59c790fd 100644 --- a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php +++ b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php @@ -168,11 +168,9 @@ private function buildQuery(array $identifiers, array $context, QueryNameGenerat $qb->select($joinAlias) ->from($identifierResourceClass, $alias) ->innerJoin("$alias.$previousAssociationProperty", $joinAlias); - break; case ClassMetadataInfo::ONE_TO_MANY: $mappedBy = $classMetadata->getAssociationMapping($previousAssociationProperty)['mappedBy']; - $previousAlias = "$previousAlias.$mappedBy"; $qb->select($alias) From 1a6aa73a96b494d19cec47023466780c5f0862b4 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Wed, 27 Dec 2017 13:54:19 +0100 Subject: [PATCH 7/7] Use comparison instead of negation --- src/Bridge/Doctrine/Orm/SubresourceDataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php index 84c59c790fd..34557ca8884 100644 --- a/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php +++ b/src/Bridge/Doctrine/Orm/SubresourceDataProvider.php @@ -128,7 +128,7 @@ public function getSubresource(string $resourceClass, array $identifiers, array */ private function buildQuery(array $identifiers, array $context, QueryNameGenerator $queryNameGenerator, QueryBuilder $previousQueryBuilder, string $previousAlias, int $remainingIdentifiers, QueryBuilder $topQueryBuilder = null): QueryBuilder { - if (!$remainingIdentifiers) { + if ($remainingIdentifiers <= 0) { return $previousQueryBuilder; }