Skip to content

Commit 8b420d4

Browse files
committed
feature #50934 [Form] Add duplicate_preferred_choices option to ChoiceType (arnaud-deabreu)
This PR was merged into the 6.4 branch. Discussion ---------- [Form] Add `duplicate_preferred_choices` option to `ChoiceType` | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #35135 | License | MIT | Doc PR | TODO I've reintroduced layout tests as they were before #32658, is that enough? /cc `@xabbuh` :) Thanks `@HeahDude` for your mentoring! Commits ------- df00a5f [Form] Add `duplicate_preferred_choices` option to `ChoiceType`
2 parents 2323f30 + df00a5f commit 8b420d4

File tree

11 files changed

+112
-28
lines changed

11 files changed

+112
-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
@@ -28,7 +28,7 @@
2828
"symfony/asset-mapper": "^6.3|^7.0",
2929
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
3030
"symfony/finder": "^5.4|^6.0|^7.0",
31-
"symfony/form": "^6.3|^7.0",
31+
"symfony/form": "^6.4|^7.0",
3232
"symfony/html-sanitizer": "^6.1|^7.0",
3333
"symfony/http-foundation": "^5.4|^6.0|^7.0",
3434
"symfony/http-kernel": "^6.4|^7.0",

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ CHANGELOG
88
`model_timezone` option in `DateType`, `DateTimeType`, and `TimeType`
99
* Deprecate `PostSetDataEvent::setData()`, use `PreSetDataEvent::setData()` instead
1010
* Deprecate `PostSubmitEvent::setData()`, use `PreSubmitDataEvent::setData()` or `SubmitDataEvent::setData()` instead
11+
* Add `duplicate_preferred_choices` option in `ChoiceType`
12+
* Add `$duplicatePreferredChoices` parameter to `ChoiceListFactoryInterface::createView()`
1113

1214
6.3
1315
---

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,12 @@ 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+
/**
149+
* @param bool $duplicatePreferredChoices
150+
*/
151+
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
149152
{
153+
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
150154
$cache = true;
151155

152156
if ($preferredChoices instanceof Cache\PreferredChoice) {
@@ -193,11 +197,12 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
193197
$index,
194198
$groupBy,
195199
$attr,
196-
$labelTranslationParameters
200+
$labelTranslationParameters,
201+
$duplicatePreferredChoices,
197202
);
198203
}
199204

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

202207
if (!isset($this->views[$hash])) {
203208
$this->views[$hash] = $this->decoratedFactory->createView(
@@ -207,7 +212,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
207212
$index,
208213
$groupBy,
209214
$attr,
210-
$labelTranslationParameters
215+
$labelTranslationParameters,
216+
$duplicatePreferredChoices,
211217
);
212218
}
213219

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ 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 the preferred choices should be duplicated
81+
* on top of the list and in their original position
82+
* or only in the top of the list
8083
*/
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;
84+
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;
8285
}

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

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,12 @@ 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+
/**
56+
* @param bool $duplicatePreferredChoices
57+
*/
58+
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
5659
{
60+
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
5761
$preferredViews = [];
5862
$preferredViewsOrder = [];
5963
$otherViews = [];
@@ -92,7 +96,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
9296
$preferredChoices,
9397
$preferredViews,
9498
$preferredViewsOrder,
95-
$otherViews
99+
$otherViews,
100+
$duplicatePreferredChoices,
96101
);
97102
}
98103

@@ -130,7 +135,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
130135
$preferredChoices,
131136
$preferredViews,
132137
$preferredViewsOrder,
133-
$otherViews
138+
$otherViews,
139+
$duplicatePreferredChoices,
134140
);
135141
}
136142

@@ -139,7 +145,7 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
139145
return new ChoiceListView($otherViews, $preferredViews);
140146
}
141147

142-
private static function addChoiceView($choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
148+
private static function addChoiceView($choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews, bool $duplicatePreferredChoices): void
143149
{
144150
// $value may be an integer or a string, since it's stored in the array
145151
// keys. We want to guarantee it's a string though.
@@ -180,12 +186,16 @@ private static function addChoiceView($choice, string $value, $label, array $key
180186
if (null !== $isPreferred && false !== $preferredKey = $isPreferred($choice, $key, $value)) {
181187
$preferredViews[$nextIndex] = $view;
182188
$preferredViewsOrder[$nextIndex] = $preferredKey;
183-
}
184189

185-
$otherViews[$nextIndex] = $view;
190+
if ($duplicatePreferredChoices) {
191+
$otherViews[$nextIndex] = $view;
192+
}
193+
} else {
194+
$otherViews[$nextIndex] = $view;
195+
}
186196
}
187197

188-
private static function addChoiceViewsFromStructuredValues(array $values, $label, array $choices, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
198+
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
189199
{
190200
foreach ($values as $key => $value) {
191201
if (null === $value) {
@@ -208,7 +218,8 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
208218
$isPreferred,
209219
$preferredViewsForGroup,
210220
$preferredViewsOrder,
211-
$otherViewsForGroup
221+
$otherViewsForGroup,
222+
$duplicatePreferredChoices,
212223
);
213224

214225
if (\count($preferredViewsForGroup) > 0) {
@@ -234,12 +245,13 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
234245
$isPreferred,
235246
$preferredViews,
236247
$preferredViewsOrder,
237-
$otherViews
248+
$otherViews,
249+
$duplicatePreferredChoices,
238250
);
239251
}
240252
}
241253

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
254+
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
243255
{
244256
$groupLabels = $groupBy($choice, $keys[$value], $value);
245257

@@ -256,7 +268,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
256268
$isPreferred,
257269
$preferredViews,
258270
$preferredViewsOrder,
259-
$otherViews
271+
$otherViews,
272+
$duplicatePreferredChoices,
260273
);
261274

262275
return;
@@ -286,7 +299,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
286299
$isPreferred,
287300
$preferredViews[$groupLabel]->choices,
288301
$preferredViewsOrder[$groupLabel],
289-
$otherViews[$groupLabel]->choices
302+
$otherViews[$groupLabel]->choices,
303+
$duplicatePreferredChoices,
290304
);
291305
}
292306
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,12 @@ 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+
/**
113+
* @param bool $duplicatePreferredChoices
114+
*/
115+
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
113116
{
117+
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
114118
$accessor = $this->propertyAccessor;
115119

116120
if (\is_string($label)) {
@@ -182,7 +186,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
182186
$index,
183187
$groupBy,
184188
$attr,
185-
$labelTranslationParameters
189+
$labelTranslationParameters,
190+
$duplicatePreferredChoices,
186191
);
187192
}
188193
}

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

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

@@ -465,7 +467,8 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op
465467
$options['choice_name'],
466468
$options['group_by'],
467469
$options['choice_attr'],
468-
$options['choice_translation_parameters']
470+
$options['choice_translation_parameters'],
471+
$options['duplicate_preferred_choices'],
469472
);
470473
}
471474
}

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