bug #17798 [Form] allow choice_label
option to be false
When `ChoiceType`is expanded, `choice_label` option can be `false` or return `false` for one or more choices by a callable.
This commit is contained in:
parent
22383987e0
commit
017e1d9e6e
@ -155,11 +155,21 @@ class DefaultChoiceListFactory implements ChoiceListFactoryInterface
|
||||
$key = $keys[$value];
|
||||
$nextIndex = is_int($index) ? $index++ : call_user_func($index, $choice, $key, $value);
|
||||
|
||||
// BC normalize label to accept a false value
|
||||
if (null === $label) {
|
||||
// If the labels are null, use the original choice key by default
|
||||
$label = (string) $key;
|
||||
} elseif (false !== $label) {
|
||||
// If "choice_label" is set to false and "expanded" is true, the value false
|
||||
// should be passed on to the "label" option of the checkboxes/radio buttons
|
||||
$dynamicLabel = call_user_func($label, $choice, $key, $value);
|
||||
$label = false === $dynamicLabel ? false : (string) $dynamicLabel;
|
||||
}
|
||||
|
||||
$view = new ChoiceView(
|
||||
$choice,
|
||||
$value,
|
||||
// If the labels are null, use the original choice key by default
|
||||
null === $label ? (string) $key : (string) call_user_func($label, $choice, $key, $value),
|
||||
$label,
|
||||
// The attributes may be a callable or a mapping from choice indices
|
||||
// to nested arrays
|
||||
is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array())
|
||||
|
@ -413,7 +413,7 @@ class ChoiceType extends AbstractType
|
||||
$resolver->setAllowedTypes('choice_translation_domain', array('null', 'bool', 'string'));
|
||||
$resolver->setAllowedTypes('choices_as_values', 'bool');
|
||||
$resolver->setAllowedTypes('choice_loader', array('null', 'Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface'));
|
||||
$resolver->setAllowedTypes('choice_label', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_label', array('null', 'bool', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_name', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_value', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
$resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath'));
|
||||
|
@ -690,6 +690,129 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => false,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&a[/trans]"]
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&c[/trans]"]
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="radio"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithoutTranslation()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
@ -895,6 +1018,129 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&a[/trans]"]
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[.=" [trans]label.&c[/trans]"]
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
]
|
||||
]
|
||||
/following-sibling::div
|
||||
[@class="checkbox"]
|
||||
[
|
||||
./label
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
]
|
||||
]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"][@class="form-control"]
|
||||
]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithoutTranslation()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a', '&c'), array(
|
||||
|
@ -708,6 +708,166 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => false,
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
/following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=4]
|
||||
[count(./label)=3]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSingleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', '&a', array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsAsFalse()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => false,
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function ($choice, $label, $value) {
|
||||
if ('&b' === $choice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return 'label.'.$value;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::label[@for="name_0"][.="[trans]label.&a[/trans]"]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@value="&c"][not(@checked)]
|
||||
/following-sibling::label[@for="name_2"][.="[trans]label.&c[/trans]"]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=4]
|
||||
[count(./label)=3]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipleChoiceExpandedWithLabelsSetFalseByCallable()
|
||||
{
|
||||
$form = $this->factory->createNamed('name', 'choice', array('&a'), array(
|
||||
'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'),
|
||||
'choices_as_values' => true,
|
||||
'choice_label' => function () {
|
||||
return false;
|
||||
},
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
));
|
||||
|
||||
$this->assertWidgetMatchesXpath($form->createView(), array(),
|
||||
'/div
|
||||
[
|
||||
./input[@type="checkbox"][@name="name[]"][@id="name_0"][@value="&a"][@checked]
|
||||
/following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@value="&b"][not(@checked)]
|
||||
/following-sibling::input[@type="hidden"][@id="name__token"]
|
||||
]
|
||||
[count(./input)=3]
|
||||
[count(./label)=1]
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
public function testFormEndWithRest()
|
||||
{
|
||||
$view = $this->factory->createNamedBuilder('name', 'form')
|
||||
|
Reference in New Issue
Block a user