Skip to content

Commit 0df0b0f

Browse files
Merge branch '7.4' into 8.0
* 7.4: (21 commits) [ObjectMapper] lazy loading [Filesystem] Unify logic for isAbsolute() in Path [DependencyInjection] Include return type in AppReference shape [Finder] Make method calls explicit in ExcludeDirectoryFilterIterator [Cache] Remove unset call on undefined variable in PhpArrayAdapter [Twig] Ensure WrappedTemplatedEmail::getReturnPath() returns a string [Routing] Simplify importing routes defined on controller services Fix tests [DependendcyInjection] Improve shape for "from_callable" definitions [PHPDoc] Fix various PHPDoc syntax errors [Console] Add missing VERBOSITY_SILENT case in CommandDataCollector [Notifier] Remove unused $transportName argument in EmailChannel::notify() [Translation] Remove an unused argument passed to parseNode() method [HttpClient] Reject 3xx pushed responses [Serializer] Use Asia/Tokyo instead of Japan in tests [ProxyManagerBridge] Remove comment that reference github discussion [JsonPath] Remove unused "nothing" property from JsonCrawler [ErrorHandler] Improve PHPDoc precision in SerializerErrorRenderer [Routing] Fix matching the "0" URL [Form] Fix EnumType choice_label logic for grouped choices ...
2 parents 5843655 + fd1752e commit 0df0b0f

File tree

2 files changed

+119
-4
lines changed

2 files changed

+119
-4
lines changed

Extension/Core/Type/EnumType.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ public function configureOptions(OptionsResolver $resolver): void
3131
->setAllowedValues('class', enum_exists(...))
3232
->setDefault('choices', static fn (Options $options): array => $options['class']::cases())
3333
->setDefault('choice_label', static function (Options $options) {
34-
if (\is_array($options['choices']) && !array_is_list($options['choices'])) {
35-
return null;
36-
}
34+
return static function (\UnitEnum $choice, int|string $key): string|TranslatableInterface {
35+
if (\is_int($key)) {
36+
// Key is an integer, use the enum's name (or translatable)
37+
return $choice instanceof TranslatableInterface ? $choice : $choice->name;
38+
}
3739

38-
return static fn (\UnitEnum $choice) => $choice instanceof TranslatableInterface ? $choice : $choice->name;
40+
// Key is a string, use it as the label
41+
return $key;
42+
};
3943
})
4044
->setDefault('choice_value', static function (Options $options): ?\Closure {
4145
if (!is_a($options['class'], \BackedEnum::class, true)) {

Tests/Extension/Core/Type/EnumTypeTest.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Extension\Core\Type;
1313

1414
use PHPUnit\Framework\Attributes\DataProvider;
15+
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
16+
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
1517
use Symfony\Component\Form\Extension\Core\Type\EnumType;
1618
use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTestCase;
1719
use Symfony\Component\Form\Tests\Fixtures\Answer;
@@ -304,6 +306,115 @@ public function testChoicesWithLabels()
304306
$this->assertSame('no', $view->children[1]->vars['label']);
305307
}
306308

309+
public function testGroupedEnumChoices()
310+
{
311+
$form = $this->factory->create($this->getTestedType(), null, [
312+
'multiple' => false,
313+
'expanded' => true,
314+
'class' => Answer::class,
315+
'choices' => [
316+
'Group 1' => [Answer::Yes, Answer::No],
317+
'Group 2' => [Answer::FourtyTwo],
318+
],
319+
]);
320+
$view = $form->createView();
321+
$this->assertCount(2, $view->vars['choices']['Group 1']->choices);
322+
$this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label);
323+
$this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label);
324+
$this->assertCount(1, $view->vars['choices']['Group 2']->choices);
325+
$this->assertSame('FourtyTwo', $view->vars['choices']['Group 2']->choices[2]->label);
326+
}
327+
328+
public function testGroupedEnumChoicesWithCustomLabels()
329+
{
330+
$form = $this->factory->create($this->getTestedType(), null, [
331+
'multiple' => false,
332+
'expanded' => true,
333+
'class' => Answer::class,
334+
'choices' => [
335+
'Group 1' => [
336+
'Custom Yes' => Answer::Yes,
337+
'Custom No' => Answer::No,
338+
],
339+
'Group 2' => [
340+
'Custom 42' => Answer::FourtyTwo,
341+
],
342+
],
343+
]);
344+
$view = $form->createView();
345+
346+
// Test Group 1
347+
$this->assertCount(2, $view->vars['choices']['Group 1']->choices);
348+
$this->assertSame('Custom Yes', $view->vars['choices']['Group 1']->choices[0]->label);
349+
$this->assertSame('Custom No', $view->vars['choices']['Group 1']->choices[1]->label);
350+
351+
// Test Group 2
352+
$this->assertCount(1, $view->vars['choices']['Group 2']->choices);
353+
$this->assertSame('Custom 42', $view->vars['choices']['Group 2']->choices[2]->label);
354+
}
355+
356+
public function testMixedGroupedAndSingleChoices()
357+
{
358+
$form = $this->factory->create($this->getTestedType(), null, [
359+
'multiple' => false,
360+
'expanded' => true,
361+
'class' => Answer::class,
362+
'choices' => [
363+
'Group 1' => [Answer::Yes, Answer::No],
364+
'Custom 42' => Answer::FourtyTwo,
365+
],
366+
]);
367+
$view = $form->createView();
368+
369+
// Group 1 (simple list) → enum names
370+
$this->assertInstanceOf(ChoiceGroupView::class, $view->vars['choices']['Group 1']);
371+
$this->assertCount(2, $view->vars['choices']['Group 1']->choices);
372+
$this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label);
373+
$this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label);
374+
375+
// Single custom → custom label (treated as flat choice)
376+
$customChoice = $view->vars['choices'][2];
377+
$this->assertInstanceOf(ChoiceView::class, $customChoice);
378+
$this->assertSame('Custom 42', $customChoice->label);
379+
}
380+
381+
public function testMixedLabeledAndUnlabeledChoices()
382+
{
383+
$form = $this->factory->create($this->getTestedType(), null, [
384+
'multiple' => false,
385+
'expanded' => true,
386+
'class' => Answer::class,
387+
'choices' => [
388+
Answer::Yes,
389+
Answer::No,
390+
'Custom 42' => Answer::FourtyTwo,
391+
],
392+
]);
393+
$view = $form->createView();
394+
// Assertions: names for unlabeled, custom for labeled
395+
$children = array_values($view->children); // Numeric access
396+
$this->assertSame('Yes', $children[0]->vars['label']);
397+
$this->assertSame('No', $children[1]->vars['label']);
398+
$this->assertSame('Custom 42', $children[2]->vars['label']);
399+
}
400+
401+
public function testEnumChoicesWithNumericCustomLabels()
402+
{
403+
$form = $this->factory->create($this->getTestedType(), null, [
404+
'multiple' => false,
405+
'expanded' => true,
406+
'class' => Answer::class,
407+
'choice_label' => null, // Explicitly override to use keys as labels for numeric customs
408+
'choices' => [
409+
'34' => Answer::Yes,
410+
'2' => Answer::No,
411+
],
412+
]);
413+
$view = $form->createView();
414+
$this->assertSame('34', $view->children[0]->vars['label']);
415+
$this->assertSame('2', $view->children[1]->vars['label']);
416+
}
417+
307418
protected function getTestOptions(): array
308419
{
309420
return ['class' => Suit::class];

0 commit comments

Comments
 (0)