Skip to content

Commit 927aed0

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

File tree

11 files changed

+101
-28
lines changed

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 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
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",

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy