@@ -138,6 +138,146 @@ public function testGetItemDoubleIdentifier(): void
138138 $ this ->assertEquals ($ returnObject , $ dataProvider ->provide ($ operation , ['ida ' => 1 , 'idb ' => 2 ], $ context ));
139139 }
140140
141+ public function testGetItemWithFetchDataFalseOnSubresourceFiltersParentLink (): void
142+ {
143+ $ reference = new Employee ();
144+
145+ $ classMetadataMock = $ this ->createMock (ClassMetadata::class);
146+ $ classMetadataMock ->method ('getIdentifierFieldNames ' )->willReturn (['id ' ]);
147+
148+ $ managerMock = $ this ->createMock (EntityManagerInterface::class);
149+ $ managerMock ->method ('getClassMetadata ' )->with (Employee::class)->willReturn ($ classMetadataMock );
150+ $ managerMock ->expects ($ this ->once ())
151+ ->method ('getReference ' )
152+ ->with (Employee::class, ['id ' => 2 ])
153+ ->willReturn ($ reference );
154+
155+ $ managerRegistryMock = $ this ->createMock (ManagerRegistry::class);
156+ $ managerRegistryMock ->method ('getManagerForClass ' )->with (Employee::class)->willReturn ($ managerMock );
157+
158+ $ operation = (new Get ())->withUriVariables ([
159+ 'companyId ' => (new Link ())->withFromClass (Company::class)->withToProperty ('company ' ),
160+ 'id ' => (new Link ())->withFromClass (Employee::class)->withIdentifiers (['id ' ]),
161+ ])->withName ('get ' )->withClass (Employee::class);
162+
163+ $ dataProvider = new ItemProvider (
164+ $ this ->createStub (ResourceMetadataCollectionFactoryInterface::class),
165+ $ managerRegistryMock ,
166+ );
167+
168+ $ this ->assertSame ($ reference , $ dataProvider ->provide ($ operation , ['companyId ' => 1 , 'id ' => 2 ], ['fetch_data ' => false ]));
169+ }
170+
171+ public function testGetItemWithFetchDataFalseMapsRenamedIdentifierUriVariable (): void
172+ {
173+ $ reference = new Employee ();
174+
175+ $ classMetadataMock = $ this ->createMock (ClassMetadata::class);
176+ $ classMetadataMock ->method ('getIdentifierFieldNames ' )->willReturn (['id ' ]);
177+
178+ $ managerMock = $ this ->createMock (EntityManagerInterface::class);
179+ $ managerMock ->method ('getClassMetadata ' )->with (Employee::class)->willReturn ($ classMetadataMock );
180+ $ managerMock ->expects ($ this ->once ())
181+ ->method ('getReference ' )
182+ ->with (Employee::class, ['id ' => 2 ])
183+ ->willReturn ($ reference );
184+
185+ $ managerRegistryMock = $ this ->createMock (ManagerRegistry::class);
186+ $ managerRegistryMock ->method ('getManagerForClass ' )->with (Employee::class)->willReturn ($ managerMock );
187+
188+ // The identifier uriVariable is named "employeeId" while the entity's own identifier field is "id".
189+ $ operation = (new Get ())->withUriVariables ([
190+ 'companyId ' => (new Link ())->withFromClass (Company::class)->withToProperty ('company ' ),
191+ 'employeeId ' => (new Link ())->withFromClass (Employee::class)->withIdentifiers (['id ' ])->withParameterName ('employeeId ' ),
192+ ])->withName ('get ' )->withClass (Employee::class);
193+
194+ $ dataProvider = new ItemProvider (
195+ $ this ->createStub (ResourceMetadataCollectionFactoryInterface::class),
196+ $ managerRegistryMock ,
197+ );
198+
199+ $ this ->assertSame ($ reference , $ dataProvider ->provide ($ operation , ['companyId ' => 1 , 'employeeId ' => 2 ], ['fetch_data ' => false ]));
200+ }
201+
202+ public function testGetItemWithFetchDataFalseFallsBackToQueryWhenOwnIdentifierMissing (): void
203+ {
204+ $ returnObject = new \stdClass ();
205+
206+ $ queryMock = $ this ->createMock ($ this ->getQueryClass ());
207+ $ queryMock ->method ('getOneOrNullResult ' )->willReturn ($ returnObject );
208+
209+ $ queryBuilderMock = $ this ->createMock (QueryBuilder::class);
210+ $ queryBuilderMock ->method ('getQuery ' )->willReturn ($ queryMock );
211+ $ queryBuilderMock ->method ('getRootAliases ' )->willReturn (['o ' ]);
212+
213+ $ classMetadataMock = $ this ->createMock (ClassMetadata::class);
214+ $ classMetadataMock ->method ('getIdentifierFieldNames ' )->willReturn (['id ' ]);
215+
216+ $ repositoryMock = $ this ->createMock (EntityRepository::class);
217+ $ repositoryMock ->method ('createQueryBuilder ' )->with ('o ' )->willReturn ($ queryBuilderMock );
218+
219+ $ managerMock = $ this ->createMock (EntityManagerInterface::class);
220+ $ managerMock ->method ('getClassMetadata ' )->willReturn ($ classMetadataMock );
221+ $ managerMock ->method ('getRepository ' )->willReturn ($ repositoryMock );
222+ // Only the parent link is provided: the own identifier cannot be resolved to a reference,
223+ // so we must fall back to the query that resolves the link instead of calling getReference().
224+ $ managerMock ->expects ($ this ->never ())->method ('getReference ' );
225+
226+ $ managerRegistryMock = $ this ->createMock (ManagerRegistry::class);
227+ $ managerRegistryMock ->method ('getManagerForClass ' )->willReturn ($ managerMock );
228+
229+ $ operation = (new Get ())->withUriVariables ([
230+ 'companyId ' => (new Link ())->withFromClass (Company::class)->withToProperty ('company ' )->withIdentifiers (['id ' ]),
231+ 'id ' => (new Link ())->withFromClass (Employee::class)->withIdentifiers (['id ' ]),
232+ ])->withName ('get ' )->withClass (Employee::class);
233+
234+ $ dataProvider = new ItemProvider (
235+ $ this ->createStub (ResourceMetadataCollectionFactoryInterface::class),
236+ $ managerRegistryMock ,
237+ );
238+
239+ $ this ->assertSame ($ returnObject , $ dataProvider ->provide ($ operation , ['companyId ' => 1 ], ['fetch_data ' => false ]));
240+ }
241+
242+ public function testGetItemWithFetchDataFalseFallsBackToQueryWhenIdentifierIsNotADoctrineIdentifier (): void
243+ {
244+ $ returnObject = new \stdClass ();
245+
246+ $ queryMock = $ this ->createMock ($ this ->getQueryClass ());
247+ $ queryMock ->method ('getOneOrNullResult ' )->willReturn ($ returnObject );
248+
249+ $ queryBuilderMock = $ this ->createMock (QueryBuilder::class);
250+ $ queryBuilderMock ->method ('getQuery ' )->willReturn ($ queryMock );
251+ $ queryBuilderMock ->method ('getRootAliases ' )->willReturn (['o ' ]);
252+
253+ // The Doctrine identifier is "id" while the resource exposes "uuid" as its API identifier:
254+ // getReference() cannot be built from "uuid", so we must fall back to the query.
255+ $ classMetadataMock = $ this ->createMock (ClassMetadata::class);
256+ $ classMetadataMock ->method ('getIdentifierFieldNames ' )->willReturn (['id ' ]);
257+
258+ $ repositoryMock = $ this ->createMock (EntityRepository::class);
259+ $ repositoryMock ->method ('createQueryBuilder ' )->with ('o ' )->willReturn ($ queryBuilderMock );
260+
261+ $ managerMock = $ this ->createMock (EntityManagerInterface::class);
262+ $ managerMock ->method ('getClassMetadata ' )->willReturn ($ classMetadataMock );
263+ $ managerMock ->method ('getRepository ' )->willReturn ($ repositoryMock );
264+ $ managerMock ->expects ($ this ->never ())->method ('getReference ' );
265+
266+ $ managerRegistryMock = $ this ->createMock (ManagerRegistry::class);
267+ $ managerRegistryMock ->method ('getManagerForClass ' )->willReturn ($ managerMock );
268+
269+ $ operation = (new Get ())->withUriVariables ([
270+ 'uuid ' => (new Link ())->withFromClass (Employee::class)->withIdentifiers (['uuid ' ])->withParameterName ('uuid ' ),
271+ ])->withName ('get ' )->withClass (Employee::class);
272+
273+ $ dataProvider = new ItemProvider (
274+ $ this ->createStub (ResourceMetadataCollectionFactoryInterface::class),
275+ $ managerRegistryMock ,
276+ );
277+
278+ $ this ->assertSame ($ returnObject , $ dataProvider ->provide ($ operation , ['uuid ' => '61817181-0ecc-42fb-a6e7-d97f2ddcb344 ' ], ['fetch_data ' => false ]));
279+ }
280+
141281 public function testQueryResultExtension (): void
142282 {
143283 $ returnObject = new \stdClass ();
0 commit comments