Skip to content

Commit 6f75dfe

Browse files
committed
bug #46098 [Form] Fix same choice loader with different choice values (HeahDude)
This PR was squashed before being merged into the 4.4 branch. Discussion ---------- [Form] Fix same choice loader with different choice values | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | #44655 | License | MIT | Doc PR | ~ It appears that deprecating the caching in the `LazyChoiceList` (cf #18359) was a mistake. The bug went under the radar because in practice every choice field has its own loader instance. However, optimizations made in #30994 then revealed the flaw (cf #42206) as the loaders were actually shared across many fields. While working on a fix I ended up implementing something similar to what's proposed in #44655. I'll send a PR for 5.4 as well. Commits ------- 65cbf18ea8 [Form] Fix same choice loader with different choice values
2 parents ba6f5d1 + 7deb587 commit 6f75dfe

File tree

4 files changed

+39
-15
lines changed

4 files changed

+39
-15
lines changed

Form/ChoiceList/DoctrineChoiceLoader.php

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
use Doctrine\Persistence\ObjectManager;
1515
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
16-
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
1716
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
1817

1918
/**
@@ -29,9 +28,9 @@ class DoctrineChoiceLoader implements ChoiceLoaderInterface
2928
private $objectLoader;
3029

3130
/**
32-
* @var ChoiceListInterface
31+
* @var array|null
3332
*/
34-
private $choiceList;
33+
private $choices;
3534

3635
/**
3736
* Creates a new choice loader.
@@ -74,15 +73,13 @@ public function __construct(ObjectManager $manager, string $class, IdReader $idR
7473
*/
7574
public function loadChoiceList($value = null)
7675
{
77-
if ($this->choiceList) {
78-
return $this->choiceList;
76+
if (null === $this->choices) {
77+
$this->choices = $this->objectLoader
78+
? $this->objectLoader->getEntities()
79+
: $this->manager->getRepository($this->class)->findAll();
7980
}
8081

81-
$objects = $this->objectLoader
82-
? $this->objectLoader->getEntities()
83-
: $this->manager->getRepository($this->class)->findAll();
84-
85-
return $this->choiceList = new ArrayChoiceList($objects, $value);
82+
return new ArrayChoiceList($this->choices, $value);
8683
}
8784

8885
/**
@@ -100,7 +97,7 @@ public function loadValuesForChoices(array $choices, $value = null)
10097
$optimize = $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader);
10198

10299
// Attention: This optimization does not check choices for existence
103-
if ($optimize && !$this->choiceList && $this->idReader->isSingleId()) {
100+
if ($optimize && !$this->choices && $this->idReader->isSingleId()) {
104101
$values = [];
105102

106103
// Maintain order and indices of the given objects
@@ -136,7 +133,7 @@ public function loadChoicesForValues(array $values, $value = null)
136133
// a single-field identifier
137134
$optimize = $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]);
138135

139-
if ($optimize && !$this->choiceList && $this->objectLoader && $this->idReader->isSingleId()) {
136+
if ($optimize && !$this->choices && $this->objectLoader && $this->idReader->isSingleId()) {
140137
$unorderedObjects = $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values);
141138
$objectsById = [];
142139
$objects = [];

Tests/Form/ChoiceList/DoctrineChoiceLoaderTest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,7 @@ public function testLoadChoiceListUsesObjectLoaderIfAvailable()
146146
$this->assertEquals($choiceList, $loaded = $loader->loadChoiceList());
147147

148148
// no further loads on subsequent calls
149-
150-
$this->assertSame($loaded, $loader->loadChoiceList());
149+
$this->assertEquals($loaded, $loader->loadChoiceList());
151150
}
152151

153152
public function testLoadValuesForChoices()

Tests/Form/Type/EntityTypeTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,4 +1779,32 @@ public function testSubmitNullMultipleUsesDefaultEmptyData()
17791779
$this->assertEquals($collection, $form->getNormData());
17801780
$this->assertEquals($collection, $form->getData());
17811781
}
1782+
1783+
public function testWithSameLoaderAndDifferentChoiceValueCallbacks()
1784+
{
1785+
$entity1 = new SingleIntIdEntity(1, 'Foo');
1786+
$entity2 = new SingleIntIdEntity(2, 'Bar');
1787+
$this->persist([$entity1, $entity2]);
1788+
1789+
$view = $this->factory->create(FormTypeTest::TESTED_TYPE)
1790+
->add('entity_one', self::TESTED_TYPE, [
1791+
'em' => 'default',
1792+
'class' => self::SINGLE_IDENT_CLASS,
1793+
])
1794+
->add('entity_two', self::TESTED_TYPE, [
1795+
'em' => 'default',
1796+
'class' => self::SINGLE_IDENT_CLASS,
1797+
'choice_value' => function ($choice) {
1798+
return $choice ? $choice->name : '';
1799+
},
1800+
])
1801+
->createView()
1802+
;
1803+
1804+
$this->assertSame('1', $view['entity_one']->vars['choices'][1]->value);
1805+
$this->assertSame('2', $view['entity_one']->vars['choices'][2]->value);
1806+
1807+
$this->assertSame('Foo', $view['entity_two']->vars['choices']['Foo']->value);
1808+
$this->assertSame('Bar', $view['entity_two']->vars['choices']['Bar']->value);
1809+
}
17821810
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"symfony/stopwatch": "^3.4|^4.0|^5.0",
3030
"symfony/config": "^4.2|^5.0",
3131
"symfony/dependency-injection": "^3.4|^4.0|^5.0",
32-
"symfony/form": "^4.4.11|^5.0.11",
32+
"symfony/form": "^4.4.41|^5.0.11",
3333
"symfony/http-kernel": "^4.3.7",
3434
"symfony/messenger": "^4.4|^5.0",
3535
"symfony/property-access": "^3.4|^4.0|^5.0",

0 commit comments

Comments
 (0)