[Form] Fixed handling of expanded choice lists, checkboxes and radio buttons with empty values ("")

This commit is contained in:
Bernhard Schussek 2012-04-10 11:51:28 +02:00
parent 07e261ea9e
commit 26451201e0
11 changed files with 306 additions and 153 deletions

View File

@ -167,17 +167,6 @@ UPGRADE FROM 2.0 to 2.1
colons and underscores, you can restore the old behavior by setting the colons and underscores, you can restore the old behavior by setting the
`index_strategy` choice field option to `ChoiceList::COPY_CHOICE`. `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 * In the choice field type's template, the structure of the `choices` variable
has changed. has changed.

View File

@ -442,8 +442,8 @@ class EntityTypeTest extends TypeTestCase
$this->assertSame($entity2, $field->getData()); $this->assertSame($entity2, $field->getData());
$this->assertFalse($field['1']->getData()); $this->assertFalse($field['1']->getData());
$this->assertTrue($field['2']->getData()); $this->assertTrue($field['2']->getData());
$this->assertSame('', $field['1']->getClientData()); $this->assertNull($field['1']->getClientData());
$this->assertSame('1', $field['2']->getClientData()); $this->assertSame('2', $field['2']->getClientData());
} }
public function testSubmitMultipleExpanded() public function testSubmitMultipleExpanded()
@ -462,7 +462,7 @@ class EntityTypeTest extends TypeTestCase
'property' => 'name', 'property' => 'name',
)); ));
$field->bind(array('1' => '1', '3' => '3')); $field->bind(array('1', '3'));
$expected = new ArrayCollection(array($entity1, $entity3)); $expected = new ArrayCollection(array($entity1, $entity3));
@ -472,8 +472,8 @@ class EntityTypeTest extends TypeTestCase
$this->assertFalse($field['2']->getData()); $this->assertFalse($field['2']->getData());
$this->assertTrue($field['3']->getData()); $this->assertTrue($field['3']->getData());
$this->assertSame('1', $field['1']->getClientData()); $this->assertSame('1', $field['1']->getClientData());
$this->assertSame('', $field['2']->getClientData()); $this->assertNull($field['2']->getClientData());
$this->assertSame('1', $field['3']->getClientData()); $this->assertSame('3', $field['3']->getClientData());
} }
public function testOverrideChoices() public function testOverrideChoices()

View File

@ -22,6 +22,22 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
*/ */
class BooleanToStringTransformer implements DataTransformerInterface 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. * Transforms a Boolean into a string.
* *
@ -34,14 +50,14 @@ class BooleanToStringTransformer implements DataTransformerInterface
public function transform($value) public function transform($value)
{ {
if (null === $value) { if (null === $value) {
return ''; return null;
} }
if (!is_bool($value)) { if (!is_bool($value)) {
throw new UnexpectedTypeException($value, 'Boolean'); 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'); throw new UnexpectedTypeException($value, 'string');
} }
return '' !== $value; return true;
} }
} }

View File

@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <bernhard.schussek@symfony-project.com>
*/
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');
}
}

View File

@ -25,7 +25,7 @@ class CheckboxType extends AbstractType
public function buildForm(FormBuilder $builder, array $options) public function buildForm(FormBuilder $builder, array $options)
{ {
$builder $builder
->appendClientTransformer(new BooleanToStringTransformer()) ->appendClientTransformer(new BooleanToStringTransformer($options['value']))
->setAttribute('value', $options['value']) ->setAttribute('value', $options['value'])
; ;
} }
@ -37,7 +37,7 @@ class CheckboxType extends AbstractType
{ {
$view $view
->set('value', $form->getAttribute('value')) ->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( return array(
'value' => '1', 'value' => '1',
'empty_data' => function (FormInterface $form, $clientData) {
return $clientData;
},
); );
} }

View File

@ -14,13 +14,14 @@ namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Exception\FormException; use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList; use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList; use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\Extension\Core\EventListener\FixRadioInputListener; 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\Extension\Core\EventListener\MergeCollectionListener;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToBooleanArrayTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
@ -81,7 +82,10 @@ class ChoiceType extends AbstractType
if ($options['expanded']) { if ($options['expanded']) {
if ($options['multiple']) { 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 { } else {
$builder $builder
->appendClientTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list'])) ->appendClientTransformer(new ChoiceToBooleanArrayTransformer($options['choice_list']))
@ -155,7 +159,7 @@ class ChoiceType extends AbstractType
'choice_list' => null, 'choice_list' => null,
'choices' => null, 'choices' => null,
'preferred_choices' => array(), 'preferred_choices' => array(),
'value_strategy' => ChoiceList::GENERATE, 'value_strategy' => ChoiceList::COPY_CHOICE,
'index_strategy' => ChoiceList::GENERATE, 'index_strategy' => ChoiceList::GENERATE,
'empty_data' => $multiple || $expanded ? array() : '', 'empty_data' => $multiple || $expanded ? array() : '',
'empty_value' => $multiple || $expanded || !isset($options['empty_value']) ? null : '', 'empty_value' => $multiple || $expanded || !isset($options['empty_value']) ? null : '',

View File

@ -470,7 +470,11 @@ class Form implements \IteratorAggregate, FormInterface
return $this; 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; $clientData = (string) $clientData;
} }
@ -522,11 +526,13 @@ class Form implements \IteratorAggregate, FormInterface
} }
if (null === $clientData || '' === $clientData) { if (null === $clientData || '' === $clientData) {
$clientData = $this->emptyData; $emptyData = $this->emptyData;
if ($clientData instanceof \Closure) { if ($emptyData instanceof \Closure) {
$clientData = $clientData($this); $emptyData = $emptyData($this, $clientData);
} }
$clientData = $emptyData;
} }
// Merge form data from children into existing client data // Merge form data from children into existing client data

View File

@ -330,8 +330,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name"] [@name="name"]
[@required="required"] [@required="required"]
[ [
./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
' '
@ -352,9 +352,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name"] [@name="name"]
[@required="required"] [@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[@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] [count(./option)=3]
' '
@ -376,8 +376,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name"] [@name="name"]
[@required="required"] [@required="required"]
[ [
./option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] ./option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
/following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
' '
@ -398,9 +398,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name"] [@name="name"]
[@required="required"] [@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[@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] [count(./option)=3]
' '
@ -438,8 +438,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[not(@required)] [not(@required)]
[ [
./option[@value=""][.="[trans][/trans]"] ./option[@value=""][.="[trans][/trans]"]
/following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=3] [count(./option)=3]
' '
@ -461,8 +461,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[not(@required)] [not(@required)]
[ [
./option[@value=""][.="[trans][/trans]"] ./option[@value=""][.="[trans][/trans]"]
/following-sibling::option[@value="0"][not(@selected)][.="[trans]Choice&A[/trans]"] /following-sibling::option[@value="&a"][not(@selected)][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=3] [count(./option)=3]
' '
@ -485,8 +485,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[not(@required)] [not(@required)]
[ [
./option[@value=""][not(@selected)][.="[trans]Select&Anything&Not&Me[/trans]"] ./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="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=3] [count(./option)=3]
' '
@ -509,8 +509,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@required="required"] [@required="required"]
[ [
./option[@value=""][.="[trans]Test&Me[/trans]"] ./option[@value=""][.="[trans]Test&Me[/trans]"]
/following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=3] [count(./option)=3]
' '
@ -532,8 +532,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@required="required"] [@required="required"]
[ [
./option[@value=""][.="[trans][/trans]"] ./option[@value=""][.="[trans][/trans]"]
/following-sibling::option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] /following-sibling::option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=3] [count(./option)=3]
' '
@ -556,13 +556,13 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name"] [@name="name"]
[./optgroup[@label="[trans]Group&1[/trans]"] [./optgroup[@label="[trans]Group&1[/trans]"]
[ [
./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
] ]
[./optgroup[@label="[trans]Group&2[/trans]"] [./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(./option)=1]
] ]
[count(./optgroup)=2] [count(./optgroup)=2]
@ -583,8 +583,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name[]"] [@name="name[]"]
[@multiple="multiple"] [@multiple="multiple"]
[ [
./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
' '
@ -605,8 +605,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name[]"] [@name="name[]"]
[@multiple="multiple"] [@multiple="multiple"]
[ [
./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
' '
@ -627,8 +627,8 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@name="name[]"] [@name="name[]"]
[@multiple="multiple"] [@multiple="multiple"]
[ [
./option[@value="0"][@selected="selected"][.="[trans]Choice&A[/trans]"] ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"]
/following-sibling::option[@value="1"][not(@selected)][.="[trans]Choice&B[/trans]"] /following-sibling::option[@value="&b"][not(@selected)][.="[trans]Choice&B[/trans]"]
] ]
[count(./option)=2] [count(./option)=2]
' '
@ -646,9 +646,9 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
$this->assertWidgetMatchesXpath($form->createView(), array(), $this->assertWidgetMatchesXpath($form->createView(), array(),
'/div '/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::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]"] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"]
] ]
[count(./input)=2] [count(./input)=2]

View File

@ -15,11 +15,13 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\BooleanToStringTransfo
class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase
{ {
const TRUE_VALUE = '1';
protected $transformer; protected $transformer;
protected function setUp() protected function setUp()
{ {
$this->transformer = new BooleanToStringTransformer(); $this->transformer = new BooleanToStringTransformer(self::TRUE_VALUE);
} }
protected function tearDown() protected function tearDown()
@ -29,9 +31,9 @@ class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase
public function testTransform() public function testTransform()
{ {
$this->assertEquals('1', $this->transformer->transform(true)); $this->assertEquals(self::TRUE_VALUE, $this->transformer->transform(true));
$this->assertEquals('', $this->transformer->transform(false)); $this->assertNull($this->transformer->transform(false));
$this->assertSame('', $this->transformer->transform(null)); $this->assertNull($this->transformer->transform(null));
} }
public function testTransformExpectsBoolean() public function testTransformExpectsBoolean()
@ -50,9 +52,9 @@ class BooleanToStringTransformerTest extends \PHPUnit_Framework_TestCase
public function testReverseTransform() public function testReverseTransform()
{ {
$this->assertTrue($this->transformer->reverseTransform('1')); $this->assertTrue($this->transformer->reverseTransform(self::TRUE_VALUE));
$this->assertTrue($this->transformer->reverseTransform('0')); $this->assertTrue($this->transformer->reverseTransform('foobar'));
$this->assertFalse($this->transformer->reverseTransform('')); $this->assertTrue($this->transformer->reverseTransform(''));
$this->assertFalse($this->transformer->reverseTransform(null)); $this->assertFalse($this->transformer->reverseTransform(null));
} }
} }

View File

@ -32,6 +32,15 @@ class CheckboxTypeTest extends TypeTestCase
$this->assertTrue($view->get('checked')); $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() public function testNotCheckedIfDataFalse()
{ {
$form = $this->factory->create('checkbox'); $form = $this->factory->create('checkbox');
@ -41,8 +50,63 @@ class CheckboxTypeTest extends TypeTestCase
$this->assertFalse($view->get('checked')); $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) public function testTransformedData($data, $expected)
{ {
@ -60,7 +124,7 @@ class CheckboxTypeTest extends TypeTestCase
$form = $this->builder $form = $this->builder
->create('expedited_shipping', 'checkbox') ->create('expedited_shipping', 'checkbox')
->prependClientTransformer($transformer) ->prependNormTransformer($transformer)
->getForm(); ->getForm();
$form->setData($data); $form->setData($data);
$view = $form->createView(); $view = $form->createView();
@ -68,7 +132,7 @@ class CheckboxTypeTest extends TypeTestCase
$this->assertEquals($expected, $view->get('checked')); $this->assertEquals($expected, $view->get('checked'));
} }
public function proviceTransformedData() public function provideTransformedData()
{ {
return array( return array(
array('expedited', true), array('expedited', true),

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type; 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\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\Extension\Core\View\ChoiceView; use Symfony\Component\Form\Extension\Core\View\ChoiceView;
@ -34,14 +36,6 @@ class ChoiceTypeTest extends TypeTestCase
private $objectChoices; private $objectChoices;
private $stringButNumericChoices = array(
'0' => 'Bernhard',
'1' => 'Fabien',
'2' => 'Kris',
'3' => 'Jon',
'4' => 'Roman',
);
protected $groupedChoices = array( protected $groupedChoices = array(
'Symfony' => array( 'Symfony' => array(
'a' => 'Bernhard', 'a' => 'Bernhard',
@ -183,10 +177,10 @@ class ChoiceTypeTest extends TypeTestCase
'choices' => $this->choices, 'choices' => $this->choices,
)); ));
$form->bind('1'); $form->bind('b');
$this->assertEquals('b', $form->getData()); $this->assertEquals('b', $form->getData());
$this->assertEquals('1', $form->getClientData()); $this->assertEquals('b', $form->getClientData());
} }
public function testBindSingleNonExpandedObjectChoices() public function testBindSingleNonExpandedObjectChoices()
@ -220,10 +214,10 @@ class ChoiceTypeTest extends TypeTestCase
'choices' => $this->choices, 'choices' => $this->choices,
)); ));
$form->bind(array('0', '1')); $form->bind(array('a', 'b'));
$this->assertEquals(array('a', 'b'), $form->getData()); $this->assertEquals(array('a', 'b'), $form->getData());
$this->assertEquals(array('0', '1'), $form->getClientData()); $this->assertEquals(array('a', 'b'), $form->getClientData());
} }
public function testBindMultipleNonExpandedObjectChoices() public function testBindMultipleNonExpandedObjectChoices()
@ -256,7 +250,7 @@ class ChoiceTypeTest extends TypeTestCase
'choices' => $this->choices, 'choices' => $this->choices,
)); ));
$form->bind('1'); $form->bind('b');
$this->assertSame('b', $form->getData()); $this->assertSame('b', $form->getData());
$this->assertFalse($form[0]->getData()); $this->assertFalse($form[0]->getData());
@ -264,11 +258,34 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertFalse($form[2]->getData()); $this->assertFalse($form[2]->getData());
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->getData()); $this->assertFalse($form[4]->getData());
$this->assertSame('', $form[0]->getClientData()); $this->assertNull($form[0]->getClientData());
$this->assertSame('1', $form[1]->getClientData()); $this->assertSame('b', $form[1]->getClientData());
$this->assertSame('', $form[2]->getClientData()); $this->assertNull($form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->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() public function testBindSingleExpandedWithFalseDoesNotHaveExtraFields()
@ -292,17 +309,17 @@ class ChoiceTypeTest extends TypeTestCase
'expanded' => true, 'expanded' => true,
'choices' => array( 'choices' => array(
'' => 'Empty', '' => 'Empty',
'1' => 'Not empty', 1 => 'Not empty',
), ),
)); ));
$form->bind('0'); $form->bind('');
$this->assertNull($form->getData()); $this->assertNull($form->getData());
$this->assertTrue($form[0]->getData()); $this->assertTrue($form[0]->getData());
$this->assertFalse($form[1]->getData()); $this->assertFalse($form[1]->getData());
$this->assertSame('1', $form[0]->getClientData()); $this->assertSame('', $form[0]->getClientData());
$this->assertSame('', $form[1]->getClientData()); $this->assertNull($form[1]->getClientData());
} }
public function testBindSingleExpandedObjectChoices() public function testBindSingleExpandedObjectChoices()
@ -329,11 +346,11 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertFalse($form[2]->getData()); $this->assertFalse($form[2]->getData());
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->getData()); $this->assertFalse($form[4]->getData());
$this->assertSame('', $form[0]->getClientData()); $this->assertNull($form[0]->getClientData());
$this->assertSame('1', $form[1]->getClientData()); $this->assertSame('2', $form[1]->getClientData());
$this->assertSame('', $form[2]->getClientData()); $this->assertNull($form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->getClientData()); $this->assertNull($form[4]->getClientData());
} }
public function testBindSingleExpandedNumericChoices() public function testBindSingleExpandedNumericChoices()
@ -352,34 +369,11 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertFalse($form[2]->getData()); $this->assertFalse($form[2]->getData());
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->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[1]->getClientData());
$this->assertSame('', $form[2]->getClientData()); $this->assertNull($form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->getClientData()); $this->assertNull($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());
} }
public function testBindMultipleExpanded() public function testBindMultipleExpanded()
@ -390,19 +384,43 @@ class ChoiceTypeTest extends TypeTestCase
'choices' => $this->choices, '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[0]->getData());
$this->assertTrue($form[1]->getData()); $this->assertFalse($form[1]->getData());
$this->assertFalse($form[2]->getData()); $this->assertTrue($form[2]->getData());
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->getData()); $this->assertFalse($form[4]->getData());
$this->assertSame('1', $form[0]->getClientData()); $this->assertSame('a', $form[0]->getClientData());
$this->assertSame('1', $form[1]->getClientData()); $this->assertNull($form[1]->getClientData());
$this->assertSame('', $form[2]->getClientData()); $this->assertSame('c', $form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->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() 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->assertSame(array($this->objectChoices[0], $this->objectChoices[1]), $form->getData());
$this->assertTrue($form[0]->getData()); $this->assertTrue($form[0]->getData());
@ -430,10 +448,10 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->getData()); $this->assertFalse($form[4]->getData());
$this->assertSame('1', $form[0]->getClientData()); $this->assertSame('1', $form[0]->getClientData());
$this->assertSame('1', $form[1]->getClientData()); $this->assertSame('2', $form[1]->getClientData());
$this->assertSame('', $form[2]->getClientData()); $this->assertNull($form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->getClientData()); $this->assertNull($form[4]->getClientData());
} }
public function testBindMultipleExpandedNumericChoices() public function testBindMultipleExpandedNumericChoices()
@ -444,7 +462,7 @@ class ChoiceTypeTest extends TypeTestCase
'choices' => $this->numericChoices, 'choices' => $this->numericChoices,
)); ));
$form->bind(array(1 => '1', 2 => '2')); $form->bind(array('1', '2'));
$this->assertSame(array(1, 2), $form->getData()); $this->assertSame(array(1, 2), $form->getData());
$this->assertFalse($form[0]->getData()); $this->assertFalse($form[0]->getData());
@ -452,11 +470,11 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertTrue($form[2]->getData()); $this->assertTrue($form[2]->getData());
$this->assertFalse($form[3]->getData()); $this->assertFalse($form[3]->getData());
$this->assertFalse($form[4]->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[1]->getClientData());
$this->assertSame('1', $form[2]->getClientData()); $this->assertSame('2', $form[2]->getClientData());
$this->assertSame('', $form[3]->getClientData()); $this->assertNull($form[3]->getClientData());
$this->assertSame('', $form[4]->getClientData()); $this->assertNull($form[4]->getClientData());
} }
/* /*
@ -598,10 +616,10 @@ class ChoiceTypeTest extends TypeTestCase
$view = $form->createView(); $view = $form->createView();
$this->assertEquals(array( $this->assertEquals(array(
new ChoiceView('0', 'A'), new ChoiceView('a', 'A'),
new ChoiceView('1', 'B'), new ChoiceView('b', 'B'),
new ChoiceView('2', 'C'), new ChoiceView('c', 'C'),
new ChoiceView('3', 'D'), new ChoiceView('d', 'D'),
), $view->get('choices')); ), $view->get('choices'));
} }
@ -615,12 +633,12 @@ class ChoiceTypeTest extends TypeTestCase
$view = $form->createView(); $view = $form->createView();
$this->assertEquals(array( $this->assertEquals(array(
0 => new ChoiceView('0', 'A'), 0 => new ChoiceView('a', 'A'),
2 => new ChoiceView('2', 'C'), 2 => new ChoiceView('c', 'C'),
), $view->get('choices')); ), $view->get('choices'));
$this->assertEquals(array( $this->assertEquals(array(
1 => new ChoiceView('1', 'B'), 1 => new ChoiceView('b', 'B'),
3 => new ChoiceView('3', 'D'), 3 => new ChoiceView('d', 'D'),
), $view->get('preferred_choices')); ), $view->get('preferred_choices'));
} }
@ -634,19 +652,19 @@ class ChoiceTypeTest extends TypeTestCase
$this->assertEquals(array( $this->assertEquals(array(
'Symfony' => array( 'Symfony' => array(
0 => new ChoiceView('0', 'Bernhard'), 0 => new ChoiceView('a', 'Bernhard'),
2 => new ChoiceView('2', 'Kris'), 2 => new ChoiceView('c', 'Kris'),
), ),
'Doctrine' => array( 'Doctrine' => array(
4 => new ChoiceView('4', 'Roman'), 4 => new ChoiceView('e', 'Roman'),
), ),
), $view->get('choices')); ), $view->get('choices'));
$this->assertEquals(array( $this->assertEquals(array(
'Symfony' => array( 'Symfony' => array(
1 => new ChoiceView('1', 'Fabien'), 1 => new ChoiceView('b', 'Fabien'),
), ),
'Doctrine' => array( 'Doctrine' => array(
3 => new ChoiceView('3', 'Jon'), 3 => new ChoiceView('d', 'Jon'),
), ),
), $view->get('preferred_choices')); ), $view->get('preferred_choices'));
} }