diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index b5204e42c074..5d01cb38931c 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -19,6 +19,7 @@ CHANGELOG * removed passing guesser services ids as the fourth argument of `DependencyInjectionExtension::__construct()` * removed the ability to validate an unsubmitted form. * removed `ChoiceLoaderInterface` implementation in `TimezoneType` + * added the `false_values` option to the `CheckboxType` which allows to configure custom values which will be treated as `false` during submission 3.4.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php index 21435ba5cbaf..f72cace1191c 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Extension\Core\DataTransformer; use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\TransformationFailedException; /** @@ -24,12 +25,19 @@ class BooleanToStringTransformer implements DataTransformerInterface { private $trueValue; + private $falseValues; + /** - * @param string $trueValue The value emitted upon transform if the input is true + * @param string $trueValue The value emitted upon transform if the input is true + * @param array $falseValues */ - public function __construct(string $trueValue) + public function __construct(string $trueValue, array $falseValues = array(null)) { $this->trueValue = $trueValue; + $this->falseValues = $falseValues; + if (in_array($this->trueValue, $this->falseValues, true)) { + throw new InvalidArgumentException('The specified "true" value is contained in the false-values'); + } } /** @@ -65,7 +73,7 @@ public function transform($value) */ public function reverseTransform($value) { - if (null === $value) { + if (in_array($value, $this->falseValues, true)) { return false; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php index 90646e8712a2..9f0491c151e0 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php @@ -32,7 +32,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) // We cannot solve this case via overriding the "data" option, because // doing so also calls setDataLocked(true). $builder->setData(isset($options['data']) ? $options['data'] : false); - $builder->addViewTransformer(new BooleanToStringTransformer($options['value'])); + $builder->addViewTransformer(new BooleanToStringTransformer($options['value'], $options['false_values'])); } /** @@ -59,7 +59,10 @@ public function configureOptions(OptionsResolver $resolver) 'value' => '1', 'empty_data' => $emptyData, 'compound' => false, + 'false_values' => array(null), )); + + $resolver->setAllowedTypes('false_values', 'array'); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php index 5195092e18b8..bbb9d0792abe 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -68,4 +68,26 @@ public function testReverseTransform() $this->assertTrue($this->transformer->reverseTransform('')); $this->assertFalse($this->transformer->reverseTransform(null)); } + + public function testCustomFalseValues() + { + $customFalseTransformer = new BooleanToStringTransformer(self::TRUE_VALUE, array('0', 'myFalse', true)); + $this->assertFalse($customFalseTransformer->reverseTransform('myFalse')); + $this->assertFalse($customFalseTransformer->reverseTransform('0')); + $this->assertFalse($customFalseTransformer->reverseTransform(true)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException + */ + public function testTrueValueContainedInFalseValues() + { + new BooleanToStringTransformer('0', array(null, '0')); + } + + public function testBeStrictOnTrueInFalseValueCheck() + { + $transformer = new BooleanToStringTransformer('0', array(null, false)); + $this->assertInstanceOf(BooleanToStringTransformer::class, $transformer); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php index a9aeb9c270ec..5ef2f66ca289 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -173,6 +173,38 @@ public function provideCustomModelTransformerData() ); } + /** + * @dataProvider provideCustomFalseValues + */ + public function testCustomFalseValues($falseValue) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'false_values' => array($falseValue), + )); + $form->submit($falseValue); + $this->assertFalse($form->getData()); + } + + public function provideCustomFalseValues() + { + return array( + array(''), + array('false'), + array('0'), + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testDontAllowNonArrayFalseValues() + { + $this->expectExceptionMessageRegExp('/"false_values" with value "invalid" is expected to be of type "array"/'); + $this->factory->create(static::TESTED_TYPE, null, array( + 'false_values' => 'invalid', + )); + } + public function testSubmitNull($expected = null, $norm = null, $view = null) { parent::testSubmitNull(false, false, null);