diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md index 996f338f00..e4e8ce6886 100644 --- a/UPGRADE-2.1.md +++ b/UPGRADE-2.1.md @@ -167,17 +167,6 @@ UPGRADE FROM 2.0 to 2.1 colons and underscores, you can restore the old behavior by setting the `index_strategy` choice field option to `ChoiceList::COPY_CHOICE`. - * The strategy for generating the `value` HTML attribute for choices in a - choice field has changed. - - Instead of using the choice value, a generated integer is now stored. Again, - take care if your JavaScript reads this value. If your choice field is a - non-expanded single-choice field, or if the choices are guaranteed not to - contain the empty string '' (which is the case when you added it manually - or when the field is a single-choice field and is not required), you can - restore the old behavior by setting the `value_strategy` choice field option - to `ChoiceList::COPY_CHOICE`. - * In the choice field type's template, the structure of the `choices` variable has changed. diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 34d1055f34..1300d3e61d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -442,8 +442,8 @@ class EntityTypeTest extends TypeTestCase $this->assertSame($entity2, $field->getData()); $this->assertFalse($field['1']->getData()); $this->assertTrue($field['2']->getData()); - $this->assertSame('', $field['1']->getClientData()); - $this->assertSame('1', $field['2']->getClientData()); + $this->assertNull($field['1']->getClientData()); + $this->assertSame('2', $field['2']->getClientData()); } public function testSubmitMultipleExpanded() @@ -462,7 +462,7 @@ class EntityTypeTest extends TypeTestCase 'property' => 'name', )); - $field->bind(array('1' => '1', '3' => '3')); + $field->bind(array('1', '3')); $expected = new ArrayCollection(array($entity1, $entity3)); @@ -472,8 +472,8 @@ class EntityTypeTest extends TypeTestCase $this->assertFalse($field['2']->getData()); $this->assertTrue($field['3']->getData()); $this->assertSame('1', $field['1']->getClientData()); - $this->assertSame('', $field['2']->getClientData()); - $this->assertSame('1', $field['3']->getClientData()); + $this->assertNull($field['2']->getClientData()); + $this->assertSame('3', $field['3']->getClientData()); } public function testOverrideChoices() diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php index 4211cd6eed..3f15fae91d 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BooleanToStringTransformer.php @@ -22,6 +22,22 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException; */ class BooleanToStringTransformer implements DataTransformerInterface { + /** + * The value emitted upon transform if the input is true + * @var string + */ + private $trueValue; + + /** + * Sets the value emitted upon transform if the input is true. + * + * @param string $trueValue + */ + public function __construct($trueValue) + { + $this->trueValue = $trueValue; + } + /** * Transforms a Boolean into a string. * @@ -34,14 +50,14 @@ class BooleanToStringTransformer implements DataTransformerInterface public function transform($value) { if (null === $value) { - return ''; + return null; } if (!is_bool($value)) { throw new UnexpectedTypeException($value, 'Boolean'); } - return true === $value ? '1' : ''; + return true === $value ? $this->trueValue : null; } /** @@ -63,7 +79,7 @@ class BooleanToStringTransformer implements DataTransformerInterface throw new UnexpectedTypeException($value, 'string'); } - return '' !== $value; + return true; } } diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php new file mode 100644 index 0000000000..6a16fc106f --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/FixCheckboxInputListener.php @@ -0,0 +1,51 @@ + + * + * 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\EventListener; + +use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\Event\FilterDataEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; + +/** + * Takes care of converting the input from a list of checkboxes to a correctly + * indexed array. + * + * @author Bernhard Schussek + */ +class FixCheckboxInputListener implements EventSubscriberInterface +{ + private $choiceList; + + /** + * Constructor. + * + * @param ChoiceListInterface $choiceList + */ + public function __construct(ChoiceListInterface $choiceList) + { + $this->choiceList = $choiceList; + } + + public function onBindClientData(FilterDataEvent $event) + { + $values = (array) $event->getData(); + $indices = $this->choiceList->getIndicesForValues($values); + + $event->setData(array_combine($indices, $values)); + } + + static public function getSubscribedEvents() + { + return array(FormEvents::BIND_CLIENT_DATA => 'onBindClientData'); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php index 2ac3a0ffad..ddbf8ae897 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CheckboxType.php @@ -25,7 +25,7 @@ class CheckboxType extends AbstractType public function buildForm(FormBuilder $builder, array $options) { $builder - ->appendClientTransformer(new BooleanToStringTransformer()) + ->appendClientTransformer(new BooleanToStringTransformer($options['value'])) ->setAttribute('value', $options['value']) ; } @@ -37,7 +37,7 @@ class CheckboxType extends AbstractType { $view ->set('value', $form->getAttribute('value')) - ->set('checked', (Boolean) $form->getClientData()) + ->set('checked', null !== $form->getClientData()) ; } @@ -48,6 +48,9 @@ class CheckboxType extends AbstractType { return array( 'value' => '1', + 'empty_data' => function (FormInterface $form, $clientData) { + return $clientData; + }, ); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 03beff4d31..74372f704c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -14,13 +14,14 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\Form\Exception\FormException; use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; +use Symfony\Component\Form\Extension\Core\EventListener\FixCheckboxInputListener; use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener; -use Symfony\Component\Form\FormView; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; @@ -81,7 +82,10 @@ class ChoiceType extends AbstractType if ($options['expanded']) { if ($options['multiple']) { - $builder->appendClientTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list'])); + $builder + ->appendClientTransformer(new ChoicesToBooleanArrayTransformer($options['choice_list'])) + ->addEventSubscriber(new FixCheckboxInputListener($options['choice_list']), 10) + ; } else { $builder ->appendClientTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list'])) @@ -155,7 +159,7 @@ class ChoiceType extends AbstractType 'choice_list' => null, 'choices' => null, 'preferred_choices' => array(), - 'value_strategy' => ChoiceList::GENERATE, + 'value_strategy' => ChoiceList::COPY_CHOICE, 'index_strategy' => ChoiceList::GENERATE, 'empty_data' => $multiple || $expanded ? array() : '', 'empty_value' => $multiple || $expanded || !isset($options['empty_value']) ? null : '', diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 0e68b88c01..b222c5ef3a 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -470,7 +470,11 @@ class Form implements \IteratorAggregate, FormInterface return $this; } - if (is_scalar($clientData) || null === $clientData) { + // Don't convert NULL to a string here in order to determine later + // whether an empty value has been submitted or whether no value has + // been submitted at all. This is important for processing checkboxes + // and radio buttons with empty values. + if (is_scalar($clientData)) { $clientData = (string) $clientData; } @@ -522,11 +526,13 @@ class Form implements \IteratorAggregate, FormInterface } if (null === $clientData || '' === $clientData) { - $clientData = $this->emptyData; + $emptyData = $this->emptyData; - if ($clientData instanceof \Closure) { - $clientData = $clientData($this); + if ($emptyData instanceof \Closure) { + $emptyData = $emptyData($this, $clientData); } + + $clientData = $emptyData; } // Merge form data from children into existing client data diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 5af0500a5d..f380a8372b 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -330,8 +330,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name"] [@required="required"] [ - ./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=2] ' @@ -352,9 +352,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name"] [@required="required"] [ - ./option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.="-- sep --"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] ] [count(./option)=3] ' @@ -376,8 +376,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name"] [@required="required"] [ - ./option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] ] [count(./option)=2] ' @@ -398,9 +398,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name"] [@required="required"] [ - ./option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@disabled="disabled"][not(@selected)][.=""] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] ] [count(./option)=3] ' @@ -438,8 +438,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [not(@required)] [ ./option[@value=""][.="[trans][/trans]"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=3] ' @@ -461,8 +461,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [not(@required)] [ ./option[@value=""][.="[trans][/trans]"] - /following-sibling::option[@value="0"][not(@selected)][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=3] ' @@ -485,8 +485,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [not(@required)] [ ./option[@value=""][not(@selected)][.="[trans]Select&Anything&Not&Me[/trans]"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=3] ' @@ -509,8 +509,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@required="required"] [ ./option[@value=""][.="[trans]Test&Me[/trans]"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=3] ' @@ -532,8 +532,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@required="required"] [ ./option[@value=""][.="[trans][/trans]"] - /following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=3] ' @@ -556,13 +556,13 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name"] [./optgroup[@label="[trans]Group&1[/trans]"] [ - ./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=2] ] [./optgroup[@label="[trans]Group&2[/trans]"] - [./option[@value="2"][not(@selected)][.="[trans]Choice&C[/trans]"]] + [./option[@value="&c"][not(@selected)][.="[trans]Choice&C[/trans]"]] [count(./option)=1] ] [count(./optgroup)=2] @@ -583,8 +583,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name[]"] [@multiple="multiple"] [ - ./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=2] ' @@ -605,8 +605,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name[]"] [@multiple="multiple"] [ - ./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=2] ' @@ -627,8 +627,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase [@name="name[]"] [@multiple="multiple"] [ - ./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] - /following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"] ] [count(./option)=2] ' @@ -646,9 +646,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="0"][@checked] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] - /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="1"][not(@checked)] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] [count(./input)=2] 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 8d5c868b25..cabdf76f2b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BooleanToStringTransformerTest.php @@ -15,11 +15,13 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransfo class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase { + const TRUE_VALUE = '1'; + protected $transformer; protected function setUp() { - $this->transformer = new BooleanToStringTransformer(); + $this->transformer = new BooleanToStringTransformer(self::TRUE_VALUE); } protected function tearDown() @@ -29,9 +31,9 @@ class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase public function testTransform() { - $this->assertEquals('1', $this->transformer->transform(true)); - $this->assertEquals('', $this->transformer->transform(false)); - $this->assertSame('', $this->transformer->transform(null)); + $this->assertEquals(self::TRUE_VALUE, $this->transformer->transform(true)); + $this->assertNull($this->transformer->transform(false)); + $this->assertNull($this->transformer->transform(null)); } public function testTransformExpectsBoolean() @@ -50,9 +52,9 @@ class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase public function testReverseTransform() { - $this->assertTrue($this->transformer->reverseTransform('1')); - $this->assertTrue($this->transformer->reverseTransform('0')); - $this->assertFalse($this->transformer->reverseTransform('')); + $this->assertTrue($this->transformer->reverseTransform(self::TRUE_VALUE)); + $this->assertTrue($this->transformer->reverseTransform('foobar')); + $this->assertTrue($this->transformer->reverseTransform('')); $this->assertFalse($this->transformer->reverseTransform(null)); } } 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 cc96bd940a..feb5343a07 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CheckboxTypeTest.php @@ -32,6 +32,15 @@ class CheckboxTypeTest extends TypeTestCase $this->assertTrue($view->get('checked')); } + public function testCheckedIfDataTrueWithEmptyValue() + { + $form = $this->factory->create('checkbox', null, array('value' => '')); + $form->setData(true); + $view = $form->createView(); + + $this->assertTrue($view->get('checked')); + } + public function testNotCheckedIfDataFalse() { $form = $this->factory->create('checkbox'); @@ -41,8 +50,63 @@ class CheckboxTypeTest extends TypeTestCase $this->assertFalse($view->get('checked')); } + public function testBindWithValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->bind('foobar'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getClientData()); + } + + public function testBindWithRandomValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->bind('krixikraxi'); + + $this->assertTrue($form->getData()); + $this->assertEquals('foobar', $form->getClientData()); + } + + public function testBindWithValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => 'foobar', + )); + $form->bind(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getClientData()); + } + + public function testBindWithEmptyValueChecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(''); + + $this->assertTrue($form->getData()); + $this->assertSame('', $form->getClientData()); + } + + public function testBindWithEmptyValueUnchecked() + { + $form = $this->factory->create('checkbox', null, array( + 'value' => '', + )); + $form->bind(null); + + $this->assertFalse($form->getData()); + $this->assertNull($form->getClientData()); + } + /** - * @dataProvider proviceTransformedData + * @dataProvider provideTransformedData */ public function testTransformedData($data, $expected) { @@ -60,7 +124,7 @@ class CheckboxTypeTest extends TypeTestCase $form = $this->builder ->create('expedited_shipping', 'checkbox') - ->prependClientTransformer($transformer) + ->prependNormTransformer($transformer) ->getForm(); $form->setData($data); $view = $form->createView(); @@ -68,7 +132,7 @@ class CheckboxTypeTest extends TypeTestCase $this->assertEquals($expected, $view->get('checked')); } - public function proviceTransformedData() + public function provideTransformedData() { return array( array('expedited', true), diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index 7e5c635398..227857fd1f 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; +use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; + use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; use Symfony\Component\Form\Extension\Core\View\ChoiceView; @@ -34,14 +36,6 @@ class ChoiceTypeTest extends TypeTestCase private $objectChoices; - private $stringButNumericChoices = array( - '0' => 'Bernhard', - '1' => 'Fabien', - '2' => 'Kris', - '3' => 'Jon', - '4' => 'Roman', - ); - protected $groupedChoices = array( 'Symfony' => array( 'a' => 'Bernhard', @@ -183,10 +177,10 @@ class ChoiceTypeTest extends TypeTestCase 'choices' => $this->choices, )); - $form->bind('1'); + $form->bind('b'); $this->assertEquals('b', $form->getData()); - $this->assertEquals('1', $form->getClientData()); + $this->assertEquals('b', $form->getClientData()); } public function testBindSingleNonExpandedObjectChoices() @@ -220,10 +214,10 @@ class ChoiceTypeTest extends TypeTestCase 'choices' => $this->choices, )); - $form->bind(array('0', '1')); + $form->bind(array('a', 'b')); $this->assertEquals(array('a', 'b'), $form->getData()); - $this->assertEquals(array('0', '1'), $form->getClientData()); + $this->assertEquals(array('a', 'b'), $form->getClientData()); } public function testBindMultipleNonExpandedObjectChoices() @@ -256,7 +250,7 @@ class ChoiceTypeTest extends TypeTestCase 'choices' => $this->choices, )); - $form->bind('1'); + $form->bind('b'); $this->assertSame('b', $form->getData()); $this->assertFalse($form[0]->getData()); @@ -264,11 +258,34 @@ class ChoiceTypeTest extends TypeTestCase $this->assertFalse($form[2]->getData()); $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); - $this->assertSame('', $form[0]->getClientData()); - $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertNull($form[0]->getClientData()); + $this->assertSame('b', $form[1]->getClientData()); + $this->assertNull($form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); + } + + public function testBindSingleExpandedNothingChecked() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => false, + 'expanded' => true, + 'choices' => $this->choices, + )); + + $form->bind(null); + + $this->assertSame(null, $form->getData()); + $this->assertFalse($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[3]->getData()); + $this->assertFalse($form[4]->getData()); + $this->assertNull($form[0]->getClientData()); + $this->assertNull($form[1]->getClientData()); + $this->assertNull($form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); } public function testBindSingleExpandedWithFalseDoesNotHaveExtraFields() @@ -292,17 +309,17 @@ class ChoiceTypeTest extends TypeTestCase 'expanded' => true, 'choices' => array( '' => 'Empty', - '1' => 'Not empty', + 1 => 'Not empty', ), )); - $form->bind('0'); + $form->bind(''); $this->assertNull($form->getData()); $this->assertTrue($form[0]->getData()); $this->assertFalse($form[1]->getData()); - $this->assertSame('1', $form[0]->getClientData()); - $this->assertSame('', $form[1]->getClientData()); + $this->assertSame('', $form[0]->getClientData()); + $this->assertNull($form[1]->getClientData()); } public function testBindSingleExpandedObjectChoices() @@ -329,11 +346,11 @@ class ChoiceTypeTest extends TypeTestCase $this->assertFalse($form[2]->getData()); $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); - $this->assertSame('', $form[0]->getClientData()); - $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertNull($form[0]->getClientData()); + $this->assertSame('2', $form[1]->getClientData()); + $this->assertNull($form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); } public function testBindSingleExpandedNumericChoices() @@ -352,34 +369,11 @@ class ChoiceTypeTest extends TypeTestCase $this->assertFalse($form[2]->getData()); $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); - $this->assertSame('', $form[0]->getClientData()); + $this->assertNull($form[0]->getClientData()); $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); - } - - public function testBindSingleExpandedStringsButNumericChoices() - { - $form = $this->factory->create('choice', null, array( - 'multiple' => false, - 'expanded' => true, - 'choices' => $this->stringButNumericChoices, - )); - - $form->bind('1'); - - $this->assertSame(1, $form->getData()); - $this->assertFalse($form[0]->getData()); - $this->assertTrue($form[1]->getData()); - $this->assertFalse($form[2]->getData()); - $this->assertFalse($form[3]->getData()); - $this->assertFalse($form[4]->getData()); - $this->assertSame('', $form[0]->getClientData()); - $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertNull($form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); } public function testBindMultipleExpanded() @@ -390,19 +384,43 @@ class ChoiceTypeTest extends TypeTestCase 'choices' => $this->choices, )); - $form->bind(array(0 => 'a', 1 => 'b')); + $form->bind(array('a', 'c')); - $this->assertSame(array(0 => 'a', 1 => 'b'), $form->getData()); + $this->assertSame(array('a', 'c'), $form->getData()); $this->assertTrue($form[0]->getData()); - $this->assertTrue($form[1]->getData()); - $this->assertFalse($form[2]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); - $this->assertSame('1', $form[0]->getClientData()); - $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertSame('a', $form[0]->getClientData()); + $this->assertNull($form[1]->getClientData()); + $this->assertSame('c', $form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); + } + + public function testBindMultipleExpandedWithEmptyField() + { + $form = $this->factory->create('choice', null, array( + 'multiple' => true, + 'expanded' => true, + 'choices' => array( + '' => 'Empty', + 1 => 'Not Empty', + 2 => 'Not Empty 2', + ), + 'value_strategy' => ChoiceList::COPY_CHOICE, + )); + + $form->bind(array('', '2')); + + $this->assertSame(array('', 2), $form->getData()); + $this->assertTrue($form[0]->getData()); + $this->assertFalse($form[1]->getData()); + $this->assertTrue($form[2]->getData()); + $this->assertSame('', $form[0]->getClientData()); + $this->assertNull($form[1]->getClientData()); + $this->assertSame('2', $form[2]->getClientData()); } public function testBindMultipleExpandedObjectChoices() @@ -421,7 +439,7 @@ class ChoiceTypeTest extends TypeTestCase ), )); - $form->bind(array(0 => '1', 1 => '2')); + $form->bind(array('1', '2')); $this->assertSame(array($this->objectChoices[0], $this->objectChoices[1]), $form->getData()); $this->assertTrue($form[0]->getData()); @@ -430,10 +448,10 @@ class ChoiceTypeTest extends TypeTestCase $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); $this->assertSame('1', $form[0]->getClientData()); - $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertSame('2', $form[1]->getClientData()); + $this->assertNull($form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); } public function testBindMultipleExpandedNumericChoices() @@ -444,7 +462,7 @@ class ChoiceTypeTest extends TypeTestCase 'choices' => $this->numericChoices, )); - $form->bind(array(1 => '1', 2 => '2')); + $form->bind(array('1', '2')); $this->assertSame(array(1, 2), $form->getData()); $this->assertFalse($form[0]->getData()); @@ -452,11 +470,11 @@ class ChoiceTypeTest extends TypeTestCase $this->assertTrue($form[2]->getData()); $this->assertFalse($form[3]->getData()); $this->assertFalse($form[4]->getData()); - $this->assertSame('', $form[0]->getClientData()); + $this->assertNull($form[0]->getClientData()); $this->assertSame('1', $form[1]->getClientData()); - $this->assertSame('1', $form[2]->getClientData()); - $this->assertSame('', $form[3]->getClientData()); - $this->assertSame('', $form[4]->getClientData()); + $this->assertSame('2', $form[2]->getClientData()); + $this->assertNull($form[3]->getClientData()); + $this->assertNull($form[4]->getClientData()); } /* @@ -598,10 +616,10 @@ class ChoiceTypeTest extends TypeTestCase $view = $form->createView(); $this->assertEquals(array( - new ChoiceView('0', 'A'), - new ChoiceView('1', 'B'), - new ChoiceView('2', 'C'), - new ChoiceView('3', 'D'), + new ChoiceView('a', 'A'), + new ChoiceView('b', 'B'), + new ChoiceView('c', 'C'), + new ChoiceView('d', 'D'), ), $view->get('choices')); } @@ -615,12 +633,12 @@ class ChoiceTypeTest extends TypeTestCase $view = $form->createView(); $this->assertEquals(array( - 0 => new ChoiceView('0', 'A'), - 2 => new ChoiceView('2', 'C'), + 0 => new ChoiceView('a', 'A'), + 2 => new ChoiceView('c', 'C'), ), $view->get('choices')); $this->assertEquals(array( - 1 => new ChoiceView('1', 'B'), - 3 => new ChoiceView('3', 'D'), + 1 => new ChoiceView('b', 'B'), + 3 => new ChoiceView('d', 'D'), ), $view->get('preferred_choices')); } @@ -634,19 +652,19 @@ class ChoiceTypeTest extends TypeTestCase $this->assertEquals(array( 'Symfony' => array( - 0 => new ChoiceView('0', 'Bernhard'), - 2 => new ChoiceView('2', 'Kris'), + 0 => new ChoiceView('a', 'Bernhard'), + 2 => new ChoiceView('c', 'Kris'), ), 'Doctrine' => array( - 4 => new ChoiceView('4', 'Roman'), + 4 => new ChoiceView('e', 'Roman'), ), ), $view->get('choices')); $this->assertEquals(array( 'Symfony' => array( - 1 => new ChoiceView('1', 'Fabien'), + 1 => new ChoiceView('b', 'Fabien'), ), 'Doctrine' => array( - 3 => new ChoiceView('3', 'Jon'), + 3 => new ChoiceView('d', 'Jon'), ), ), $view->get('preferred_choices')); }