Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6342009

Browse files
[DoctrineBridge] Map entities by single-key when disabling auto-mapping
1 parent a8b4739 commit 6342009

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\HttpFoundation\Request;
2222
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
2323
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
24+
use Symfony\Component\HttpKernel\Exception\NearMissValueResolverException;
2425
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
2526

2627
/**
@@ -123,15 +124,21 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam
123124
{
124125
if (\is_array($options->id)) {
125126
$id = [];
127+
$usedAttributes = [];
126128
foreach ($options->id as $field) {
127129
// Convert "%s_uuid" to "foobar_uuid"
128130
if (str_contains($field, '%s')) {
129131
$field = sprintf($field, $name);
130132
}
131133

134+
$usedAttributes[] = $field;
132135
$id[$field] = $request->attributes->get($field);
133136
}
134137

138+
if ($usedAttributes) {
139+
$request->attributes->set('_entity_used_attributes', array_merge($request->attributes->get('_entity_used_attributes') ?? [], $usedAttributes));
140+
}
141+
135142
return $id;
136143
}
137144

@@ -140,10 +147,14 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam
140147
}
141148

142149
if ($request->attributes->has($name)) {
150+
$request->attributes->set('_entity_used_attributes', array_merge($request->attributes->get('_entity_used_attributes') ?? [], [$name]));
151+
143152
return $request->attributes->get($name) ?? ($options->stripNull ? false : null);
144153
}
145154

146155
if (!$options->id && $request->attributes->has('id')) {
156+
$request->attributes->set('_entity_used_attributes', array_merge($request->attributes->get('_entity_used_attributes') ?? [], ['id']));
157+
147158
return $request->attributes->get('id') ?? ($options->stripNull ? false : null);
148159
}
149160

@@ -152,11 +163,15 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam
152163

153164
private function getCriteria(Request $request, MapEntity $options, ObjectManager $manager): array
154165
{
155-
if (null === $mapping = $options->mapping) {
156-
$mapping = $request->attributes->keys();
166+
$singleKey = [] === $options->mapping;
167+
168+
if ($autoMapping = !$mapping = $options->mapping) {
169+
$mapping = $request->attributes->all();
170+
unset($mapping['_entity_used_attributes']);
171+
$mapping = array_keys($mapping);
157172
}
158173

159-
if ($mapping && \is_array($mapping) && array_is_list($mapping)) {
174+
if ($mapping && array_is_list($mapping)) {
160175
$mapping = array_combine($mapping, $mapping);
161176
}
162177

@@ -176,13 +191,33 @@ private function getCriteria(Request $request, MapEntity $options, ObjectManager
176191

177192
$criteria = [];
178193
$metadata = $manager->getClassMetadata($options->class);
194+
$usedAttributes = [];
195+
196+
if ($singleKey) {
197+
foreach ($request->attributes->get('_entity_used_attributes') ?? [] as $attribute) {
198+
unset($mapping[$attribute]);
199+
}
200+
}
179201

180202
foreach ($mapping as $attribute => $field) {
181203
if (!$metadata->hasField($field) && (!$metadata->hasAssociation($field) || !$metadata->isSingleValuedAssociation($field))) {
204+
if (!$autoMapping) {
205+
throw new NearMissValueResolverException(sprintf('Invalid mapping in #[MapEntity] attribute: there are no field or association named "%s" for entities of type "%s".', $field, $options->class));
206+
}
207+
182208
continue;
183209
}
184210

211+
$usedAttributes[] = $attribute;
185212
$criteria[$field] = $request->attributes->get($attribute);
213+
214+
if ($singleKey) {
215+
break;
216+
}
217+
}
218+
219+
if ($usedAttributes) {
220+
$request->attributes->set('_entity_used_attributes', array_merge($request->attributes->get('_entity_used_attributes') ?? [], $usedAttributes));
186221
}
187222

188223
if ($options->stripNull) {

src/Symfony/Bridge/Doctrine/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
7.1
55
---
66

7+
* Map entities by single-key when disabling auto-mapping
78
* Deprecate the `DoctrineExtractor::getTypes()` method, use `DoctrineExtractor::getType()` instead
89

910
7.0

src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,60 @@ public function testResolveWithMappingAndExclude()
257257
$this->assertSame([$object], $resolver->resolve($request, $argument));
258258
}
259259

260+
public function testResolveWithMultipleEntitiesAndNoAutoMapping()
261+
{
262+
$manager = $this->getMockBuilder(ObjectManager::class)->getMock();
263+
$registry = $this->createRegistry($manager);
264+
$resolver = new EntityValueResolver($registry);
265+
266+
$request = new Request();
267+
$request->attributes->set('foo', 1);
268+
$request->attributes->set('bar', 2);
269+
270+
$argument1 = $this->createArgument('Foo', new MapEntity('Foo', mapping: []));
271+
$argument2 = $this->createArgument('Bar', new MapEntity('Bar', mapping: []));
272+
273+
$metadata1 = $this->getMockBuilder(ClassMetadata::class)->getMock();
274+
$metadata1->expects($this->once())
275+
->method('hasField')
276+
->with('foo')
277+
->willReturn(true);
278+
279+
$metadata2 = $this->getMockBuilder(ClassMetadata::class)->getMock();
280+
$metadata2->expects($this->once())
281+
->method('hasField')
282+
->with('bar')
283+
->willReturn(true);
284+
285+
$manager->expects($this->any())
286+
->method('getClassMetadata')
287+
->willReturnCallback(static fn ($v) => match ($v) {
288+
'Foo' => $metadata1,
289+
'Bar' => $metadata2,
290+
});
291+
292+
$object1 = new \stdClass();
293+
$object2 = new \stdClass();
294+
295+
$repository = $this->getMockBuilder(ObjectRepository::class)->getMock();
296+
$repository->expects($this->any())
297+
->method('findOneBy')
298+
->willReturnCallback(static fn ($v) => match ($v) {
299+
['foo' => 1] => $object1,
300+
['bar' => 2] => $object2,
301+
});
302+
303+
$manager->expects($this->any())
304+
->method('getRepository')
305+
->willReturn($repository);
306+
307+
$this->assertSame([$object1], $resolver->resolve($request, $argument1));
308+
$this->assertSame(['foo'], $request->attributes->get('_entity_used_attributes'));
309+
310+
$this->assertSame([$object2], $resolver->resolve($request, $argument2));
311+
$this->assertSame(['foo', 'bar'], $request->attributes->get('_entity_used_attributes'));
312+
}
313+
260314
public function testExceptionWithExpressionIfNoLanguageAvailable()
261315
{
262316
$manager = $this->getMockBuilder(ObjectManager::class)->getMock();

0 commit comments

Comments
 (0)