diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index 8cd8e29259aa2..e08c9e51276be 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -155,11 +155,21 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ $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()) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 573c768cd6519..8f8fc69cfb07b 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -413,7 +413,7 @@ public function configureOptions(OptionsResolver $resolver) $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')); diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 8c846c643ed57..35d0b943234fa 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -690,6 +690,129 @@ public function testSingleChoiceExpanded() ); } + 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 @@ public function testMultipleChoiceExpanded() ); } + 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( diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index 7eeeac0a029cc..511c7f138cc7f 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -708,6 +708,166 @@ public function testChoiceRowWithCustomBlock() ); } + 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')
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: