• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 20712321912

05 Jan 2026 10:20AM UTC coverage: 28.307%. First build
20712321912

Pull #7647

github

web-flow
Merge af83b1e2f into 0e899fa81
Pull Request #7647: Fix partial fetch with same entity included multiple time with different fields

19 of 52 new or added lines in 2 files covered. (36.54%)

16457 of 58137 relevant lines covered (28.31%)

51.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Doctrine/Orm/Tests/Extension/EagerLoadingExtensionTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Doctrine\Orm\Tests\Extension;
15

16
use ApiPlatform\Doctrine\Orm\Extension\EagerLoadingExtension;
17
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\AbstractDummy;
18
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\ConcreteDummy;
19
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\Dummy;
20
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\EmbeddableDummy;
21
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\PropertyCollectionIriOnly;
22
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\PropertyCollectionIriOnlyRelation;
23
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\RelatedDummy;
24
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\ThirdLevel;
25
use ApiPlatform\Doctrine\Orm\Tests\Fixtures\Entity\UnknownDummy;
26
use ApiPlatform\Doctrine\Orm\Util\QueryNameGenerator;
27
use ApiPlatform\Metadata\ApiProperty;
28
use ApiPlatform\Metadata\Exception\PropertyNotFoundException;
29
use ApiPlatform\Metadata\Exception\ResourceClassNotFoundException;
30
use ApiPlatform\Metadata\Exception\RuntimeException;
31
use ApiPlatform\Metadata\Get;
32
use ApiPlatform\Metadata\GetCollection;
33
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
34
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
35
use ApiPlatform\Metadata\Property\PropertyNameCollection;
36
use Doctrine\ORM\EntityManager;
37
use Doctrine\ORM\Mapping\ClassMetadata;
38
use Doctrine\ORM\Mapping\JoinColumn;
39
use Doctrine\ORM\Query\Expr\Join;
40
use Doctrine\ORM\QueryBuilder;
41
use PHPUnit\Framework\TestCase;
42
use Prophecy\Argument;
43
use Prophecy\PhpUnit\ProphecyTrait;
44
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
45
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
46
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
47
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
48

49
/**
50
 * @author Amrouche Hamza <hamza.simperfit@gmail.com>
51
 * @author Antoine Bluchet <soyuka@gmail.com>
52
 */
53
class EagerLoadingExtensionTest extends TestCase
54
{
55
    use ProphecyTrait;
56

57
    public function testApplyToCollection(): void
58
    {
59
        $context = ['groups' => ['foo']];
×
60
        $callContext = ['serializer_groups' => ['foo']];
×
61

62
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
63

64
        $relatedNameCollection = new PropertyNameCollection(['id', 'name', 'notindatabase', 'notreadable', 'embeddedDummy']);
×
65
        $relatedEmbedableCollection = new PropertyNameCollection(['name']);
×
66

67
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
68

69
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
70
        $relationPropertyMetadata = new ApiProperty();
×
71
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
72

73
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
74
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
75
        $propertyNameCollectionFactoryProphecy->create(EmbeddableDummy::class)->willReturn($relatedEmbedableCollection)->shouldBeCalled();
×
76

77
        $idPropertyMetadata = new ApiProperty();
×
78
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
79
        $namePropertyMetadata = new ApiProperty();
×
80
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
81
        $embeddedPropertyMetadata = new ApiProperty();
×
82
        $embeddedPropertyMetadata = $embeddedPropertyMetadata->withReadable(true);
×
83
        $notInDatabasePropertyMetadata = new ApiProperty();
×
84
        $notInDatabasePropertyMetadata = $notInDatabasePropertyMetadata->withReadable(true);
×
85
        $notReadablePropertyMetadata = new ApiProperty();
×
86
        $notReadablePropertyMetadata = $notReadablePropertyMetadata->withReadable(false);
×
87

88
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', $callContext)->willReturn($idPropertyMetadata)->shouldBeCalled();
×
89
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', $callContext)->willReturn($namePropertyMetadata)->shouldBeCalled();
×
90
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'embeddedDummy', $callContext)->willReturn($embeddedPropertyMetadata)->shouldBeCalled();
×
91
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'notindatabase', $callContext)->willReturn($notInDatabasePropertyMetadata)->shouldBeCalled();
×
92
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'notreadable', $callContext)->willReturn($notReadablePropertyMetadata)->shouldBeCalled();
×
93

94
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
95

96
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
97
        $classMetadataProphecy->associationMappings = [
×
98
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
99
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
100
        ];
×
101

102
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
103

104
        foreach ($relatedNameCollection as $property) {
×
105
            if ('id' !== $property && 'embeddedDummy' !== $property) {
×
106
                $relatedClassMetadataProphecy->hasField($property)->willReturn('notindatabase' !== $property)->shouldBeCalled();
×
107
            }
108
        }
109
        $relatedClassMetadataProphecy->hasField('embeddedDummy.name')->willReturn(true)->shouldBeCalled();
×
110

111
        $relatedClassMetadataProphecy->embeddedClasses = ['embeddedDummy' => ['class' => EmbeddableDummy::class]];
×
112

113
        $relatedClassMetadataProphecy->associationMappings = [];
×
114

115
        $emProphecy = $this->prophesize(EntityManager::class);
×
116
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
117
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
118

119
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
120
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
121
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
122

123
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
124
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
125
        $queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name,embeddedDummy.name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
126
        $queryBuilderProphecy->addSelect('partial relatedDummy2_a2.{id,name,embeddedDummy.name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
127
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
128

129
        $queryBuilder = $queryBuilderProphecy->reveal();
×
130
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
131
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, null, $context);
×
132
    }
133

134
    public function testApplyToItem(): void
135
    {
136
        $context = ['groups' => ['foo']];
×
137
        $callContext = ['serializer_groups' => ['foo']];
×
138

139
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
140

141
        $relatedNameCollection = new PropertyNameCollection(['id', 'name', 'embeddedDummy', 'notindatabase', 'notreadable', 'relation']);
×
142
        $relatedEmbedableCollection = new PropertyNameCollection(['name']);
×
143

144
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
145
        $propertyNameCollectionFactoryProphecy->create(EmbeddableDummy::class)->willReturn($relatedEmbedableCollection)->shouldBeCalled();
×
146
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
×
147
        $propertyNameCollectionFactoryProphecy->create(ThirdLevel::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
×
148

149
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
150
        $relationPropertyMetadata = new ApiProperty();
×
151
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
152

153
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
154
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
155
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy3', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
156
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy4', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
157
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy5', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
158
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'singleInheritanceRelation', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
159
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
160

161
        $idPropertyMetadata = new ApiProperty();
×
162
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
163
        $namePropertyMetadata = new ApiProperty();
×
164
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
165
        $embeddedDummyPropertyMetadata = new ApiProperty();
×
166
        $embeddedDummyPropertyMetadata = $embeddedDummyPropertyMetadata->withReadable(true);
×
167
        $notInDatabasePropertyMetadata = new ApiProperty();
×
168
        $notInDatabasePropertyMetadata = $notInDatabasePropertyMetadata->withReadable(true);
×
169
        $notReadablePropertyMetadata = new ApiProperty();
×
170
        $notReadablePropertyMetadata = $notReadablePropertyMetadata->withReadable(false);
×
171

172
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', $callContext)->willReturn($idPropertyMetadata)->shouldBeCalled();
×
173
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', $callContext)->willReturn($namePropertyMetadata)->shouldBeCalled();
×
174
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'embeddedDummy', $callContext)->willReturn($embeddedDummyPropertyMetadata)->shouldBeCalled();
×
175
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'notindatabase', $callContext)->willReturn($notInDatabasePropertyMetadata)->shouldBeCalled();
×
176
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'notreadable', $callContext)->willReturn($notReadablePropertyMetadata)->shouldBeCalled();
×
177
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'relation', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
178
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'thirdLevel', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
179
        $propertyMetadataFactoryProphecy->create(UnknownDummy::class, 'id', $callContext)->willReturn($idPropertyMetadata)->shouldBeCalled();
×
180
        $propertyMetadataFactoryProphecy->create(ThirdLevel::class, 'id', $callContext)->willReturn($idPropertyMetadata)->shouldBeCalled();
×
181

182
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
183

184
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
185
        $classMetadataProphecy->associationMappings = [
×
186
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
187
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => UnknownDummy::class],
×
188
            'relatedDummy3' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinTable' => ['joinColumns' => [new JoinColumn(nullable: false)]], 'targetEntity' => UnknownDummy::class],
×
189
            'relatedDummy4' => ['fetch' => ClassMetadata::FETCH_EAGER, 'targetEntity' => UnknownDummy::class],
×
190
            'relatedDummy5' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class],
×
191
            'singleInheritanceRelation' => ['fetch' => ClassMetadata::FETCH_EAGER, 'targetEntity' => AbstractDummy::class],
×
192
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'targetEntity' => RelatedDummy::class],
×
193
        ];
×
194

195
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
196

197
        foreach ($relatedNameCollection as $property) {
×
198
            if ('id' !== $property && 'embeddedDummy' !== $property) {
×
199
                $relatedClassMetadataProphecy->hasField($property)->willReturn('notindatabase' !== $property)->shouldBeCalled();
×
200
            }
201
        }
202
        $relatedClassMetadataProphecy->hasField('embeddedDummy.name')->willReturn(true)->shouldBeCalled();
×
203

204
        $relatedClassMetadataProphecy->associationMappings = [
×
205
            'relation' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => UnknownDummy::class],
×
206
            'thirdLevel' => ['fetch' => ClassMetadata::FETCH_EAGER, 'targetEntity' => ThirdLevel::class, 'sourceEntity' => RelatedDummy::class, 'inversedBy' => 'relatedDummies', 'type' => ClassMetadata::TO_ONE],
×
207
        ];
×
208

209
        $relatedClassMetadataProphecy->embeddedClasses = ['embeddedDummy' => ['class' => EmbeddableDummy::class]];
×
210

211
        $singleInheritanceClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
212
        $singleInheritanceClassMetadataProphecy->subClasses = [ConcreteDummy::class];
×
213

214
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
215
        $unknownClassMetadataProphecy->associationMappings = [];
×
216

217
        $thirdLevelMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
218
        $thirdLevelMetadataProphecy->associationMappings = [];
×
219

220
        $emProphecy = $this->prophesize(EntityManager::class);
×
221
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
222
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
223
        $emProphecy->getClassMetadata(AbstractDummy::class)->shouldBeCalled()->willReturn($singleInheritanceClassMetadataProphecy->reveal());
×
224
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($unknownClassMetadataProphecy->reveal());
×
225
        $emProphecy->getClassMetadata(ThirdLevel::class)->shouldBeCalled()->willReturn($thirdLevelMetadataProphecy->reveal());
×
226

227
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
228
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
229
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
230
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
231
        $queryBuilderProphecy->leftJoin('relatedDummy_a1.relation', 'relation_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
232
        $queryBuilderProphecy->leftJoin('relatedDummy_a1.thirdLevel', 'thirdLevel_a3')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
233
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a4')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
234
        $queryBuilderProphecy->leftJoin('o.relatedDummy3', 'relatedDummy3_a5')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
235
        $queryBuilderProphecy->leftJoin('o.relatedDummy4', 'relatedDummy4_a6')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
236
        $queryBuilderProphecy->leftJoin('o.singleInheritanceRelation', 'singleInheritanceRelation_a7')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
237
        $queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a8')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
238
        $queryBuilderProphecy->leftJoin('relatedDummies_a8.relation', 'relation_a9')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
239
        $queryBuilderProphecy->leftJoin('relatedDummies_a8.thirdLevel', 'thirdLevel_a10')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
240
        $queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name,embeddedDummy.name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
241
        $queryBuilderProphecy->addSelect('partial thirdLevel_a3.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
242
        $queryBuilderProphecy->addSelect('partial relation_a2.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
243
        $queryBuilderProphecy->addSelect('partial relatedDummy2_a4.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
244
        $queryBuilderProphecy->addSelect('partial relatedDummy3_a5.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
245
        $queryBuilderProphecy->addSelect('partial relatedDummy4_a6.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
246
        $queryBuilderProphecy->addSelect('singleInheritanceRelation_a7')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
247
        $queryBuilderProphecy->addSelect('partial relatedDummies_a8.{id,name,embeddedDummy.name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
248
        $queryBuilderProphecy->addSelect('partial relation_a9.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
249
        $queryBuilderProphecy->addSelect('partial thirdLevel_a10.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
250
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
251
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
252

253
        $queryBuilder = $queryBuilderProphecy->reveal();
×
254
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
255

256
        $orderExtensionTest->applyToItem($queryBuilder, new QueryNameGenerator(), Dummy::class, [], null, $context);
×
257
    }
258

259
    public function testCreateItemWithOperation(): void
260
    {
261
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
262
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
263
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
264
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', ['serializer_groups' => ['foo']])->shouldBeCalled()->willReturn(new ApiProperty());
×
265

266
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
267
        $classMetadataProphecy->associationMappings = [
×
268
            'foo' => ['fetch' => 1],
×
269
        ];
×
270

271
        $emProphecy = $this->prophesize(EntityManager::class);
×
272
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
273
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
274
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
275
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
276

277
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
278
        $eagerExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(name: 'item_operation'), ['groups' => ['foo']]);
×
279
    }
280

281
    public function testCreateCollectionWithOperation(): void
282
    {
283
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
284
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
285
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
286
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'foo', ['serializer_groups' => ['foo']])->shouldBeCalled()->willReturn(new ApiProperty());
×
287

288
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
289
        $classMetadataProphecy->associationMappings = [
×
290
            'foo' => ['fetch' => 1],
×
291
        ];
×
292

293
        $emProphecy = $this->prophesize(EntityManager::class);
×
294
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
295
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
296
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
297
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
298

299
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
300
        $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, new GetCollection(name: 'collection_operation'), ['groups' => ['foo']]);
×
301
    }
302

303
    public function testDenormalizeItemWithCorrectResourceClass(): void
304
    {
305
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
306
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
307

308
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
309
        $classMetadataProphecy->associationMappings = [];
×
310

311
        // Dummy is the correct class for the denormalization context serialization groups, and we're fetching RelatedDummy
312
        $emProphecy = $this->prophesize(EntityManager::class);
×
313
        $emProphecy->getClassMetadata(Dummy::class)->shouldNotBeCalled();
×
314
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
315
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
316
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
317
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
318
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
319

320
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
321
        $eagerExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), RelatedDummy::class, ['id' => 1], new Get(name: 'get', normalizationContext: ['groups' => ['foo']]), ['resource_class' => Dummy::class]);
×
322
    }
323

324
    public function testDenormalizeItemWithExistingGroups(): void
325
    {
326
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
327
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
328

329
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
330
        $classMetadataProphecy->associationMappings = [];
×
331

332
        // groups exist from the context, we don't need to compute them again
333
        $emProphecy = $this->prophesize(EntityManager::class);
×
334
        $emProphecy->getClassMetadata(Dummy::class)->shouldNotBeCalled();
×
335
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
336
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
337
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
338
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
339
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
340

341
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
342
        $eagerExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), RelatedDummy::class, ['id' => 1], new Get(name: 'item_operation', normalizationContext: ['groups' => ['foo']]), [AbstractNormalizer::GROUPS => 'some_groups']);
×
343
    }
344

345
    public function testMaxJoinsReached(): void
346
    {
347
        $this->expectException(RuntimeException::class);
×
348
        $this->expectExceptionMessage('The total number of joined relations has exceeded the specified maximum. Raise the limit if necessary with the "api_platform.eager_loading.max_joins" configuration key (https://api-platform.com/docs/core/performance/#eager-loading), or limit the maximum serialization depth using the "enable_max_depth" option of the Symfony serializer (https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth).');
×
349

350
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
351

352
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
353
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
354

355
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
356
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
357

358
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
359
        $relationPropertyMetadata = new ApiProperty();
×
360
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
361

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

364
        $relatedPropertyMetadata = new ApiProperty();
×
365
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
366

367
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'dummy', ['serializer_groups' => ['foo']])->willReturn($relatedPropertyMetadata)->shouldBeCalled();
×
368

369
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
370
        $classMetadataProphecy->associationMappings = [
×
371
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
372
        ];
×
373
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
374

375
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
376
        $relatedClassMetadataProphecy->associationMappings = [
×
377
            'dummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => Dummy::class],
×
378
        ];
×
379
        $relatedClassMetadataProphecy->hasField('dummy')->willReturn(true);
×
380

381
        $emProphecy = $this->prophesize(EntityManager::class);
×
382
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
383
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
384

385
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
386
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
387
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
388
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
389

NEW
390
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->willReturn($queryBuilderProphecy);
×
NEW
391
        $queryBuilderProphecy->addSelect(Argument::type('string'))->willReturn($queryBuilderProphecy);
×
392
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
393

394
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
395
        $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, null, ['groups' => ['foo']]);
×
396
    }
397

398
    public function testMaxDepth(): void
399
    {
400
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
401

402
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
403
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
404

405
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
406
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
407

408
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
409
        $relationPropertyMetadata = new ApiProperty();
×
410
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
411

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

414
        $relatedPropertyMetadata = new ApiProperty();
×
415
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
416

417
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'dummy', ['serializer_groups' => ['foo'], 'normalization_groups' => ['foo']])->willReturn($relatedPropertyMetadata)->shouldBeCalled();
×
418

419
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
420
        $classMetadataProphecy->associationMappings = [
×
421
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
422
        ];
×
423
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
424

425
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
426
        $relatedClassMetadataProphecy->associationMappings = [
×
427
            'dummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => Dummy::class],
×
428
        ];
×
429
        $relatedClassMetadataProphecy->hasField('dummy')->willReturn(true);
×
430

431
        $dummyClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
432
        $relatedClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
433
        $classMetadataFactoryProphecy = $this->prophesize(ClassMetadataFactoryInterface::class);
×
434

435
        $dummyAttributeMetadata = new AttributeMetadata('dummy');
×
436
        $dummyAttributeMetadata->setMaxDepth(2);
×
437

438
        $relatedAttributeMetadata = new AttributeMetadata('relatedDummy');
×
439
        $relatedAttributeMetadata->setMaxDepth(4);
×
440

441
        $dummyClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['relatedDummy' => $dummyAttributeMetadata]);
×
442
        $relatedClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['dummy' => $relatedAttributeMetadata]);
×
443

444
        $classMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedClassMetadataInterfaceProphecy->reveal());
×
445
        $classMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummyClassMetadataInterfaceProphecy->reveal());
×
446

447
        $emProphecy = $this->prophesize(EntityManager::class);
×
448
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
449
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
450

451
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
452
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
453
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
454
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
455

456
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalledTimes(2)->willReturn($queryBuilderProphecy);
×
457
        $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
458
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
459

460
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true, $classMetadataFactoryProphecy->reveal());
×
461
        $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: ['enable_max_depth' => 'true', 'groups' => ['foo']]));
×
462
    }
463

464
    public function testForceEager(): void
465
    {
466
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
467
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
×
468

469
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
470
        $relationPropertyMetadata = new ApiProperty();
×
471
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
472

473
        $idPropertyMetadata = new ApiProperty();
×
474
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
475

476
        $propertyMetadataFactoryProphecy->create(UnknownDummy::class, 'id', ['serializer_groups' => ['foobar'], 'normalization_groups' => 'foobar'])->willReturn($idPropertyMetadata)->shouldBeCalled();
×
477
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foobar'], 'normalization_groups' => 'foobar'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
478

479
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
480

481
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
482
        $classMetadataProphecy->associationMappings = [
×
483
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
484
        ];
×
485

486
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
487
        $unknownClassMetadataProphecy->associationMappings = [];
×
488

489
        $emProphecy = $this->prophesize(EntityManager::class);
×
490
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
491
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($unknownClassMetadataProphecy->reveal());
×
492

493
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
494
        $queryBuilderProphecy->addSelect('partial relation_a1.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
495
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
496

497
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
498
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
499
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
500

501
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, true, true);
×
502
        $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(normalizationContext: [AbstractNormalizer::GROUPS => 'foobar']));
×
503
    }
504

505
    public function testExtraLazy(): void
506
    {
507
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
508
        // $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
509

510
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
511
        $relationPropertyMetadata = new ApiProperty();
×
512
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
513

514
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foobar'], 'normalization_groups' => 'foobar'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
515

516
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
517

518
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
519
        $classMetadataProphecy->associationMappings = [
×
520
            'relation' => ['fetch' => ClassMetadata::FETCH_EXTRA_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
521
        ];
×
522

523
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
524
        $unknownClassMetadataProphecy->associationMappings = [];
×
525

526
        $emProphecy = $this->prophesize(EntityManager::class);
×
527
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
528

529
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
530
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
531
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
532

533
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, true, true);
×
534
        $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(normalizationContext: [AbstractNormalizer::GROUPS => 'foobar']));
×
535
    }
536

537
    public function testResourceClassNotFoundException(): void
538
    {
539
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
540

541
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
542
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willThrow(new ResourceClassNotFoundException());
×
543

544
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
545
        $classMetadataProphecy->associationMappings = [
×
546
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
547
        ];
×
548
        $emProphecy = $this->prophesize(EntityManager::class);
×
549
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
550
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
551
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
552
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
553
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
554

555
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, true, true);
×
556
        $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
557
    }
558

559
    public function testPropertyNotFoundException(): void
560
    {
561
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
562

563
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
564
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willThrow(new PropertyNotFoundException());
×
565

566
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
567
        $classMetadataProphecy->associationMappings = [
×
568
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
569
        ];
×
570
        $emProphecy = $this->prophesize(EntityManager::class);
×
571
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
572
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
573
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
574
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
575
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
576

577
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, true, true);
×
578
        $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
579
    }
580

581
    public function testResourceClassNotFoundExceptionPropertyNameCollection(): void
582
    {
583
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
584
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willThrow(new ResourceClassNotFoundException());
×
585

586
        $relationPropertyMetadata = new ApiProperty();
×
587
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
588
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
589
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata);
×
590

591
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
592
        $classMetadataProphecy->associationMappings = [
×
593
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
594
        ];
×
595
        $emProphecy = $this->prophesize(EntityManager::class);
×
596
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
597
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
598
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
599
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
600
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
601
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
602
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
603
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
604

605
        $orderExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, true, true);
×
606
        $orderExtensionTest->applyToItem($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, [], new Get(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
607
    }
608

609
    public function testAttributes(): void
610
    {
611
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
612

613
        $relatedNameCollection = new PropertyNameCollection(['id', 'name']);
×
614
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
615

616
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
617
        $relationPropertyMetadata = new ApiProperty();
×
618
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
619

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

623
        $idPropertyMetadata = new ApiProperty();
×
624
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
625
        $namePropertyMetadata = new ApiProperty();
×
626
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
627

628
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($idPropertyMetadata)->shouldBeCalled();
×
629
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($namePropertyMetadata)->shouldBeCalled();
×
630

631
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
632

633
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
634
        $classMetadataProphecy->associationMappings = [
×
635
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
636
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
637
        ];
×
638

639
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
640

641
        foreach ($relatedNameCollection as $property) {
×
642
            if ('id' !== $property) {
×
643
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
644
            }
645
        }
646

647
        $relatedClassMetadataProphecy->associationMappings = [];
×
648

649
        $emProphecy = $this->prophesize(EntityManager::class);
×
650
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
651
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
652

653
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
654
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
655
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
656

657
        $queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
658
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
659
        $queryBuilderProphecy->addSelect('partial relatedDummies_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
660
        $queryBuilderProphecy->addSelect('partial relatedDummy_a2.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
661
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
662

663
        $queryBuilder = $queryBuilderProphecy->reveal();
×
664
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
665
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
666
    }
667

668
    public function testNotInAttributes(): void
669
    {
670
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
671
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
672
        $relationPropertyMetadata = new ApiProperty();
×
673
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
674

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

677
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
678

679
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
680
        $classMetadataProphecy->associationMappings = [
×
681
            'relatedDummy' => ['fetch' => 3, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
682
        ];
×
683

684
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
685
        $relatedClassMetadataProphecy->associationMappings = [];
×
686

687
        $emProphecy = $this->prophesize(EntityManager::class);
×
688
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
689

690
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
691
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
692
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
693

694
        $queryBuilder = $queryBuilderProphecy->reveal();
×
695
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
696
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo', AbstractNormalizer::ATTRIBUTES => ['relatedDummy']]));
×
697
    }
698

699
    public function testOnlyOneRelationNotInAttributes(): void
700
    {
701
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
702

703
        $relatedNameCollection = new PropertyNameCollection(['id', 'name']);
×
704
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
705

706
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
707
        $relationPropertyMetadata = new ApiProperty();
×
708
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
709

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

713
        $idPropertyMetadata = new ApiProperty();
×
714
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
715
        $namePropertyMetadata = new ApiProperty();
×
716
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
717

718
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($idPropertyMetadata)->shouldBeCalled();
×
719
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($namePropertyMetadata)->shouldBeCalled();
×
720

721
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
722

723
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
724
        $classMetadataProphecy->associationMappings = [
×
725
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
726
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
727
        ];
×
728

729
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
730

731
        foreach ($relatedNameCollection as $property) {
×
732
            if ('id' !== $property) {
×
733
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
734
            }
735
        }
736

737
        $relatedClassMetadataProphecy->associationMappings = [];
×
738

739
        $emProphecy = $this->prophesize(EntityManager::class);
×
740
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
741
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
742

743
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
744
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
745
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
746

747
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
748
        $queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
749
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
750

751
        $queryBuilder = $queryBuilderProphecy->reveal();
×
752
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
753
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo', AbstractNormalizer::ATTRIBUTES => ['relatedDummy' => ['id', 'name']]]));
×
754
    }
755

756
    public function testApplyToCollectionNoPartial(): void
757
    {
758
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
759

760
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
761
        $relationPropertyMetadata = new ApiProperty();
×
762
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
763

764
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
765
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
766

767
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
768

769
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
770
        $classMetadataProphecy->associationMappings = [
×
771
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
772
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
773
        ];
×
774

775
        $emProphecy = $this->prophesize(EntityManager::class);
×
776
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
777
        $relatedClassMetadataProphecy->associationMappings = [];
×
778
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
779
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
780

781
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
782
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
783
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
784

785
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
786
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
787
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
788
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
789
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
790
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
791

792
        $queryBuilder = $queryBuilderProphecy->reveal();
×
793
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
794
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
795
    }
796

797
    public function testApplyToCollectionWithANonReadableButFetchEagerProperty(): void
798
    {
799
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
800

801
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
802
        $relationPropertyMetadata = new ApiProperty();
×
803
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
804
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
805
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
806

807
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
808
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
809

810
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
811
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
812

813
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
814

815
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
816
        $classMetadataProphecy->associationMappings = [
×
817
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
818
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
819
        ];
×
820

821
        $emProphecy = $this->prophesize(EntityManager::class);
×
822
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
823
        $relatedClassMetadataProphecy->associationMappings = [];
×
824
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
825
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
826

827
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
828
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
829
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
830

831
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
832
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
833
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
834
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
835
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
836
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
837

838
        $queryBuilder = $queryBuilderProphecy->reveal();
×
839
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
840
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
841
    }
842

843
    #[\PHPUnit\Framework\Attributes\DataProvider('provideExistingJoinCases')]
844
    public function testApplyToCollectionWithExistingJoin(string $joinType): void
845
    {
846
        $context = ['groups' => ['foo']];
×
847
        $callContext = ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'];
×
848

849
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
850

851
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
852
        $relationPropertyMetadata = new ApiProperty();
×
853
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
854

855
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', $callContext)->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
856

857
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
858

859
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
860
        $classMetadataProphecy->associationMappings = [
×
861
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
862
        ];
×
863

864
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
865

866
        $emProphecy = $this->prophesize(EntityManager::class);
×
867
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
868
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
869

870
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
871
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
872
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
873
        $queryBuilderProphecy->getDQLPart('join')->willReturn([
×
874
            'o' => [
×
875
                new Join($joinType, 'o.relatedDummy', 'existing_join_alias'),
×
876
            ],
×
877
        ]);
×
878
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
879
        $queryBuilderProphecy->addSelect('existing_join_alias')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
880

881
        $queryBuilder = $queryBuilderProphecy->reveal();
×
882
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false);
×
883
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']), $context);
×
884
    }
885

886
    public static function provideExistingJoinCases(): iterable
887
    {
888
        yield [Join::LEFT_JOIN];
×
889
        yield [Join::INNER_JOIN];
×
890
    }
891

892
    public function testApplyToCollectionWithAReadableButNotFetchEagerProperty(): void
893
    {
894
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
895

896
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
897
        $relationPropertyMetadata = new ApiProperty();
×
898
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(false);
×
899
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
900
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
901

902
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
903
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy2', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
904

905
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
906

907
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
908
        $classMetadataProphecy->associationMappings = [
×
909
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
910
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => false]], 'targetEntity' => RelatedDummy::class],
×
911
        ];
×
912

913
        $emProphecy = $this->prophesize(EntityManager::class);
×
914
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
915
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldNotBecalled();
×
916

917
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
918
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
919
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
920

921
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldNotBeCalled();
×
922
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldNotBeCalled();
×
923
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldNotBeCalled();
×
924
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldNotBeCalled();
×
925

926
        $queryBuilder = $queryBuilderProphecy->reveal();
×
927
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
928
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
929
    }
930

931
    public function testAvoidFetchCollectionOnIriOnlyProperty(): void
932
    {
933
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
934

935
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
936
        $relationPropertyMetadata = new ApiProperty();
×
937
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
938
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
939
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
940
        $relationPropertyMetadata = $relationPropertyMetadata->withUriTemplate('/property-collection-relations');
×
941

942
        $propertyMetadataFactoryProphecy->create(PropertyCollectionIriOnly::class, 'propertyCollectionIriOnlyRelation', ['serializer_groups' => ['read'], 'normalization_groups' => 'read'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
943

944
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
945

946
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
947
        $classMetadataProphecy->associationMappings = [
×
948
            'propertyCollectionIriOnlyRelation' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => PropertyCollectionIriOnlyRelation::class],
×
949
        ];
×
950

951
        $emProphecy = $this->prophesize(EntityManager::class);
×
952
        $emProphecy->getClassMetadata(PropertyCollectionIriOnly::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
953
        $emProphecy->getClassMetadata(PropertyCollectionIriOnlyRelation::class)->shouldNotBecalled();
×
954

955
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
956
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
957
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
958

959
        $queryBuilderProphecy->leftJoin('o.propertyCollectionIriOnlyRelation', 'propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
960
        $queryBuilderProphecy->addSelect('propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
961

962
        $queryBuilder = $queryBuilderProphecy->reveal();
×
963
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
964
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), PropertyCollectionIriOnly::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'read']));
×
965
    }
966
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc