Skip to content

Commit

Permalink
[Form] Add duplicate_preferred_choices option to ChoiceType
Browse files Browse the repository at this point in the history
  • Loading branch information
arnaud-deabreu authored and nicolas-grekas committed Sep 29, 2023
1 parent 3850658 commit df00a5f
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,31 @@ public function testSingleChoiceWithPreferred()
);
}

public function testSingleChoiceWithPreferredIsNotDuplicated()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [
'choices' => ['Choice&A' => '&a', 'Choice&B' => '&b'],
'preferred_choices' => ['&b'],
'duplicate_preferred_choices' => false,
'multiple' => false,
'expanded' => false,
]);

$this->assertWidgetMatchesXpath($form->createView(), ['separator' => '-- sep --', 'attr' => ['class' => 'my&class']],
'/select
[@name="name"]
[@class="my&class form-control"]
[not(@required)]
[
./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
/following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
/following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
]
[count(./option)=3]
'
);
}

public function testSingleChoiceWithSelectedPreferred()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,31 @@ public function testSingleChoiceWithPreferred()
);
}

public function testSingleChoiceWithPreferredIsNotDuplicated()
{
$form = $this->factory->createNamed('name', ChoiceType::class, '&a', [
'choices' => ['Choice&A' => '&a', 'Choice&B' => '&b'],
'preferred_choices' => ['&b'],
'duplicate_preferred_choices' => false,
'multiple' => false,
'expanded' => false,
]);

$this->assertWidgetMatchesXpath($form->createView(), ['separator' => '-- sep --', 'attr' => ['class' => 'my&class']],
'/select
[@name="name"]
[@class="my&class form-select"]
[not(@required)]
[
./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
/following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"]
/following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
]
[count(./option)=3]
'
);
}

public function testSingleChoiceWithSelectedPreferred()
{
$form = $this->factory->createNamed('name', ChoiceType::class, '&a', [
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/Twig/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"symfony/asset-mapper": "^6.3|^7.0",
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
"symfony/finder": "^5.4|^6.0|^7.0",
"symfony/form": "^6.3|^7.0",
"symfony/form": "^6.4|^7.0",
"symfony/html-sanitizer": "^6.1|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^6.2|^7.0",
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Form/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ CHANGELOG
`model_timezone` option in `DateType`, `DateTimeType`, and `TimeType`
* Deprecate `PostSetDataEvent::setData()`, use `PreSetDataEvent::setData()` instead
* Deprecate `PostSubmitEvent::setData()`, use `PreSubmitDataEvent::setData()` or `SubmitDataEvent::setData()` instead
* Add `duplicate_preferred_choices` option in `ChoiceType`
* Add `$duplicatePreferredChoices` parameter to `ChoiceListFactoryInterface::createView()`

6.3
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,12 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, mixed $value
return $this->lists[$hash];
}

public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []): ChoiceListView
/**
* @param bool $duplicatePreferredChoices
*/
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
{
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
$cache = true;

if ($preferredChoices instanceof Cache\PreferredChoice) {
Expand Down Expand Up @@ -193,11 +197,12 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
$index,
$groupBy,
$attr,
$labelTranslationParameters
$labelTranslationParameters,
$duplicatePreferredChoices,
);
}

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

if (!isset($this->views[$hash])) {
$this->views[$hash] = $this->decoratedFactory->createView(
Expand All @@ -207,7 +212,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
$index,
$groupBy,
$attr,
$labelTranslationParameters
$labelTranslationParameters,
$duplicatePreferredChoices,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $va
* pass false to discard the label
* @param array|callable|null $attr The callable generating the HTML attributes
* @param array|callable $labelTranslationParameters The parameters used to translate the choice labels
* @param bool $duplicatePreferredChoices Whether the preferred choices should be duplicated
* on top of the list and in their original position
* or only in the top of the list
*/
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;
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, callable $va
return new LazyChoiceList($loader, $value);
}

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
/**
* @param bool $duplicatePreferredChoices
*/
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
{
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
$preferredViews = [];
$preferredViewsOrder = [];
$otherViews = [];
Expand Down Expand Up @@ -92,7 +96,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
$preferredChoices,
$preferredViews,
$preferredViewsOrder,
$otherViews
$otherViews,
$duplicatePreferredChoices,
);
}

Expand Down Expand Up @@ -130,7 +135,8 @@ public function createView(ChoiceListInterface $list, array|callable $preferredC
$preferredChoices,
$preferredViews,
$preferredViewsOrder,
$otherViews
$otherViews,
$duplicatePreferredChoices,
);
}

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

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

$otherViews[$nextIndex] = $view;
if ($duplicatePreferredChoices) {
$otherViews[$nextIndex] = $view;
}
} else {
$otherViews[$nextIndex] = $view;
}
}

private static function addChoiceViewsFromStructuredValues(array $values, $label, array $choices, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
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
{
foreach ($values as $key => $value) {
if (null === $value) {
Expand All @@ -208,7 +218,8 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
$isPreferred,
$preferredViewsForGroup,
$preferredViewsOrder,
$otherViewsForGroup
$otherViewsForGroup,
$duplicatePreferredChoices,
);

if (\count($preferredViewsForGroup) > 0) {
Expand All @@ -234,12 +245,13 @@ private static function addChoiceViewsFromStructuredValues(array $values, $label
$isPreferred,
$preferredViews,
$preferredViewsOrder,
$otherViews
$otherViews,
$duplicatePreferredChoices,
);
}
}

private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews): void
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
{
$groupLabels = $groupBy($choice, $keys[$value], $value);

Expand All @@ -256,7 +268,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
$isPreferred,
$preferredViews,
$preferredViewsOrder,
$otherViews
$otherViews,
$duplicatePreferredChoices,
);

return;
Expand Down Expand Up @@ -286,7 +299,8 @@ private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choi
$isPreferred,
$preferredViews[$groupLabel]->choices,
$preferredViewsOrder[$groupLabel],
$otherViews[$groupLabel]->choices
$otherViews[$groupLabel]->choices,
$duplicatePreferredChoices,
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,12 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, mixed $value
return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
}

public function createView(ChoiceListInterface $list, mixed $preferredChoices = null, mixed $label = null, mixed $index = null, mixed $groupBy = null, mixed $attr = null, mixed $labelTranslationParameters = []): ChoiceListView
/**
* @param bool $duplicatePreferredChoices
*/
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
{
$duplicatePreferredChoices = \func_num_args() > 7 ? func_get_arg(7) : true;
$accessor = $this->propertyAccessor;

if (\is_string($label)) {
Expand Down Expand Up @@ -182,7 +186,8 @@ public function createView(ChoiceListInterface $list, mixed $preferredChoices =
$index,
$groupBy,
$attr,
$labelTranslationParameters
$labelTranslationParameters,
$duplicatePreferredChoices,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ public function configureOptions(OptionsResolver $resolver)
'choice_attr' => null,
'choice_translation_parameters' => [],
'preferred_choices' => [],
'duplicate_preferred_choices' => true,
'group_by' => null,
'empty_data' => $emptyData,
'placeholder' => $placeholderDefault,
Expand Down Expand Up @@ -383,6 +384,7 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setAllowedTypes('choice_translation_parameters', ['null', 'array', 'callable', ChoiceTranslationParameters::class]);
$resolver->setAllowedTypes('placeholder_attr', ['array']);
$resolver->setAllowedTypes('preferred_choices', ['array', \Traversable::class, 'callable', 'string', PropertyPath::class, PreferredChoice::class]);
$resolver->setAllowedTypes('duplicate_preferred_choices', 'bool');
$resolver->setAllowedTypes('group_by', ['null', 'callable', 'string', PropertyPath::class, GroupBy::class]);
}

Expand Down Expand Up @@ -465,7 +467,8 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op
$options['choice_name'],
$options['group_by'],
$options['choice_attr'],
$options['choice_translation_parameters']
$options['choice_translation_parameters'],
$options['duplicate_preferred_choices'],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"choice_translation_parameters",
"choice_value",
"choices",
"duplicate_preferred_choices",
"expanded",
"group_by",
"multiple",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice")
choice_translation_parameters invalid_message auto_initialize csrf_token_manager
choice_value trim block_name
choices block_prefix
expanded by_reference
group_by data
multiple disabled
placeholder form_attr
placeholder_attr getter
preferred_choices help
help_attr
duplicate_preferred_choices by_reference
expanded data
group_by disabled
multiple form_attr
placeholder getter
placeholder_attr help
preferred_choices help_attr
help_html
help_translation_parameters
inherit_data
Expand Down

0 comments on commit df00a5f

Please sign in to comment.