diff --git a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php index 3b3c5ca7a00..b71b807f39b 100644 --- a/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php +++ b/src/Bridge/Doctrine/Orm/Extension/EagerLoadingExtension.php @@ -44,13 +44,17 @@ final class EagerLoadingExtension implements QueryCollectionExtensionInterface, private $serializerContextBuilder; private $requestStack; - public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, int $maxJoins = 30, bool $forceEager = true, RequestStack $requestStack = null, SerializerContextBuilderInterface $serializerContextBuilder = null) + /** + * @TODO move $fetchPartial after $forceEager (@soyuka) in 3.0 + */ + public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceMetadataFactoryInterface $resourceMetadataFactory, int $maxJoins = 30, bool $forceEager = true, RequestStack $requestStack = null, SerializerContextBuilderInterface $serializerContextBuilder = null, bool $fetchPartial = false) { $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; $this->propertyMetadataFactory = $propertyMetadataFactory; $this->resourceMetadataFactory = $resourceMetadataFactory; $this->maxJoins = $maxJoins; $this->forceEager = $forceEager; + $this->fetchPartial = $fetchPartial; $this->serializerContextBuilder = $serializerContextBuilder; $this->requestStack = $requestStack; } @@ -67,10 +71,11 @@ public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGenerator } $forceEager = $this->shouldOperationForceEager($resourceClass, $options); + $fetchPartial = $this->shouldOperationFetchPartial($resourceClass, $options); $groups = $this->getSerializerGroups($resourceClass, $options, 'normalization_context'); - $this->joinRelations($queryBuilder, $queryNameGenerator, $resourceClass, $forceEager, $queryBuilder->getRootAliases()[0], $groups); + $this->joinRelations($queryBuilder, $queryNameGenerator, $resourceClass, $forceEager, $fetchPartial, $queryBuilder->getRootAliases()[0], $groups); } /** @@ -86,6 +91,7 @@ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterf } $forceEager = $this->shouldOperationForceEager($resourceClass, $options); + $fetchPartial = $this->shouldOperationFetchPartial($resourceClass, $options); if (isset($context['groups'])) { $groups = ['serializer_groups' => $context['groups']]; @@ -95,7 +101,7 @@ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterf $groups = $this->getSerializerGroups($resourceClass, $options, 'normalization_context'); } - $this->joinRelations($queryBuilder, $queryNameGenerator, $resourceClass, $forceEager, $queryBuilder->getRootAliases()[0], $groups); + $this->joinRelations($queryBuilder, $queryNameGenerator, $resourceClass, $forceEager, $fetchPartial, $queryBuilder->getRootAliases()[0], $groups); } /** @@ -112,7 +118,7 @@ public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterf * * @throws RuntimeException when the max number of joins has been reached */ - private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, bool $forceEager, string $parentAlias, array $propertyMetadataOptions = [], bool $wasLeftJoin = false, int &$joinCount = 0) + private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, bool $forceEager, bool $fetchPartial, string $parentAlias, array $propertyMetadataOptions = [], bool $wasLeftJoin = false, int &$joinCount = 0) { if ($joinCount > $this->maxJoins) { throw new RuntimeException('The total number of joined relations has exceeded the specified maximum. Raise the limit if necessary.'); @@ -152,18 +158,23 @@ private function joinRelations(QueryBuilder $queryBuilder, QueryNameGeneratorInt $queryBuilder->{$method}(sprintf('%s.%s', $parentAlias, $association), $associationAlias); ++$joinCount; - try { - $this->addSelect($queryBuilder, $mapping['targetEntity'], $associationAlias, $propertyMetadataOptions); - } catch (ResourceClassNotFoundException $resourceClassNotFoundException) { - continue; + if (true === $fetchPartial) { + try { + $this->addSelect($queryBuilder, $mapping['targetEntity'], $associationAlias, $propertyMetadataOptions); + } catch (ResourceClassNotFoundException $resourceClassNotFoundException) { + continue; + } + } else { + $queryBuilder->addSelect($associationAlias); } + // Avoid recursion if ($mapping['targetEntity'] === $resourceClass) { $queryBuilder->addSelect($associationAlias); continue; } - $this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $associationAlias, $propertyMetadataOptions, $method === 'leftJoin', $joinCount); + $this->joinRelations($queryBuilder, $queryNameGenerator, $mapping['targetEntity'], $forceEager, $fetchPartial, $associationAlias, $propertyMetadataOptions, $method === 'leftJoin', $joinCount); } } @@ -184,7 +195,7 @@ private function addSelect(QueryBuilder $queryBuilder, string $entity, string $a } //the field test allows to add methods to a Resource which do not reflect real database fields - if (true === $targetClassMetadata->hasField($property) && true === $propertyMetadata->isReadable()) { + if (true === $targetClassMetadata->hasField($property) && (true === $propertyMetadata->getAttribute('fetchable') || true === $propertyMetadata->isReadable())) { $select[] = $property; } } diff --git a/src/Bridge/Doctrine/Orm/Util/EagerLoadingTrait.php b/src/Bridge/Doctrine/Orm/Util/EagerLoadingTrait.php index 712ca921d71..d9aed58b920 100644 --- a/src/Bridge/Doctrine/Orm/Util/EagerLoadingTrait.php +++ b/src/Bridge/Doctrine/Orm/Util/EagerLoadingTrait.php @@ -24,6 +24,7 @@ trait EagerLoadingTrait { private $forceEager; + private $fetchPartial; private $resourceMetadataFactory; /** @@ -35,18 +36,46 @@ trait EagerLoadingTrait * @return bool */ private function shouldOperationForceEager(string $resourceClass, array $options): bool + { + return $this->getBooleanOperationAttribute($resourceClass, $options, 'force_eager', $this->forceEager); + } + + /** + * Checks if an operation has a `fetch_partial` attribute. + * + * @param string $resourceClass + * @param array $options + * + * @return bool + */ + private function shouldOperationFetchPartial(string $resourceClass, array $options): bool + { + return $this->getBooleanOperationAttribute($resourceClass, $options, 'fetch_partial', $this->fetchPartial); + } + + /** + * Get the boolean attribute of an operation or the resource metadata. + * + * @param string $resourceClass + * @param array $options + * @param string $attributeName + * @param bool $default + * + * @return bool + */ + private function getBooleanOperationAttribute(string $resourceClass, array $options, string $attributeName, bool $default): bool { $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); if (isset($options['collection_operation_name'])) { - $forceEager = $resourceMetadata->getCollectionOperationAttribute($options['collection_operation_name'], 'force_eager', null, true); + $attribute = $resourceMetadata->getCollectionOperationAttribute($options['collection_operation_name'], $attributeName, null, true); } elseif (isset($options['item_operation_name'])) { - $forceEager = $resourceMetadata->getItemOperationAttribute($options['item_operation_name'], 'force_eager', null, true); + $attribute = $resourceMetadata->getItemOperationAttribute($options['item_operation_name'], $attributeName, null, true); } else { - $forceEager = $resourceMetadata->getAttribute('force_eager'); + $attribute = $resourceMetadata->getAttribute($attributeName); } - return is_bool($forceEager) ? $forceEager : (bool) $this->forceEager; + return is_bool($attribute) ? $attribute : $default; } /** diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index 597b4fe78b7..b33c021bbc0 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -110,6 +110,7 @@ private function handleConfig(ContainerBuilder $container, array $config, array $container->setParameter('api_platform.error_formats', $errorFormats); $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']); $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']); + $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']); $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']); $container->setParameter('api_platform.collection.order', $config['collection']['order']); $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']); diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php index 1e7f75171d3..cafae3f45a5 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php @@ -50,6 +50,7 @@ public function getConfigTreeBuilder() ->addDefaultsIfNotSet() ->children() ->booleanNode('enabled')->defaultTrue()->info('To enable or disable eager loading')->end() + ->booleanNode('fetch_partial')->defaultFalse()->info('Fetch only partial data according to serialization groups. If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.')->end() ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end() ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end() ->end() diff --git a/src/Bridge/Symfony/Bundle/Resources/config/doctrine_orm.xml b/src/Bridge/Symfony/Bundle/Resources/config/doctrine_orm.xml index d0d35530196..4f2004868e6 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/doctrine_orm.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/doctrine_orm.xml @@ -95,6 +95,7 @@ %api_platform.eager_loading.force_eager% + %api_platform.eager_loading.fetch_partial% diff --git a/src/Metadata/Property/PropertyMetadata.php b/src/Metadata/Property/PropertyMetadata.php index 79ecebaee58..3e5794ba089 100644 --- a/src/Metadata/Property/PropertyMetadata.php +++ b/src/Metadata/Property/PropertyMetadata.php @@ -288,6 +288,23 @@ public function getAttributes() return $this->attributes; } + /** + * Gets an attribute. + * + * @param string $key + * @param mixed $defaultValue + * + * @return mixed + */ + public function getAttribute(string $key, $defaultValue = null) + { + if (isset($this->attributes[$key])) { + return $this->attributes[$key]; + } + + return $defaultValue; + } + /** * Returns a new instance with the given attribute. * diff --git a/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php b/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php index 26f2344c5f5..087bd024db5 100644 --- a/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php +++ b/tests/Bridge/Doctrine/Orm/Extension/EagerLoadingExtensionTest.php @@ -105,7 +105,7 @@ public function testApplyToCollection() $queryBuilderProphecy->addSelect('partial relatedDummy2_a2.{id,name}')->shouldBeCalled(1); $queryBuilder = $queryBuilderProphecy->reveal(); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class); } @@ -200,7 +200,7 @@ public function testApplyToItem() $queryBuilderProphecy->addSelect('singleInheritanceRelation_a6')->shouldBeCalled(1); $queryBuilder = $queryBuilderProphecy->reveal(); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $orderExtensionTest->applyToItem($queryBuilder, new QueryNameGenerator(), Dummy::class, []); } @@ -225,7 +225,7 @@ public function testCreateItemWithOperationName() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], 'item_operation'); } @@ -249,7 +249,7 @@ public function testCreateCollectionWithOperationName() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, 'collection_operation'); } @@ -271,7 +271,7 @@ public function testDenormalizeItemWithCorrectResourceClass() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $eagerExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), RelatedDummy::class, ['id' => 1], 'item_operation', ['resource_class' => Dummy::class]); } @@ -293,7 +293,7 @@ public function testDenormalizeItemWithExistingGroups() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $eagerExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), RelatedDummy::class, ['id' => 1], 'item_operation', ['groups' => 'some_groups']); } @@ -346,7 +346,7 @@ public function testMaxDepthReached() $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalled(); $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled(); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, null, null, true); $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class); } @@ -390,7 +390,7 @@ public function testForceEager() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true, null, null, true); $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, []); } @@ -414,7 +414,7 @@ public function testResourceClassNotFoundException() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true, null, null, true); $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, []); } @@ -438,7 +438,7 @@ public function testPropertyNotFoundException() $queryBuilderProphecy->getRootAliases()->willReturn(['o']); $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true, null, null, true); $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, []); } @@ -467,7 +467,7 @@ public function testResourceClassNotFoundExceptionPropertyNameCollection() $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalled(1); - $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true); + $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, true, null, null, true); $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, []); } @@ -531,7 +531,50 @@ public function testApplyToCollectionWithSerializerContextBuilder() $serializerContextBuilderProphecy->createFromRequest($request, true)->shouldBeCalled()->willReturn(['groups' => ['foo']]); $queryBuilder = $queryBuilderProphecy->reveal(); - $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, $requestStack, $serializerContextBuilderProphecy->reveal()); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30, false, $requestStack, $serializerContextBuilderProphecy->reveal(), true); + $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class); + } + + public function testApplyToCollectionNoPartial() + { + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata()); + + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + + $relatedNameCollection = new PropertyNameCollection(['id', 'name', 'notindatabase', 'notreadable']); + + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + $relationPropertyMetadata = new PropertyMetadata(); + $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true); + + $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', [])->willReturn($relationPropertyMetadata)->shouldBeCalled(); + $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', [])->willReturn($relationPropertyMetadata)->shouldBeCalled(); + + $queryBuilderProphecy = $this->prophesize(QueryBuilder::class); + + $classMetadataProphecy = $this->prophesize(ClassMetadata::class); + $classMetadataProphecy->associationMappings = [ + 'relatedDummy' => ['fetch' => 3, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class], + 'relatedDummy2' => ['fetch' => 3, 'joinColumns' => [['nullable' => false]], 'targetEntity' => RelatedDummy::class], + ]; + + $emProphecy = $this->prophesize(EntityManager::class); + $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class); + $relatedClassMetadataProphecy->associationMappings = []; + $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal()); + $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal()); + + $queryBuilderProphecy->getRootAliases()->willReturn(['o']); + $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy); + + $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalled(1); + $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalled(1); + $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalled(1); + $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalled(1); + + $queryBuilder = $queryBuilderProphecy->reveal(); + $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), 30); $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class); } } diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 5d1384c2edf..d25c9a52efc 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -231,6 +231,7 @@ private function getContainerBuilderProphecy() 'api_platform.eager_loading.enabled' => Argument::type('bool'), 'api_platform.eager_loading.max_joins' => 30, 'api_platform.eager_loading.force_eager' => true, + 'api_platform.eager_loading.fetch_partial' => false, 'api_platform.resource_class_directories' => [], ]; foreach ($parameters as $key => $value) { diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index ccf33a7f855..b0b66932f3a 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -77,6 +77,7 @@ public function testDefaultConfig() 'enabled' => true, 'max_joins' => 30, 'force_eager' => true, + 'fetch_partial' => false, ], 'collection' => [ 'order' => null, diff --git a/tests/Metadata/Property/PropertyMetadataTest.php b/tests/Metadata/Property/PropertyMetadataTest.php index 8b34768ac22..b34605c0e67 100644 --- a/tests/Metadata/Property/PropertyMetadataTest.php +++ b/tests/Metadata/Property/PropertyMetadataTest.php @@ -76,6 +76,7 @@ public function testValueObject() $newMetadata = $metadata->withAttributes(['a' => 'b']); $this->assertNotSame($metadata, $newMetadata); $this->assertEquals(['a' => 'b'], $newMetadata->getAttributes()); + $this->assertEquals('b', $newMetadata->getAttribute('a')); } public function testShouldReturnRequiredFalseWhenRequiredTrueIsSetButMaskedByWritableFalse()