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

api-platform / core / 13859480979

14 Mar 2025 03:06PM UTC coverage: 8.768% (+0.3%) from 8.515%
13859480979

push

github

soyuka
docs: changelog 4.1.1

13382 of 152631 relevant lines covered (8.77%)

23.56 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']);
×
120
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
121

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

269
        $emProphecy = $this->prophesize(EntityManager::class);
×
270
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
271
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
272
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
273

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

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

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

290
        $emProphecy = $this->prophesize(EntityManager::class);
×
291
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
292
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
293
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
294

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

299
    public function testDenormalizeItemWithCorrectResourceClass(): void
300
    {
301
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
302
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
303

304
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
305
        $classMetadataProphecy->associationMappings = [];
×
306

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

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

319
    public function testDenormalizeItemWithExistingGroups(): void
320
    {
321
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
322
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
323

324
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
325
        $classMetadataProphecy->associationMappings = [];
×
326

327
        // groups exist from the context, we don't need to compute them again
328
        $emProphecy = $this->prophesize(EntityManager::class);
×
329
        $emProphecy->getClassMetadata(Dummy::class)->shouldNotBeCalled();
×
330
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
331
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
332
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
333
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
334

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

339
    public function testMaxJoinsReached(): void
340
    {
341
        $this->expectException(RuntimeException::class);
×
342
        $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).');
×
343

344
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
345

346
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
347
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
348

349
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
350
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
351

352
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
353
        $relationPropertyMetadata = new ApiProperty();
×
354
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
355

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

358
        $relatedPropertyMetadata = new ApiProperty();
×
359
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
360

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

363
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
364
        $classMetadataProphecy->associationMappings = [
×
365
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
366
        ];
×
367
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
368

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

375
        $emProphecy = $this->prophesize(EntityManager::class);
×
376
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
377
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
378

379
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
380
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
381
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
382

383
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
384
        $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
385
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
386

387
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
388
        $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, null, ['groups' => ['foo']]);
×
389
    }
390

391
    public function testMaxDepth(): void
392
    {
393
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
394

395
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
396
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
397

398
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
399
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
400

401
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
402
        $relationPropertyMetadata = new ApiProperty();
×
403
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
404

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

407
        $relatedPropertyMetadata = new ApiProperty();
×
408
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
409

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

412
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
413
        $classMetadataProphecy->associationMappings = [
×
414
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
415
        ];
×
416
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
417

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

424
        $dummyClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
425
        $relatedClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
426
        $classMetadataFactoryProphecy = $this->prophesize(ClassMetadataFactoryInterface::class);
×
427

428
        $dummyAttributeMetadata = new AttributeMetadata('dummy');
×
429
        $dummyAttributeMetadata->setMaxDepth(2);
×
430

431
        $relatedAttributeMetadata = new AttributeMetadata('relatedDummy');
×
432
        $relatedAttributeMetadata->setMaxDepth(4);
×
433

434
        $dummyClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['relatedDummy' => $dummyAttributeMetadata]);
×
435
        $relatedClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['dummy' => $relatedAttributeMetadata]);
×
436

437
        $classMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedClassMetadataInterfaceProphecy->reveal());
×
438
        $classMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummyClassMetadataInterfaceProphecy->reveal());
×
439

440
        $emProphecy = $this->prophesize(EntityManager::class);
×
441
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
442
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
443

444
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
445
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
446
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
447

448
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalledTimes(2)->willReturn($queryBuilderProphecy);
×
449
        $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
450
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
451

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

456
    public function testForceEager(): void
457
    {
458
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
459
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
×
460

461
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
462
        $relationPropertyMetadata = new ApiProperty();
×
463
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
464

465
        $idPropertyMetadata = new ApiProperty();
×
466
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
467

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

471
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
472

473
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
474
        $classMetadataProphecy->associationMappings = [
×
475
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
476
        ];
×
477

478
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
479
        $unknownClassMetadataProphecy->associationMappings = [];
×
480

481
        $emProphecy = $this->prophesize(EntityManager::class);
×
482
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
483
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($unknownClassMetadataProphecy->reveal());
×
484

485
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
486
        $queryBuilderProphecy->addSelect('partial relation_a1.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
487
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
488

489
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
490
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
491

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

496
    public function testExtraLazy(): void
497
    {
498
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
499
        // $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
500

501
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
502
        $relationPropertyMetadata = new ApiProperty();
×
503
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
504

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

507
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
508

509
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
510
        $classMetadataProphecy->associationMappings = [
×
511
            'relation' => ['fetch' => ClassMetadata::FETCH_EXTRA_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
512
        ];
×
513

514
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
515
        $unknownClassMetadataProphecy->associationMappings = [];
×
516

517
        $emProphecy = $this->prophesize(EntityManager::class);
×
518
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
519

520
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
521
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
522

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

527
    public function testResourceClassNotFoundException(): void
528
    {
529
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
530

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

534
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
535
        $classMetadataProphecy->associationMappings = [
×
536
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
537
        ];
×
538
        $emProphecy = $this->prophesize(EntityManager::class);
×
539
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
540
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
541
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
542
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
543

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

548
    public function testPropertyNotFoundException(): void
549
    {
550
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
551

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

555
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
556
        $classMetadataProphecy->associationMappings = [
×
557
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
558
        ];
×
559
        $emProphecy = $this->prophesize(EntityManager::class);
×
560
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
561
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
562
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
563
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
564

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

569
    public function testResourceClassNotFoundExceptionPropertyNameCollection(): void
570
    {
571
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
572
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willThrow(new ResourceClassNotFoundException());
×
573

574
        $relationPropertyMetadata = new ApiProperty();
×
575
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
576
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
577
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata);
×
578

579
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
580
        $classMetadataProphecy->associationMappings = [
×
581
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
582
        ];
×
583
        $emProphecy = $this->prophesize(EntityManager::class);
×
584
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
585
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
586
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
587
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
588
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
589
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
590
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
591

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

596
    public function testAttributes(): void
597
    {
598
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
599

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

603
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
604
        $relationPropertyMetadata = new ApiProperty();
×
605
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
606

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

610
        $idPropertyMetadata = new ApiProperty();
×
611
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
612
        $namePropertyMetadata = new ApiProperty();
×
613
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
614

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

618
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
619

620
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
621
        $classMetadataProphecy->associationMappings = [
×
622
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
623
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
624
        ];
×
625

626
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
627

628
        foreach ($relatedNameCollection as $property) {
×
629
            if ('id' !== $property) {
×
630
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
631
            }
632
        }
633

634
        $relatedClassMetadataProphecy->associationMappings = [];
×
635

636
        $emProphecy = $this->prophesize(EntityManager::class);
×
637
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
638
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
639

640
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
641
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
642

643
        $queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
644
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
645
        $queryBuilderProphecy->addSelect('partial relatedDummies_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
646
        $queryBuilderProphecy->addSelect('partial relatedDummy_a2.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
647
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
648

649
        $queryBuilder = $queryBuilderProphecy->reveal();
×
650
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
651
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
652
    }
653

654
    public function testNotInAttributes(): void
655
    {
656
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
657
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
658
        $relationPropertyMetadata = new ApiProperty();
×
659
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
660

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

663
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
664

665
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
666
        $classMetadataProphecy->associationMappings = [
×
667
            'relatedDummy' => ['fetch' => 3, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
668
        ];
×
669

670
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
671
        $relatedClassMetadataProphecy->associationMappings = [];
×
672

673
        $emProphecy = $this->prophesize(EntityManager::class);
×
674
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
675

676
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
677
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
678

679
        $queryBuilder = $queryBuilderProphecy->reveal();
×
680
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
681
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo', AbstractNormalizer::ATTRIBUTES => ['relatedDummy']]));
×
682
    }
683

684
    public function testOnlyOneRelationNotInAttributes(): void
685
    {
686
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
687

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

691
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
692
        $relationPropertyMetadata = new ApiProperty();
×
693
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
694

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

698
        $idPropertyMetadata = new ApiProperty();
×
699
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
700
        $namePropertyMetadata = new ApiProperty();
×
701
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
702

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

706
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
707

708
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
709
        $classMetadataProphecy->associationMappings = [
×
710
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
711
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
712
        ];
×
713

714
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
715

716
        foreach ($relatedNameCollection as $property) {
×
717
            if ('id' !== $property) {
×
718
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
719
            }
720
        }
721

722
        $relatedClassMetadataProphecy->associationMappings = [];
×
723

724
        $emProphecy = $this->prophesize(EntityManager::class);
×
725
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
726
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
727

728
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
729
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
730

731
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
732
        $queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
733
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
734

735
        $queryBuilder = $queryBuilderProphecy->reveal();
×
736
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
737
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo', AbstractNormalizer::ATTRIBUTES => ['relatedDummy' => ['id', 'name']]]));
×
738
    }
739

740
    public function testApplyToCollectionNoPartial(): void
741
    {
742
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
743

744
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
745
        $relationPropertyMetadata = new ApiProperty();
×
746
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
747

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

751
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
752

753
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
754
        $classMetadataProphecy->associationMappings = [
×
755
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
756
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
757
        ];
×
758

759
        $emProphecy = $this->prophesize(EntityManager::class);
×
760
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
761
        $relatedClassMetadataProphecy->associationMappings = [];
×
762
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
763
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
764

765
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
766
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
767

768
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
769
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
770
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
771
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
772
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
773
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
774

775
        $queryBuilder = $queryBuilderProphecy->reveal();
×
776
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
777
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
778
    }
779

780
    public function testApplyToCollectionWithANonReadableButFetchEagerProperty(): void
781
    {
782
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
783

784
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
785
        $relationPropertyMetadata = new ApiProperty();
×
786
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
787
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
788
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
789

790
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
791
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
792

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

796
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
797

798
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
799
        $classMetadataProphecy->associationMappings = [
×
800
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
801
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
802
        ];
×
803

804
        $emProphecy = $this->prophesize(EntityManager::class);
×
805
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
806
        $relatedClassMetadataProphecy->associationMappings = [];
×
807
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
808
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
809

810
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
811
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
812

813
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
814
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
815
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
816
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
817
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
818
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
819

820
        $queryBuilder = $queryBuilderProphecy->reveal();
×
821
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
822
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
823
    }
824

825
    #[\PHPUnit\Framework\Attributes\DataProvider('provideExistingJoinCases')]
826
    public function testApplyToCollectionWithExistingJoin(string $joinType): void
827
    {
828
        $context = ['groups' => ['foo']];
×
829
        $callContext = ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'];
×
830

831
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
832

833
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
834
        $relationPropertyMetadata = new ApiProperty();
×
835
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
836

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

839
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
840

841
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
842
        $classMetadataProphecy->associationMappings = [
×
843
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
844
        ];
×
845

846
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
847

848
        $emProphecy = $this->prophesize(EntityManager::class);
×
849
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
850
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
851

852
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
853
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
854
        $queryBuilderProphecy->getDQLPart('join')->willReturn([
×
855
            'o' => [
×
856
                new Join($joinType, 'o.relatedDummy', 'existing_join_alias'),
×
857
            ],
×
858
        ]);
×
859
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
860
        $queryBuilderProphecy->addSelect('existing_join_alias')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
861

862
        $queryBuilder = $queryBuilderProphecy->reveal();
×
863
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false);
×
864
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']), $context);
×
865
    }
866

867
    public static function provideExistingJoinCases(): iterable
868
    {
869
        yield [Join::LEFT_JOIN];
×
870
        yield [Join::INNER_JOIN];
×
871
    }
872

873
    public function testApplyToCollectionWithAReadableButNotFetchEagerProperty(): void
874
    {
875
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
876

877
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
878
        $relationPropertyMetadata = new ApiProperty();
×
879
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(false);
×
880
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
881
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
882

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

886
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
887

888
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
889
        $classMetadataProphecy->associationMappings = [
×
890
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
891
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => false]], 'targetEntity' => RelatedDummy::class],
×
892
        ];
×
893

894
        $emProphecy = $this->prophesize(EntityManager::class);
×
895
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
896
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldNotBecalled();
×
897

898
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
899
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
900

901
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldNotBeCalled();
×
902
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldNotBeCalled();
×
903
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldNotBeCalled();
×
904
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldNotBeCalled();
×
905

906
        $queryBuilder = $queryBuilderProphecy->reveal();
×
907
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
908
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
909
    }
910

911
    public function testAvoidFetchCollectionOnIriOnlyProperty(): void
912
    {
913
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
914

915
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
916
        $relationPropertyMetadata = new ApiProperty();
×
917
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
918
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
919
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
920
        $relationPropertyMetadata = $relationPropertyMetadata->withUriTemplate('/property-collection-relations');
×
921

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

924
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
925

926
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
927
        $classMetadataProphecy->associationMappings = [
×
928
            'propertyCollectionIriOnlyRelation' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => PropertyCollectionIriOnlyRelation::class],
×
929
        ];
×
930

931
        $emProphecy = $this->prophesize(EntityManager::class);
×
932
        $emProphecy->getClassMetadata(PropertyCollectionIriOnly::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
933
        $emProphecy->getClassMetadata(PropertyCollectionIriOnlyRelation::class)->shouldNotBecalled();
×
934

935
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
936
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
937

938
        $queryBuilderProphecy->leftJoin('o.propertyCollectionIriOnlyRelation', 'propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
939
        $queryBuilderProphecy->addSelect('propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
940

941
        $queryBuilder = $queryBuilderProphecy->reveal();
×
942
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
943
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), PropertyCollectionIriOnly::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'read']));
×
944
    }
945
}
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