8000 [Form] Add `duplicate_preferred_choices` option to `ChoiceType` · symfony/symfony@927aed0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 927aed0

Browse files
[Form] Add duplicate_preferred_choices option to ChoiceType
1 parent 43066ff commit 927aed0

11 files changed

+101
-28
lines changed

src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTestCase.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,31 @@ public function testSingleChoiceWithPreferred()
576576
);
577577
}
578578

579+
public function testSingleChoiceWithPreferredIsNotDuplicated()
580+
{
581+
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [
582+
'choices' => ['Choice&A' => '&a', 'Choice&B' => '&b'],
583+
'preferred_choices' => ['&b'],
584+
'duplicate_preferred_choices' => false,
585+
'multiple' => false,
586+
'expanded' => false,
587+
]);
588+
589+
$this->assertWidgetMatchesXpath($form->createView(), ['separator' => '-- sep --', 'attr' => ['class' => 'my&class']],
590+
'/select
591+
[@name="name"]
592+
[@class="my&class form-control"]
593+
[not(@required)]
594+
[
595+
./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
596+
/following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
597+
/following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
598+
]
599+
[count(./option)=3]
600+
'
601+
);
602+
}
603+
579604
public function testSingleChoiceWithSelectedPreferred()
580605
{
581606
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [

src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTestCase.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,31 @@ public function testSingleChoiceWithPreferred()
584584
);
585585
}
586586

587+
public function testSingleChoiceWithPreferredIsNotDuplicated()
588+
{
589+
$form = $this->factory->createNamed('name', ChoiceType::class, '&a', [
590+
'choices' => ['Choice&A' => '&a', 'Choice&B' => '&b'],
591+
'preferred_choices' => ['&b'],
592+
'duplicate_preferred_choices' => false,
593+
'multiple' => false,
594+
'expanded' => false,
595+
]);
596+
597+
$this->assertWidgetMatchesXpath($form->createView(), ['separator' => '-- sep --', 'attr' => ['class' => 'my&class']],
598+
'/select
599+
[@name="name"]
600+
[@class="my&class form-select"]
601+
[not(@required)]
602+
[
603+
./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
604+
/following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
605+
/following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
606+
]
607+
[count(./option)=3]
608+
'
609+
);
610+
}
611+
587612
public function testSingleChoiceWithSelectedPreferred()
588613
{
589614
$form = $this->factory->createNamed('name', ChoiceType::class, '&a', [

src/Symfony/Bridge/Twig/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"symfony/asset-mapper": "^6.3|^7.0",
3030
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
3131
"symfony/finder": "^5.4|^6.0|^7.0",
32-
"symfony/form": "^6.3|^7.0",
32+
"symfony/form": "^6.4|^7.0",
3333
"symfony/html-sanitizer": "^6.1|^7.0",
3434
"symfony/http-foundation": "^5.4|^6.0|^7.0",
3535
"symfony/http-kernel": "^6.2|^7.0",

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ CHANGELOG
66

77
* Deprecate using `DateTime` or `DateTimeImmutable` model data with a different timezone than configured with the
88
`model_timezone` option in `DateType`, `DateTimeType`, and `TimeType`
9+
* Add `duplicate_preferred_choices` option in `ChoiceType`
10+
* Add `$duplicatePreferredChoices` parameter to `ChoiceListFactoryInterface::createView`
911

1012
6.3
1113
---

src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, mixed $value
145145
return $this->lists[$hash];
146146
}
147147

148-
public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []): ChoiceListView
148+
public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []/*, bool $duplicatePreferredChoices = true*/): ChoiceListView
149149
{
150+
$duplicatePreferredChoices = func_num_args() > 7 ? func_get_arg(7) : true;
150151
$cache = true;
151152

152153
if ($preferredChoices instanceof Cache\PreferredChoice) {
@@ -193,11 +194,12 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
193194
$index,
194195
$groupBy,
195196
$attr,
196-
$labelTranslationParameters
197+
$labelTranslationParameters,
198+
$duplicatePreferredChoices,
197199
);
198200
}
199201

200-
$hash = self::generateHash([$list, $preferredChoices, $label, $index, $groupBy, $attr, $labelTranslationParameters]);
202+
$hash = self::generateHash([$list, $preferredChoices, $label, $index, $groupBy, $attr, $labelTranslationParameters, $duplicatePreferredChoices]);
201203

202204
if (!isset($this->views[$hash])) {
203205
$this->views[$hash] = $this->decoratedFactory->createView(
@@ -207,7 +209,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
207209
$index,
208210
$groupBy,
209211
$attr,
210-
$labelTranslationParameters
212+
$labelTranslationParameters,
213+
$duplicatePreferredChoices,
211214
);
212215
}
213216

src/Symfony/Component/Form/ChoiceList/Factory/ChoiceListFactoryInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $va
7777
* pass false to discard the label
7878
* @param array|callable|null $attr The callable generating the HTML attributes
7979
* @param array|callable $labelTranslationParameters The parameters used to translate the choice labels
80+
* @param bool $duplicatePreferredChoices Whether to duplicate preferred choices in choices list
8081
*/
81-
public function createView(ChoiceListInterface $list, array|callable $preferredChoices = null, callable|false $label = null, callable $index = null, callable $groupBy = null, array|callable $attr = null, array|callable $labelTranslationParameters = []): ChoiceListView;
82+
public function createView(ChoiceListInterface $list, array|callable $preferredChoices = null, callable|false $label = null, callable $index = null, callable $groupBy = null, array|callable $attr = null, array|callable $labelTranslationParameters = []/*, bool $duplicatePreferredChoices = true*/): ChoiceListView;
8283
}

src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $va
5252
return new LazyChoiceList($loader, $value);
5353
}
5454

55-
public function createView(ChoiceListInterface $list, array|callable $preferredChoices = null, callable|false $label = null, callable $index = null, callable $groupBy = null, array|callable $attr = null, array|callable $labelTranslationParameters = []): ChoiceListView
55+
public function createView(ChoiceListInterface $list, array|callable $preferredChoices = null, callable|false $label = null, callable $index = null, callable $groupBy = null, array|callable $attr = null, array|callable $labelTranslationParameters = []/*, bool $duplicatePreferredChoices = true*/): ChoiceListView
5656
{
57+
$duplicatePreferredChoices = func_num_args() > 7 ? func_get_arg(7) : true;
5758
$preferredViews = [];
5859
$preferredViewsOrder = [];
5960
$otherViews = [];
@@ -92,7 +93,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
9293
$preferredChoices,
9394
$preferredViews,
9495
$preferredViewsOrder,
95-
$otherViews
96+
$otherViews,
97+
$duplicatePreferredChoices,
9698
);
9799
}
98100

@@ -130,7 +132,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
130132
$preferredChoices,
131133
$preferredViews,
132134
$preferredViewsOrder,
133-
$otherViews
135+
$otherViews,
136+
$duplicatePreferredChoices,
134137
);
135138
}
136139

@@ -139,7 +142,7 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
139142
return new ChoiceListView($otherViews, $preferredViews);
140143
}
141144

142-
private static function addChoiceView($choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
145+
private static function addChoiceView($choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews, bool $duplicatePreferredChoices): void
143146
{
144147
// $value may be an integer or a string, since it's stored in 10000 the array
145148
// keys. We want to guarantee it's a string though.
@@ -180,12 +183,16 @@ private static function addChoiceView($choice, string $value, $label, array $key
180183
if (null !== $isPreferred && false !== $preferredKey = $isPreferred($choice, $key, $value)) {
181184
$preferredViews[$nextIndex] = $view;
182185
$preferredViewsOrder[$nextIndex] = $preferredKey;
183-
}
184186

185-
$otherViews[$nextIndex] = $view;
187+
if ($duplicatePreferredChoices) {
188+
$otherViews[$nextIndex] = $view;
189+
}
190+
} else {
191+
$otherViews[$nextIndex] = $view;
192+
}
186193
}
187194

188-
private static function addChoiceViewsFromStructuredValues(array $values, $label, array $choices, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
195+
private static function addChoiceViewsFromStructuredValues(array $values, $label, array $choices, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews, bool $duplicatePreferredChoices): void
189196
{
190197
foreach ($values as $key => $value) {
191198
if (null === $value) {
@@ -208,7 +215,8 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
208215
$isPreferred,
209216
$preferredViewsForGroup,
210217
$preferredViewsOrder,
211-
$otherViewsForGroup
218+
$otherViewsForGroup,
219+
$duplicatePreferredChoices,
212220
);
213221

214222
if (\count($preferredViewsForGroup) > 0) {
@@ -234,12 +242,13 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
234242
$isPreferred,
235243
$preferredViews,
236244
$preferredViewsOrder,
237-
$otherViews
D7AE
245+
$otherViews,
246+
$duplicatePreferredChoices,
238247
);
239248
}
240249
}
241250

242-
private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
251+
private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews, bool $duplicatePreferredChoices): void
243252
{
244253
$groupLabels = $groupBy($choice, $keys[$value], $value);
245254

@@ -256,7 +265,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
256265
$isPreferred,
257266
$preferredViews,
258267
$preferredViewsOrder,
259-
$otherViews
268+
$otherViews,
269+
$duplicatePreferredChoices,
260270
);
261271

262272
return;
@@ -286,7 +296,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
286296
$isPreferred,
287297
$preferredViews[$groupLabel]->choices,
288298
$preferredViewsOrder[$groupLabel],
289-
$otherViews[$groupLabel]->choices
299+
$otherViews[$groupLabel]->choices,
300+
$duplicatePreferredChoices,
290301
);
291302
}
292303
}

src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, mixed $value
109109
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
110110
}
111111

112-
public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []): ChoiceListView
112+
public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []/*, bool $duplicatePreferredChoices = true*/): ChoiceListView
113113
{
114+
$duplicatePreferredChoices = func_num_args() > 7 ? func_get_arg(7) : true;
114115
$accessor = $this->propertyAccessor;
115116

116117
if (\is_string($label)) {
@@ -182,7 +183,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
182183
$index,
183184
$groupBy,
184185
$attr,
185-
$labelTranslationParameters
186+
$labelTranslationParameters,
187+
$duplicatePreferredChoices,
186188
);
187189
}
188190
}

src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ public function configureOptions(OptionsResolver $resolver)
355355
'choice_attr' => null,
356356
'choice_translation_parameters' => [],
357357
'preferred_choices' => [],
358+
'duplicate_preferred_choices' => true,
358359
'group_by' => null,
359360
'empty_data' => $emptyData,
360361
'placeholder' => $placeholderDefault,
@@ -384,6 +385,7 @@ public function configureOptions(OptionsResolver $resolver)
384385
$resolver->setAllowedTypes('choice_translation_parameters', ['null', 'array', 'callable', ChoiceTranslationParameters::class]);
385386
$resolver->setAllowedTypes('placeholder_attr', ['array']);
386387
$resolver->setAllowedTypes('preferred_choices', ['array', \Traversable::class, 'callable', 'string', PropertyPath::class, PreferredChoice::class]);
388+
$resolver->setAllowedTypes('duplicate_preferred_choices', 'bool');
387389
$resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', PropertyPath::class, GroupBy::class]);
388390
}
389391

@@ -466,7 +468,8 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op
466468
$options['choice_name'],
467469
$options['group_by'],
468470
$options['choice_attr'],
469-
$options['choice_translation_parameters']
471+
$options['choice_translation_parameters'],
472+
$options['duplicate_preferred_choices'],
470473
);
471474
}
472475
}

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"choice_translation_parameters",
1313
"choice_value",
1414
"choices",
15+
"duplicate_preferred_choices",
1516
"expanded",
1617
"group_by",
1718
"multiple",

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
1414
choice_translation_parameters invalid_message auto_initialize csrf_token_manager
1515
choice_value trim block_name
1616
choices block_prefix
17-
expanded by_reference
18-
group_by data
19-
multiple disabled
20-
placeholder form_attr
21-
placeholder_attr getter
22-
preferred_choices help
23-
help_attr
17+
duplicate_preferred_choices by_reference
18+
expanded data
19+
group_by disabled
20+
multiple form_attr
21+
placeholder getter
22+
placeholder_attr help
23+
preferred_choices help_attr
2424
help_html
2525
help_translation_parameters
2626
inherit_data

0 commit comments

Comments
 (0)
0