diff --git a/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php b/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php index b529a5869cec5..106cb341d7003 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php @@ -31,11 +31,15 @@ public function configureOptions(OptionsResolver $resolver): void ->setAllowedValues('class', enum_exists(...)) ->setDefault('choices', static fn (Options $options): array => $options['class']::cases()) ->setDefault('choice_label', static function (Options $options) { - if (\is_array($options['choices']) && !array_is_list($options['choices'])) { - return null; - } + return static function (\UnitEnum $choice, int|string $key): string|TranslatableInterface { + if (\is_int($key)) { + // Key is an integer, use the enum's name (or translatable) + return $choice instanceof TranslatableInterface ? $choice : $choice->name; + } - return static fn (\UnitEnum $choice) => $choice instanceof TranslatableInterface ? $choice : $choice->name; + // Key is a string, use it as the label + return $key; + }; }) ->setDefault('choice_value', static function (Options $options): ?\Closure { if (!is_a($options['class'], \BackedEnum::class, true)) { diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php index 3916d86ebca22..dfd162070d280 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php @@ -11,6 +11,8 @@ namespace Extension\Core\Type; +use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTestCase; use Symfony\Component\Form\Tests\Fixtures\Answer; @@ -311,6 +313,115 @@ public function testChoicesWithLabels() $this->assertSame('no', $view->children[1]->vars['label']); } + public function testGroupedEnumChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [Answer::Yes, Answer::No], + 'Group 2' => [Answer::FourtyTwo], + ], + ]); + $view = $form->createView(); + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label); + $this->assertCount(1, $view->vars['choices']['Group 2']->choices); + $this->assertSame('FourtyTwo', $view->vars['choices']['Group 2']->choices[2]->label); + } + + public function testGroupedEnumChoicesWithCustomLabels() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [ + 'Custom Yes' => Answer::Yes, + 'Custom No' => Answer::No, + ], + 'Group 2' => [ + 'Custom 42' => Answer::FourtyTwo, + ], + ], + ]); + $view = $form->createView(); + + // Test Group 1 + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Custom Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('Custom No', $view->vars['choices']['Group 1']->choices[1]->label); + + // Test Group 2 + $this->assertCount(1, $view->vars['choices']['Group 2']->choices); + $this->assertSame('Custom 42', $view->vars['choices']['Group 2']->choices[2]->label); + } + + public function testMixedGroupedAndSingleChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [Answer::Yes, Answer::No], + 'Custom 42' => Answer::FourtyTwo, + ], + ]); + $view = $form->createView(); + + // Group 1 (simple list) → enum names + $this->assertInstanceOf(ChoiceGroupView::class, $view->vars['choices']['Group 1']); + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label); + + // Single custom → custom label (treated as flat choice) + $customChoice = $view->vars['choices'][2]; + $this->assertInstanceOf(ChoiceView::class, $customChoice); + $this->assertSame('Custom 42', $customChoice->label); + } + + public function testMixedLabeledAndUnlabeledChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + Answer::Yes, + Answer::No, + 'Custom 42' => Answer::FourtyTwo, + ], + ]); + $view = $form->createView(); + // Assertions: names for unlabeled, custom for labeled + $children = array_values($view->children); // Numeric access + $this->assertSame('Yes', $children[0]->vars['label']); + $this->assertSame('No', $children[1]->vars['label']); + $this->assertSame('Custom 42', $children[2]->vars['label']); + } + + public function testEnumChoicesWithNumericCustomLabels() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choice_label' => null, // Explicitly override to use keys as labels for numeric customs + 'choices' => [ + '34' => Answer::Yes, + '2' => Answer::No, + ], + ]); + $view = $form->createView(); + $this->assertSame('34', $view->children[0]->vars['label']); + $this->assertSame('2', $view->children[1]->vars['label']); + } + protected function getTestOptions(): array { return ['class' => Suit::class];