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

api-platform / core / 20710553553

05 Jan 2026 09:13AM UTC coverage: 28.85%. First build
20710553553

Pull #7645

github

web-flow
Merge 735a2536d into 0e899fa81
Pull Request #7645: Fix partial fetch when relation switches context

12 of 62 new or added lines in 2 files covered. (19.35%)

16778 of 58156 relevant lines covered (28.85%)

78.4 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 testContextSwitch(): void
340
    {
NEW
341
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
342

NEW
343
        $relatedNameCollection = new PropertyNameCollection(['id', 'name']);
×
NEW
344
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
345

NEW
346
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
NEW
347
        $relationPropertyMetadata = new ApiProperty();
×
NEW
348
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
349

NEW
350
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummies', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
NEW
351
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relatedDummy', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata)->shouldBeCalled();
×
352

NEW
353
        $idPropertyMetadata = new ApiProperty();
×
NEW
354
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
NEW
355
        $namePropertyMetadata = new ApiProperty();
×
NEW
356
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
357

358
        // When called via `relatedDummies` without context switch
NEW
359
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($idPropertyMetadata)->shouldBeCalled();
×
NEW
360
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($namePropertyMetadata)->shouldBeCalled();
×
361

362
        // When called via `relatedDummy` with context switch
NEW
363
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'id', ['serializer_groups' => ['bar'], 'normalization_groups' => ['bar']])->willReturn($idPropertyMetadata)->shouldBeCalled();
×
NEW
364
        $propertyMetadataFactoryProphecy->create(RelatedDummy::class, 'name', ['serializer_groups' => ['bar'], 'normalization_groups' => ['bar']])->willReturn($namePropertyMetadata)->shouldBeCalled();
×
365

NEW
366
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
367

NEW
368
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
NEW
369
        $classMetadataProphecy->associationMappings = [
×
NEW
370
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
NEW
371
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
NEW
372
        ];
×
373

NEW
374
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
375

NEW
376
        foreach ($relatedNameCollection as $property) {
×
NEW
377
            if ('id' !== $property && 'embeddedDummy' !== $property) {
×
NEW
378
                $relatedClassMetadataProphecy->hasField($property)->willReturn('notindatabase' !== $property)->shouldBeCalled();
×
379
            }
380
        }
381

NEW
382
        $dummyClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
NEW
383
        $relatedClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
NEW
384
        $classMetadataFactoryProphecy = $this->prophesize(ClassMetadataFactoryInterface::class);
×
385

NEW
386
        $relatedDummyAttributeMetadata = new AttributeMetadata('relatedDummy');
×
NEW
387
        $relatedDummyAttributeMetadata->setNormalizationContextForGroups(['groups' => ['bar']], ['foo']);
×
388

NEW
389
        $dummyClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['relatedDummy' => $relatedDummyAttributeMetadata]);
×
NEW
390
        $relatedClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn([]);
×
391

NEW
392
        $classMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedClassMetadataInterfaceProphecy->reveal());
×
NEW
393
        $classMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummyClassMetadataInterfaceProphecy->reveal());
×
394

NEW
395
        $relatedClassMetadataProphecy->associationMappings = [];
×
396

NEW
397
        $emProphecy = $this->prophesize(EntityManager::class);
×
NEW
398
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
NEW
399
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
400

NEW
401
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
NEW
402
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
403

NEW
404
        $queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
NEW
405
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
NEW
406
        $queryBuilderProphecy->addSelect('partial relatedDummies_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
NEW
407
        $queryBuilderProphecy->addSelect('partial relatedDummy_a2.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
NEW
408
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
409

NEW
410
        $queryBuilder = $queryBuilderProphecy->reveal();
×
NEW
411
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true, $classMetadataFactoryProphecy->reveal());
×
NEW
412
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
413
    }
414

415
    public function testMaxJoinsReached(): void
416
    {
417
        $this->expectException(RuntimeException::class);
×
418
        $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).');
×
419

420
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
421

422
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
423
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
424

425
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
426
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
427

428
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
429
        $relationPropertyMetadata = new ApiProperty();
×
430
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
431

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

434
        $relatedPropertyMetadata = new ApiProperty();
×
435
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
436

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

439
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
440
        $classMetadataProphecy->associationMappings = [
×
441
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
442
        ];
×
443
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
444

445
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
446
        $relatedClassMetadataProphecy->associationMappings = [
×
447
            'dummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => Dummy::class],
×
448
        ];
×
449
        $relatedClassMetadataProphecy->hasField('dummy')->willReturn(true);
×
450

451
        $emProphecy = $this->prophesize(EntityManager::class);
×
452
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
453
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
454

455
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
456
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
457
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
458

459
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
460
        $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
461
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
462

463
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
464
        $eagerExtensionTest->applyToCollection($queryBuilderProphecy->reveal(), new QueryNameGenerator(), Dummy::class, null, ['groups' => ['foo']]);
×
465
    }
466

467
    public function testMaxDepth(): void
468
    {
469
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
470

471
        $relatedNameCollection = new PropertyNameCollection(['dummy']);
×
472
        $dummyNameCollection = new PropertyNameCollection(['relatedDummy']);
×
473

474
        $propertyNameCollectionFactoryProphecy->create(RelatedDummy::class)->willReturn($relatedNameCollection)->shouldBeCalled();
×
475
        $propertyNameCollectionFactoryProphecy->create(Dummy::class)->willReturn($dummyNameCollection)->shouldBeCalled();
×
476

477
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
478
        $relationPropertyMetadata = new ApiProperty();
×
479
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
480

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

483
        $relatedPropertyMetadata = new ApiProperty();
×
484
        $relatedPropertyMetadata = $relatedPropertyMetadata->withReadableLink(true);
×
485

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

488
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
489
        $classMetadataProphecy->associationMappings = [
×
490
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
491
        ];
×
492
        $classMetadataProphecy->hasField('relatedDummy')->willReturn(true);
×
493

494
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
495
        $relatedClassMetadataProphecy->associationMappings = [
×
496
            'dummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => Dummy::class],
×
497
        ];
×
498
        $relatedClassMetadataProphecy->hasField('dummy')->willReturn(true);
×
499

500
        $dummyClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
501
        $relatedClassMetadataInterfaceProphecy = $this->prophesize(ClassMetadataInterface::class);
×
502
        $classMetadataFactoryProphecy = $this->prophesize(ClassMetadataFactoryInterface::class);
×
503

504
        $dummyAttributeMetadata = new AttributeMetadata('dummy');
×
505
        $dummyAttributeMetadata->setMaxDepth(2);
×
506

507
        $relatedAttributeMetadata = new AttributeMetadata('relatedDummy');
×
508
        $relatedAttributeMetadata->setMaxDepth(4);
×
509

510
        $dummyClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['relatedDummy' => $dummyAttributeMetadata]);
×
511
        $relatedClassMetadataInterfaceProphecy->getAttributesMetadata()->willReturn(['dummy' => $relatedAttributeMetadata]);
×
512

513
        $classMetadataFactoryProphecy->getMetadataFor(RelatedDummy::class)->willReturn($relatedClassMetadataInterfaceProphecy->reveal());
×
514
        $classMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($dummyClassMetadataInterfaceProphecy->reveal());
×
515

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

520
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
521
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
522
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
523

524
        $queryBuilderProphecy->innerJoin(Argument::type('string'), Argument::type('string'))->shouldBeCalledTimes(2)->willReturn($queryBuilderProphecy);
×
525
        $queryBuilderProphecy->addSelect(Argument::type('string'))->shouldBeCalled()->willReturn($queryBuilderProphecy);
×
526
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
527

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

532
    public function testForceEager(): void
533
    {
534
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
535
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willReturn(new PropertyNameCollection(['id']))->shouldBeCalled();
×
536

537
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
538
        $relationPropertyMetadata = new ApiProperty();
×
539
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
540

541
        $idPropertyMetadata = new ApiProperty();
×
542
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
543

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

547
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
548

549
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
550
        $classMetadataProphecy->associationMappings = [
×
551
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
552
        ];
×
553

554
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
555
        $unknownClassMetadataProphecy->associationMappings = [];
×
556

557
        $emProphecy = $this->prophesize(EntityManager::class);
×
558
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
559
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($unknownClassMetadataProphecy->reveal());
×
560

561
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
562
        $queryBuilderProphecy->addSelect('partial relation_a1.{id}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
563
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
564

565
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
566
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
567

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

572
    public function testExtraLazy(): void
573
    {
574
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
575

576
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
577
        $relationPropertyMetadata = new ApiProperty();
×
578
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
579

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

582
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
583

584
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
585
        $classMetadataProphecy->associationMappings = [
×
586
            'relation' => ['fetch' => ClassMetadata::FETCH_EXTRA_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
587
        ];
×
588

589
        $unknownClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
590
        $unknownClassMetadataProphecy->associationMappings = [];
×
591

592
        $emProphecy = $this->prophesize(EntityManager::class);
×
593
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
594

595
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
596
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
597

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

602
    public function testResourceClassNotFoundException(): void
603
    {
604
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
605

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

609
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
610
        $classMetadataProphecy->associationMappings = [
×
611
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
612
        ];
×
613
        $emProphecy = $this->prophesize(EntityManager::class);
×
614
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
615
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
616
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
617
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
618

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

623
    public function testPropertyNotFoundException(): void
624
    {
625
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
626

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

630
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
631
        $classMetadataProphecy->associationMappings = [
×
632
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [['nullable' => false]]],
×
633
        ];
×
634
        $emProphecy = $this->prophesize(EntityManager::class);
×
635
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
636
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
637
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
638
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
639

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

644
    public function testResourceClassNotFoundExceptionPropertyNameCollection(): void
645
    {
646
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
647
        $propertyNameCollectionFactoryProphecy->create(UnknownDummy::class)->willThrow(new ResourceClassNotFoundException());
×
648

649
        $relationPropertyMetadata = new ApiProperty();
×
650
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
651
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
652
        $propertyMetadataFactoryProphecy->create(Dummy::class, 'relation', ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'])->willReturn($relationPropertyMetadata);
×
653

654
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
655
        $classMetadataProphecy->associationMappings = [
×
656
            'relation' => ['fetch' => ClassMetadata::FETCH_LAZY, 'targetEntity' => UnknownDummy::class, 'joinColumns' => [new JoinColumn(nullable: false)]],
×
657
        ];
×
658
        $emProphecy = $this->prophesize(EntityManager::class);
×
659
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
660
        $emProphecy->getClassMetadata(UnknownDummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
661
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
662
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
663
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
664
        $queryBuilderProphecy->innerJoin('o.relation', 'relation_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
665
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
666

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

671
    public function testAttributes(): void
672
    {
673
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
674

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

678
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
679
        $relationPropertyMetadata = new ApiProperty();
×
680
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
681

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

685
        $idPropertyMetadata = new ApiProperty();
×
686
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
687
        $namePropertyMetadata = new ApiProperty();
×
688
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
689

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

693
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
694

695
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
696
        $classMetadataProphecy->associationMappings = [
×
697
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
698
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
699
        ];
×
700

701
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
702

703
        foreach ($relatedNameCollection as $property) {
×
704
            if ('id' !== $property) {
×
705
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
706
            }
707
        }
708

709
        $relatedClassMetadataProphecy->associationMappings = [];
×
710

711
        $emProphecy = $this->prophesize(EntityManager::class);
×
712
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
713
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
714

715
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
716
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
717

718
        $queryBuilderProphecy->leftJoin('o.relatedDummies', 'relatedDummies_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
719
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
720
        $queryBuilderProphecy->addSelect('partial relatedDummies_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
721
        $queryBuilderProphecy->addSelect('partial relatedDummy_a2.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
722
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
723

724
        $queryBuilder = $queryBuilderProphecy->reveal();
×
725
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
726
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
727
    }
728

729
    public function testNotInAttributes(): void
730
    {
731
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
732
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
733
        $relationPropertyMetadata = new ApiProperty();
×
734
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
735

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

738
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
739

740
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
741
        $classMetadataProphecy->associationMappings = [
×
742
            'relatedDummy' => ['fetch' => 3, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
743
        ];
×
744

745
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
746
        $relatedClassMetadataProphecy->associationMappings = [];
×
747

748
        $emProphecy = $this->prophesize(EntityManager::class);
×
749
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
750

751
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
752
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
753

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

759
    public function testOnlyOneRelationNotInAttributes(): void
760
    {
761
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
762

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

766
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
767
        $relationPropertyMetadata = new ApiProperty();
×
768
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
769

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

773
        $idPropertyMetadata = new ApiProperty();
×
774
        $idPropertyMetadata = $idPropertyMetadata->withIdentifier(true);
×
775
        $namePropertyMetadata = new ApiProperty();
×
776
        $namePropertyMetadata = $namePropertyMetadata->withReadable(true);
×
777

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

781
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
782

783
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
784
        $classMetadataProphecy->associationMappings = [
×
785
            'relatedDummies' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
786
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
787
        ];
×
788

789
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
790

791
        foreach ($relatedNameCollection as $property) {
×
792
            if ('id' !== $property) {
×
793
                $relatedClassMetadataProphecy->hasField($property)->willReturn(true)->shouldBeCalled();
×
794
            }
795
        }
796

797
        $relatedClassMetadataProphecy->associationMappings = [];
×
798

799
        $emProphecy = $this->prophesize(EntityManager::class);
×
800
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
801
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
802

803
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
804
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
805

806
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
807
        $queryBuilderProphecy->addSelect('partial relatedDummy_a1.{id,name}')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
808
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
809

810
        $queryBuilder = $queryBuilderProphecy->reveal();
×
811
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false, true);
×
812
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo', AbstractNormalizer::ATTRIBUTES => ['relatedDummy' => ['id', 'name']]]));
×
813
    }
814

815
    public function testApplyToCollectionNoPartial(): void
816
    {
817
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
818

819
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
820
        $relationPropertyMetadata = new ApiProperty();
×
821
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
822

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

826
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
827

828
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
829
        $classMetadataProphecy->associationMappings = [
×
830
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
831
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
832
        ];
×
833

834
        $emProphecy = $this->prophesize(EntityManager::class);
×
835
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
836
        $relatedClassMetadataProphecy->associationMappings = [];
×
837
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
838
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
839

840
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
841
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
842

843
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
844
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
845
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
846
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
847
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
848
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
849

850
        $queryBuilder = $queryBuilderProphecy->reveal();
×
851
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
852
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
853
    }
854

855
    public function testApplyToCollectionWithANonReadableButFetchEagerProperty(): void
856
    {
857
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
858

859
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
860
        $relationPropertyMetadata = new ApiProperty();
×
861
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
862
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
863
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
864

865
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(false);
×
866
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(false);
×
867

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

871
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
872

873
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
874
        $classMetadataProphecy->associationMappings = [
×
875
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: true)], 'targetEntity' => RelatedDummy::class],
×
876
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [new JoinColumn(nullable: false)], 'targetEntity' => RelatedDummy::class],
×
877
        ];
×
878

879
        $emProphecy = $this->prophesize(EntityManager::class);
×
880
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
881
        $relatedClassMetadataProphecy->associationMappings = [];
×
882
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
883
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
884

885
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
886
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
887

888
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
889
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
890
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
891
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
892
        $queryBuilderProphecy->getDQLPart('join')->willReturn([]);
×
893
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
894

895
        $queryBuilder = $queryBuilderProphecy->reveal();
×
896
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
897
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
898
    }
899

900
    #[\PHPUnit\Framework\Attributes\DataProvider('provideExistingJoinCases')]
901
    public function testApplyToCollectionWithExistingJoin(string $joinType): void
902
    {
903
        $context = ['groups' => ['foo']];
×
904
        $callContext = ['serializer_groups' => ['foo'], 'normalization_groups' => 'foo'];
×
905

906
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
907

908
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
909
        $relationPropertyMetadata = new ApiProperty();
×
910
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
911

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

914
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
915

916
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
917
        $classMetadataProphecy->associationMappings = [
×
918
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
919
        ];
×
920

921
        $relatedClassMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
922

923
        $emProphecy = $this->prophesize(EntityManager::class);
×
924
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
925
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldBeCalled()->willReturn($relatedClassMetadataProphecy->reveal());
×
926

927
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
928
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
929
        $queryBuilderProphecy->getDQLPart('join')->willReturn([
×
930
            'o' => [
×
931
                new Join($joinType, 'o.relatedDummy', 'existing_join_alias'),
×
932
            ],
×
933
        ]);
×
934
        $queryBuilderProphecy->getDQLPart('select')->willReturn([]);
×
935
        $queryBuilderProphecy->addSelect('existing_join_alias')->shouldBeCalledTimes(1)->willReturn($queryBuilderProphecy);
×
936

937
        $queryBuilder = $queryBuilderProphecy->reveal();
×
938
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30, false);
×
939
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']), $context);
×
940
    }
941

942
    public static function provideExistingJoinCases(): iterable
943
    {
944
        yield [Join::LEFT_JOIN];
×
945
        yield [Join::INNER_JOIN];
×
946
    }
947

948
    public function testApplyToCollectionWithAReadableButNotFetchEagerProperty(): void
949
    {
950
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
951

952
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
953
        $relationPropertyMetadata = new ApiProperty();
×
954
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(false);
×
955
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
956
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
957

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

961
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
962

963
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
964
        $classMetadataProphecy->associationMappings = [
×
965
            'relatedDummy' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => RelatedDummy::class],
×
966
            'relatedDummy2' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => false]], 'targetEntity' => RelatedDummy::class],
×
967
        ];
×
968

969
        $emProphecy = $this->prophesize(EntityManager::class);
×
970
        $emProphecy->getClassMetadata(Dummy::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
971
        $emProphecy->getClassMetadata(RelatedDummy::class)->shouldNotBecalled();
×
972

973
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
974
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
975

976
        $queryBuilderProphecy->leftJoin('o.relatedDummy', 'relatedDummy_a1')->shouldNotBeCalled();
×
977
        $queryBuilderProphecy->innerJoin('o.relatedDummy2', 'relatedDummy2_a2')->shouldNotBeCalled();
×
978
        $queryBuilderProphecy->addSelect('relatedDummy_a1')->shouldNotBeCalled();
×
979
        $queryBuilderProphecy->addSelect('relatedDummy2_a2')->shouldNotBeCalled();
×
980

981
        $queryBuilder = $queryBuilderProphecy->reveal();
×
982
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
983
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), Dummy::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'foo']));
×
984
    }
985

986
    public function testAvoidFetchCollectionOnIriOnlyProperty(): void
987
    {
988
        $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
×
989

990
        $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
×
991
        $relationPropertyMetadata = new ApiProperty();
×
992
        $relationPropertyMetadata = $relationPropertyMetadata->withFetchEager(true);
×
993
        $relationPropertyMetadata = $relationPropertyMetadata->withReadableLink(true);
×
994
        $relationPropertyMetadata = $relationPropertyMetadata->withReadable(true);
×
995
        $relationPropertyMetadata = $relationPropertyMetadata->withUriTemplate('/property-collection-relations');
×
996

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

999
        $queryBuilderProphecy = $this->prophesize(QueryBuilder::class);
×
1000

1001
        $classMetadataProphecy = $this->prophesize(ClassMetadata::class);
×
1002
        $classMetadataProphecy->associationMappings = [
×
1003
            'propertyCollectionIriOnlyRelation' => ['fetch' => ClassMetadata::FETCH_EAGER, 'joinColumns' => [['nullable' => true]], 'targetEntity' => PropertyCollectionIriOnlyRelation::class],
×
1004
        ];
×
1005

1006
        $emProphecy = $this->prophesize(EntityManager::class);
×
1007
        $emProphecy->getClassMetadata(PropertyCollectionIriOnly::class)->shouldBeCalled()->willReturn($classMetadataProphecy->reveal());
×
1008
        $emProphecy->getClassMetadata(PropertyCollectionIriOnlyRelation::class)->shouldNotBecalled();
×
1009

1010
        $queryBuilderProphecy->getRootAliases()->willReturn(['o']);
×
1011
        $queryBuilderProphecy->getEntityManager()->willReturn($emProphecy);
×
1012

1013
        $queryBuilderProphecy->leftJoin('o.propertyCollectionIriOnlyRelation', 'propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
1014
        $queryBuilderProphecy->addSelect('propertyCollectionIriOnlyRelation_a1')->shouldNotBeCalled();
×
1015

1016
        $queryBuilder = $queryBuilderProphecy->reveal();
×
1017
        $eagerExtensionTest = new EagerLoadingExtension($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), 30);
×
1018
        $eagerExtensionTest->applyToCollection($queryBuilder, new QueryNameGenerator(), PropertyCollectionIriOnly::class, new GetCollection(normalizationContext: [AbstractNormalizer::GROUPS => 'read']));
×
1019
    }
1020
}
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