-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Add option widget to ChoiceType #17646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Form\Extension\Core\DataTransformer; | ||
|
||
use Symfony\Component\Form\DataTransformerInterface; | ||
use Symfony\Component\Form\Exception\TransformationFailedException; | ||
|
||
/** | ||
* Converts an array of values to a string with multiple values separated by a delimiter. | ||
* | ||
* @author Bilal Amarni <[email protected]> | ||
*/ | ||
class ValuesToStringTransformer implements DataTransformerInterface | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private $delimiter; | ||
|
||
/** | ||
* @var bool | ||
*/ | ||
private $trim; | ||
|
||
/** | ||
* @param string $delimiter | ||
* @param bool $trim | ||
*/ | ||
public function __construct($delimiter, $trim) | ||
{ | ||
$this->delimiter = $delimiter; | ||
$this->trim = $trim; | ||
} | ||
|
||
/** | ||
* @param array $array | ||
* | ||
* @return string | ||
* | ||
* @throws UnexpectedTypeException if the given value is not an array | ||
*/ | ||
public function transform($array) | ||
{ | ||
if (null === $array) { | ||
return ''; | ||
} | ||
|
||
if (!is_array($array)) { | ||
throw new TransformationFailedException('Expected an array'); | ||
} | ||
|
||
return implode($this->delimiter, $array); | ||
} | ||
|
||
/** | ||
* @param string $string | ||
* | ||
* @return array | ||
* | ||
* @throws UnexpectedTypeException if the given value is not a string | ||
*/ | ||
public function reverseTransform($string) | ||
{ | ||
if (empty($string)) { | ||
return array(); | ||
} | ||
|
||
if (!is_string($string)) { | ||
throw new TransformationFailedException('Expected a string'); | ||
} | ||
|
||
$values = explode($this->delimiter, $string); | ||
|
||
if ($this->trim) { | ||
$values = array_map('trim', $values); | ||
} | ||
|
||
return $values; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ | |
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; | ||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; | ||
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; | ||
use Symfony\Component\Form\Extension\Core\DataTransformer\ValuesToStringTransformer; | ||
use Symfony\Component\OptionsResolver\Options; | ||
use Symfony\Component\OptionsResolver\OptionsResolver; | ||
|
||
|
@@ -60,7 +61,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) | |
$choiceList = $this->createChoiceList($options); | ||
$builder->setAttribute('choice_list', $choiceList); | ||
|
||
if ($options['expanded']) { | ||
if ($options['expanded'] && !in_array($options['widget'], array('text', 'hidden'), true)) { | ||
$builder->setDataMapper($options['multiple'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be What is the point of having
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For |
||
? new CheckboxListMapper($choiceList) | ||
: new RadioListMapper($choiceList)); | ||
|
@@ -146,10 +147,15 @@ public function buildForm(FormBuilderInterface $builder, array $options) | |
}); | ||
} | ||
} elseif ($options['multiple']) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry I don't get your comment? it's handled in the next else condition There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you expect the html output to be when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As |
||
// <select> tag with "multiple" option | ||
// "select", "text" or "hidden" widget with "multiple" option | ||
$builder->addViewTransformer(new ChoicesToValuesTransformer($choiceList)); | ||
|
||
// for "text" / "hidden" widget, view data uses a delimiter | ||
if (in_array($options['widget'], array('text', 'hidden'), true)) { | ||
$builder->addViewTransformer(new ValuesToStringTransformer($options['delimiter'], $options['trim'])); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implementation is wrong IMO since transformers may be executed in the right order for transformation but not for reversing transformation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure I get what you mean, Symfony will chain them in the correct order automatically depending on the direction (view to norm or norm to view). |
||
} else { | ||
// <select> tag without "multiple" option | ||
// "select", "text" or "hidden" tag without "multiple" option | ||
$builder->addViewTransformer(new ChoiceToValueTransformer($choiceList)); | ||
} | ||
|
||
|
@@ -179,15 +185,20 @@ public function buildView(FormView $view, FormInterface $form, array $options) | |
: $this->createChoiceListView($choiceList, $options); | ||
|
||
$view->vars = array_replace($view->vars, array( | ||
'widget' => $options['widget'], | ||
'multiple' => $options['multiple'], | ||
'expanded' => $options['expanded'], | ||
'expanded' => $options['expanded'], // BC | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like actual compatibility |
||
'preferred_choices' => $choiceListView->preferredChoices, | ||
'choices' => $choiceListView->choices, | ||
'separator' => '-------------------', | ||
'placeholder' => null, | ||
'choice_translation_domain' => $choiceTranslationDomain, | ||
)); | ||
|
||
if (in_array($options['widget'], array('text', 'hidden'), true)) { | ||
return; | ||
} | ||
|
||
// The decision, whether a choice is selected, is potentially done | ||
// thousand of times during the rendering of a template. Provide a | ||
// closure here that is optimized for the value of the form, to | ||
|
@@ -226,6 +237,10 @@ public function buildView(FormView $view, FormInterface $form, array $options) | |
*/ | ||
public function finishView(FormView $view, FormInterface $form, array $options) | ||
{ | ||
if (in_array($options['widget'], array('text', 'hidden'), true)) { | ||
return; | ||
} | ||
|
||
if ($options['expanded']) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One last comment "for the road" if I may : You did the change in https://github.com/bamarni/symfony/blob/9ba6a34f64fc063b3bfc8874d6f9300ba8f4c0be/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php#L145 Why not this PR ? Handling this everywhere in the class (except for choice view vars) is how you can make |
||
// Radio buttons should have the same name as the parent | ||
$childName = $view->vars['full_name']; | ||
|
@@ -302,9 +317,30 @@ public function configureOptions(OptionsResolver $resolver) | |
return $choiceTranslationDomain; | ||
}; | ||
|
||
$multipleNormalizer = function (Options $options, $multiple) { | ||
if (in_array($options['widget'], array('radio', 'checkbox'), true)) { | ||
return 'checkbox' === $options['widget']; | ||
} | ||
|
||
return $multiple; | ||
}; | ||
|
||
$expandedNomalizer = function (Options $options, $expanded) { | ||
if (null !== $expanded) { | ||
@trigger_error('The "expanded" option is deprecated since version 3.1 and will be removed in 4.0. You should use "widget" instead.', E_USER_DEPRECATED); | ||
} else { | ||
$expanded = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is not need if you cast return to bool :
anyway the cast would be done anywhere in this class as it's only a test variable. Then it has to remain internal and not to be removed in 4.0 with the actual implementation IMO. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is so wrong about this normalizer for you to comment on it since days? :D |
||
} | ||
|
||
return in_array($options['widget'], array('radio', 'checkbox'), true) ?: $expanded; | ||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could have only one normalizer : $expandedNormalizer = function (Options $options, $expanded) {
// trigger error
return 'expanded' === $options['widget'];
} Anyway it should be : $expandedNomalizer = function (Options $options, $expanded) {
if (null !== $expanded) {
@trigger_error('The "expanded" option is deprecated since version 3.1 for internal use only. You should use "widget" option with "radio" or "checkbox" instead.', E_USER_DEPRECATED);
}
return in_array($options['widget'], array('radio', 'checkbox'));
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as discussed previously, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no need to handle you could write : $expandedNomalizer = function (Options $options, $expanded) {
if (null !== $expanded) {
@trigger_error('The "expanded" option is deprecated since version 3.1 for internal use only. You should use "widget" option with "radio" or "checkbox" instead.', E_USER_DEPRECATED);
}
return 'select' === $options['widget'] ? false : (in_array($options['widget'], array('radio', 'checkbox')) ?: $expanded);
}; only if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The goal of this normalizer is to handle |
||
$resolver->setDefaults(array( | ||
'widget' => null, | ||
'multiple' => false, | ||
'expanded' => false, | ||
'delimiter' => ',', | ||
'expanded' => null, // deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO should be marked as internal since it's still used in the implementation of this class |
||
'choice_list' => null, // deprecated | ||
'choices' => array(), | ||
'choices_as_values' => null, // deprecated since 3.1 | ||
'choice_loader' => null, | ||
|
@@ -325,6 +361,8 @@ public function configureOptions(OptionsResolver $resolver) | |
'choice_translation_domain' => true, | ||
)); | ||
|
||
$resolver->setNormalizer('expanded', $expandedNomalizer); | ||
$resolver->setNormalizer('multiple', $multipleNormalizer); | ||
$resolver->setNormalizer('placeholder', $placeholderNormalizer); | ||
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); | ||
$resolver->setNormalizer('choices_as_values', $choicesAsValuesNormalizer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you should add : $resolver->setAllowedTypes('widget', array('null', 'string'));
$resolver->setAllowedValues('widget', array('hidden', 'text', 'select', 'checkbox', 'radio')); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
@@ -338,6 +376,7 @@ public function configureOptions(OptionsResolver $resolver) | |
$resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); | ||
$resolver->setAllowedTypes('preferred_choices', array('array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); | ||
$resolver->setAllowedTypes('group_by', array('null', 'array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); | ||
$resolver->setAllowedValues('widget', array(null, 'hidden', 'text', 'select', 'checkbox', 'radio')); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hidden_widget
andform_widget_simple
never get avalue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see https://github.com/symfony/form/blob/master/Extension/Core/Type/FormType.php#L89