Skip to content

Commit

Permalink
Merge 526547b into e8908c6
Browse files Browse the repository at this point in the history
  • Loading branch information
xPolo360 committed Mar 23, 2021
2 parents e8908c6 + 526547b commit 0a4e7da
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 11 deletions.
20 changes: 18 additions & 2 deletions features/doctrine/eager_loading.feature
Expand Up @@ -8,7 +8,7 @@ Feature: Eager Loading
Scenario: Eager loading for a relation
Given there is a RelatedDummy with 2 friends
When I send a "GET" request to "/related_dummies/1"
And the response status code should be 200
Then the response status code should be 200
And the DQL should be equal to:
"""
SELECT o, thirdLevel_a1, relatedToDummyFriend_a2, dummyFriend_a3
Expand Down Expand Up @@ -42,7 +42,7 @@ Feature: Eager Loading
Scenario: Eager loading for a relation and a search filter
Given there is a RelatedDummy with 2 friends
When I send a "GET" request to "/related_dummies?relatedToDummyFriend.dummyFriend=2"
And the response status code should be 200
Then the response status code should be 200
And the DQL should be equal to:
"""
SELECT o, thirdLevel_a4, relatedToDummyFriend_a1, dummyFriend_a5
Expand All @@ -59,6 +59,22 @@ Feature: Eager Loading
ORDER BY o.id ASC
"""

Scenario: Eager loading for a relation and a property filter with multiple relations
Given there is a dummy travel
When I send a "GET" request to "/dummy_travels/1?properties[]=confirmed&properties[car][]=brand&properties[passenger][]=nickname"
Then the response status code should be 200
And the JSON node "confirmed" should be equal to "true"
And the JSON node "car.carBrand" should be equal to "DummyBrand"
And the JSON node "passenger.nickname" should be equal to "Tom"
And the DQL should be equal to:
"""
SELECT o, car_a1, passenger_a2
FROM ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTravel o
LEFT JOIN o.car car_a1
LEFT JOIN o.passenger passenger_a2
WHERE o.id = :id_id
"""

Scenario: Eager loading for a relation with complex sub-query filter
Given there is a RelatedDummy with 2 friends
When I send a "GET" request to "/related_dummies?complex_sub_query_filter=1"
Expand Down
4 changes: 2 additions & 2 deletions features/filter/property_filter.feature
Expand Up @@ -7,14 +7,14 @@ Feature: Set properties to include
Scenario: Test properties filter
Given there are 1 dummy objects with relatedDummy and its thirdLevel
When I send a "GET" request to "/dummies/1?properties[]=name&properties[]=alias&properties[]=relatedDummy&properties[]=name_converted"
And the JSON node "name" should be equal to "Dummy #1"
Then the JSON node "name" should be equal to "Dummy #1"
And the JSON node "alias" should be equal to "Alias #0"
And the JSON node "relatedDummies" should not exist
And the JSON node "name_converted" should exist

Scenario: Test relation embedding
When I send a "GET" request to "/dummies/1?properties[]=name&properties[]=alias&properties[relatedDummy][]=name"
And the JSON node "name" should be equal to "Dummy #1"
Then the JSON node "name" should be equal to "Dummy #1"
And the JSON node "alias" should be equal to "Alias #0"
And the JSON node "relatedDummy.name" should be equal to "RelatedDummy #1"
And the JSON node "relatedDummies" should not exist
7 changes: 4 additions & 3 deletions src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php
Expand Up @@ -167,10 +167,11 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
continue;
}

// prepare the child context
$childNormalizationContext = $normalizationContext;
if (isset($normalizationContext[AbstractNormalizer::ATTRIBUTES])) {
if ($inAttributes = isset($normalizationContext[AbstractNormalizer::ATTRIBUTES][$association])) {
// prepare the child context
$normalizationContext[AbstractNormalizer::ATTRIBUTES] = $normalizationContext[AbstractNormalizer::ATTRIBUTES][$association];
$childNormalizationContext[AbstractNormalizer::ATTRIBUTES] = $normalizationContext[AbstractNormalizer::ATTRIBUTES][$association];
}
} else {
$inAttributes = null;
Expand Down Expand Up @@ -236,7 +237,7 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt
}
}

$this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $fetchPartial, $associationAlias, $options, $normalizationContext, $isLeftJoin, $joinCount, $currentDepth);
$this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $fetchPartial, $associationAlias, $options, $childNormalizationContext, $isLeftJoin, $joinCount, $currentDepth);
}
}

Expand Down
44 changes: 44 additions & 0 deletions tests/Behat/DoctrineContext.php
Expand Up @@ -48,9 +48,11 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyGroup as DummyGroupDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyMercure as DummyMercureDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyOffer as DummyOfferDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyPassenger as DummyPassengerDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyProduct as DummyProductDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyProperty as DummyPropertyDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyTableInheritanceNotApiResourceChild as DummyTableInheritanceNotApiResourceChildDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyTravel as DummyTravelDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\EmbeddableDummy as EmbeddableDummyDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\EmbeddedDummy as EmbeddedDummyDocument;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\FileConfigDummy as FileConfigDummyDocument;
Expand Down Expand Up @@ -117,9 +119,11 @@
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyImmutableDate;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyMercure;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyOffer;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyPassenger;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProduct;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyProperty;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceNotApiResourceChild;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTravel;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddableDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\EmbeddedDummy;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ExternalUser;
Expand Down Expand Up @@ -1093,6 +1097,30 @@ public function thereIsAFooEntityWithRelatedBars()
$this->manager->flush();
}

/**
* @Given there is a dummy travel
*/
public function thereIsADummyTravel()
{
$car = $this->buildDummyCar();
$car->setName('model x');
$car->setCanSell(true);
$car->setAvailableAt(new \DateTime());
$this->manager->persist($car);

$passenger = $this->buildDummyPassenger();
$passenger->nickname = 'Tom';
$this->manager->persist($passenger);

$travel = $this->buildDummyTravel();
$travel->car = $car;
$travel->passenger = $passenger;
$travel->confirmed = true;
$this->manager->persist($travel);

$this->manager->flush();
}

/**
* @Given there is a RelatedDummy with :nb friends
*/
Expand Down Expand Up @@ -1830,6 +1858,22 @@ private function buildDummyCarColor()
return $this->isOrm() ? new DummyCarColor() : new DummyCarColorDocument();
}

/**
* @return DummyPassenger|DummyPassengerDocument
*/
private function buildDummyPassenger()
{
return $this->isOrm() ? new DummyPassenger() : new DummyPassengerDocument();
}

/**
* @return DummyTravel|DummyTravelDocument
*/
private function buildDummyTravel()
{
return $this->isOrm() ? new DummyTravel() : new DummyTravelDocument();
}

/**
* @return DummyDate|DummyDateDocument
*/
Expand Down
Expand Up @@ -693,6 +693,7 @@ public function testAttributes()
$relationPropertyMetadata = new PropertyMetadata();
$relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);

$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', ['serializer_groups' => ['foo']])->willReturn($relationPropertyMetadata)->shouldBeCalled();
$propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', ['serializer_groups' => ['foo']])->willReturn($relationPropertyMetadata)->shouldBeCalled();

$idPropertyMetadata = new PropertyMetadata();
Expand All @@ -707,6 +708,7 @@ public function testAttributes()

$classMetadataProphecy = $this->prophesize(ClassMetadata::class);
$classMetadataProphecy->associationMappings = [
'relatedDummies' => ['fetch' => ClassMetadataInfo::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
'relatedDummy' => ['fetch' => ClassMetadataInfo::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
];

Expand All @@ -727,8 +729,10 @@ public function testAttributes()
$queryBuilderProphecy->getRootAliases()->willReturn(['o']);
$queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);

$queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1);
$queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name}')->shouldBeCalledTimes(1);
$queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a1')->shouldBeCalledTimes(1);
$queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a2')->shouldBeCalledTimes(1);
$queryBuilderProphecy->addSelect('partial relatedDummies_a1.{id,name}')->shouldBeCalledTimes(1);
$queryBuilderProphecy->addSelect('partial relatedDummy_a2.{id,name}')->shouldBeCalledTimes(1);
$queryBuilderProphecy->getDQLPart('join')->willReturn([]);

$request = Request::create('/api/dummies', 'GET', []);
Expand All @@ -737,7 +741,7 @@ public function testAttributes()
$requestStack->push($request);

$serializerContextBuilderProphecy = $this->prophesize(SerializerContextBuilderInterface::class);
$serializerContextBuilderProphecy->createFromRequest($request, true)->shouldBeCalled()->willReturn([AbstractNormalizer::GROUPS => ['foo'], AbstractNormalizer::ATTRIBUTES => ['relatedDummy' => ['id', 'name']]]);
$serializerContextBuilderProphecy->createFromRequest($request, true)->shouldBeCalled()->willReturn([AbstractNormalizer::GROUPS => ['foo'], AbstractNormalizer::ATTRIBUTES => ['relatedDummies' => ['id', 'name'], 'relatedDummy' => ['id', 'name']]]);

$queryBuilder = $queryBuilderProphecy->reveal();
$eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, $requestStack, $serializerContextBuilderProphecy->reveal(), true);
Expand Down
39 changes: 39 additions & 0 deletions tests/Fixtures/TestBundle/Document/DummyPassenger.php
@@ -0,0 +1,39 @@
<?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\Core\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/**
* @ApiResource
* @ODM\Document
*/
class DummyPassenger
{
/**
* @ODM\Id(strategy="INCREMENT", type="int")
*/
private $id;

/**
* @ODM\Field(type="string")
*/
public $nickname;

public function getId()
{
return $this->id;
}
}
49 changes: 49 additions & 0 deletions tests/Fixtures/TestBundle/Document/DummyTravel.php
@@ -0,0 +1,49 @@
<?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\Core\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/**
* @ApiResource(filters={"dummy_travel.property"})
* @ODM\Document
*/
class DummyTravel
{
/**
* @ODM\Id(strategy="INCREMENT", type="int")
*/
private $id;

/**
* @ODM\ReferenceOne(targetDocument=DummyCar::class)
*/
public $car;

/**
* @ODM\Field(type="bool")
*/
public $confirmed;

/**
* @ODM\ReferenceOne(targetDocument=DummyPassenger::class)
*/
public $passenger;

public function getId()
{
return $this->id;
}
}
2 changes: 1 addition & 1 deletion tests/Fixtures/TestBundle/Entity/DummyTravel.php
Expand Up @@ -17,7 +17,7 @@
use Doctrine\ORM\Mapping as ORM;

/**
* @ApiResource
* @ApiResource(filters={"dummy_travel.property"})
* @ORM\Entity
*/
class DummyTravel
Expand Down
4 changes: 4 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Expand Up @@ -126,6 +126,10 @@ services:
parent: 'api_platform.serializer.property_filter'
tags: [ { name: 'api_platform.filter', id: 'my_dummy.property' } ]

app.dummy_travel_resource.property_filter:
parent: 'api_platform.serializer.property_filter'
tags: [ { name: 'api_platform.filter', id: 'dummy_travel.property' } ]

ApiPlatform\Core\Tests\Fixtures\TestBundle\Filter\RequiredFilter:
arguments: ['@doctrine']
tags: ['api_platform.filter']
Expand Down

0 comments on commit 0a4e7da

Please sign in to comment.