Merge branch '3.0'

* 3.0:
  [Form] refactor CheckboxListMapper and RadioListMapper
  Revert "[Form] refactor `RadioListMapper::mapDataToForm()`"
This commit is contained in:
Fabien Potencier 2016-04-28 11:57:20 +02:00
commit 461e8712bc
6 changed files with 105 additions and 100 deletions

View File

@ -274,7 +274,7 @@ class EntityTypeTest extends TypeTestCase
$field->submit(null);
$this->assertNull($field->getData());
$this->assertNull($field->getViewData());
$this->assertSame('', $field->getViewData(), 'View data is always a string');
}
public function testSubmitSingleNonExpandedNull()

View File

@ -21,11 +21,19 @@
"symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
<<<<<<< HEAD
"symfony/stopwatch": "~2.8|~3.0",
"symfony/dependency-injection": "~2.8|~3.0",
"symfony/form": "~3.0",
"symfony/http-kernel": "~2.8|~3.0",
"symfony/property-access": "~2.8|~3.0",
=======
"symfony/stopwatch": "~2.2|~3.0.0",
"symfony/dependency-injection": "~2.2|~3.0.0",
"symfony/form": "~2.8.5|~3.0.5",
"symfony/http-kernel": "~2.2|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0",
>>>>>>> 2.8
"symfony/property-info": "~2.8|3.0",
"symfony/security": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",

View File

@ -11,9 +11,8 @@
namespace Symfony\Component\Form\Extension\Core\DataMapper;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* Maps choices to/from checkbox forms.
@ -26,16 +25,6 @@ use Symfony\Component\Form\Exception\TransformationFailedException;
*/
class CheckboxListMapper implements DataMapperInterface
{
/**
* @var ChoiceListInterface
*/
private $choiceList;
public function __construct(ChoiceListInterface $choiceList)
{
$this->choiceList = $choiceList;
}
/**
* {@inheritdoc}
*/
@ -46,22 +35,12 @@ class CheckboxListMapper implements DataMapperInterface
}
if (!is_array($choices)) {
throw new TransformationFailedException('Expected an array.');
}
try {
$valueMap = array_flip($this->choiceList->getValuesForChoices($choices));
} catch (\Exception $e) {
throw new TransformationFailedException(
'Can not read the choices from the choice list.',
$e->getCode(),
$e
);
throw new UnexpectedTypeException($choices, 'array');
}
foreach ($checkboxes as $checkbox) {
$value = $checkbox->getConfig()->getOption('value');
$checkbox->setData(isset($valueMap[$value]) ? true : false);
$checkbox->setData(in_array($value, $choices, true));
}
}
@ -70,6 +49,10 @@ class CheckboxListMapper implements DataMapperInterface
*/
public function mapFormsToData($checkboxes, &$choices)
{
if (!is_array($choices)) {
throw new UnexpectedTypeException($choices, 'array');
}
$values = array();
foreach ($checkboxes as $checkbox) {
@ -79,14 +62,6 @@ class CheckboxListMapper implements DataMapperInterface
}
}
try {
$choices = $this->choiceList->getChoicesForValues($values);
} catch (\Exception $e) {
throw new TransformationFailedException(
'Can not read the values from the choice list.',
$e->getCode(),
$e
);
}
$choices = $values;
}
}

View File

@ -11,8 +11,8 @@
namespace Symfony\Component\Form\Extension\Core\DataMapper;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
/**
* Maps choices to/from radio forms.
@ -25,24 +25,18 @@ use Symfony\Component\Form\DataMapperInterface;
*/
class RadioListMapper implements DataMapperInterface
{
/**
* @var ChoiceListInterface
*/
private $choiceList;
public function __construct(ChoiceListInterface $choiceList)
{
$this->choiceList = $choiceList;
}
/**
* {@inheritdoc}
*/
public function mapDataToForms($data, $radios)
public function mapDataToForms($choice, $radios)
{
if (!is_string($choice)) {
throw new UnexpectedTypeException($choice, 'string');
}
foreach ($radios as $radio) {
$value = $radio->getConfig()->getOption('value');
$radio->setData($value === $data ? true : false);
$radio->setData($choice === $value);
}
}
@ -51,6 +45,10 @@ class RadioListMapper implements DataMapperInterface
*/
public function mapFormsToData($radios, &$choice)
{
if (null !== $choice && !is_string($choice)) {
throw new UnexpectedTypeException($choice, 'null or string');
}
$choice = null;
foreach ($radios as $radio) {
@ -59,8 +57,7 @@ class RadioListMapper implements DataMapperInterface
return;
}
$value = $radio->getConfig()->getOption('value');
$choice = current($this->choiceList->getChoicesForValues(array($value)));
$choice = $radio->getConfig()->getOption('value');
return;
}

View File

@ -61,9 +61,7 @@ class ChoiceType extends AbstractType
$builder->setAttribute('choice_list', $choiceList);
if ($options['expanded']) {
$builder->setDataMapper($options['multiple']
? new CheckboxListMapper($choiceList)
: new RadioListMapper($choiceList));
$builder->setDataMapper($options['multiple'] ? new CheckboxListMapper() : new RadioListMapper());
// Initialize all choices before doing the index check below.
// This helps in cases where index checks are optimized for non
@ -134,30 +132,13 @@ class ChoiceType extends AbstractType
$event->setData($data);
});
}
if (!$options['multiple']) {
// For radio lists, transform empty arrays to null
// This is kind of a hack necessary because the RadioListMapper
// is not invoked for forms without choices
$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
if (array() === $event->getData()) {
$event->setData(null);
}
});
// For radio lists, pre selection of the choice needs to pre set data
// with the string value so it can be matched in
// {@link \Symfony\Component\Form\Extension\Core\DataMapper\RadioListMapper::mapDataToForms()}
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$choiceList = $event->getForm()->getConfig()->getAttribute('choice_list');
$value = current($choiceList->getValuesForChoices(array($event->getData())));
$event->setData((string) $value);
});
}
} elseif ($options['multiple']) {
// <select> tag with "multiple" option
if ($options['multiple']) {
// <select> tag with "multiple" option or list of checkbox inputs
$builder->addViewTransformer(new ChoicesToValuesTransformer($choiceList));
} else {
// <select> tag without "multiple" option
// <select> tag without "multiple" option or list of radio inputs
$builder->addViewTransformer(new ChoiceToValueTransformer($choiceList));
}
@ -252,7 +233,11 @@ class ChoiceType extends AbstractType
public function configureOptions(OptionsResolver $resolver)
{
$emptyData = function (Options $options) {
if ($options['multiple'] || $options['expanded']) {
if ($options['expanded'] && !$options['multiple']) {
return;
}
if ($options['multiple']) {
return array();
}

View File

@ -30,6 +30,20 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
'n/a' => '',
);
private $booleanChoicesWithNull = array(
'Yes' => true,
'No' => false,
'n/a' => null,
);
private $numericChoicesFlipped = array(
0 => 'Bernhard',
1 => 'Fabien',
2 => 'Kris',
3 => 'Jon',
4 => 'Roman',
);
private $objectChoices;
protected $groupedChoices = array(
@ -135,6 +149,19 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$this->assertTrue($view->children[2]->vars['checked'], 'Empty value should be pre selected');
}
public function testExpandedChoiceListWithBooleanAndNullValues()
{
$view = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
'choices' => $this->booleanChoicesWithNull,
'choices_as_values' => true,
'expanded' => true,
))->createView();
$this->assertFalse($view->children[0]->vars['checked'], 'True value should not be pre selected');
$this->assertFalse($view->children[1]->vars['checked'], 'False value should not be pre selected');
$this->assertTrue($view->children[2]->vars['checked'], 'Empty value should be pre selected');
}
public function testExpandedChoiceListWithScalarValuesAndFalseAsPreSetData()
{
$view = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', false, array(
@ -148,6 +175,19 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$this->assertFalse($view->children[2]->vars['checked'], 'Empty value should not be pre selected');
}
public function testExpandedChoiceListWithBooleanAndNullValuesAndFalseAsPreSetData()
{
$view = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', false, array(
'choices' => $this->booleanChoicesWithNull,
'choices_as_values' => true,
'expanded' => true,
))->createView();
$this->assertFalse($view->children[0]->vars['checked'], 'True value should not be pre selected');
$this->assertTrue($view->children[1]->vars['checked'], 'False value should be pre selected');
$this->assertFalse($view->children[2]->vars['checked'], 'Null value should not be pre selected');
}
public function testPlaceholderPresentOnNonRequiredExpandedSingleChoice()
{
$form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(
@ -263,7 +303,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$view = $form->createView();
$this->assertSame('', $view->vars['value'], 'Value should be empty');
$this->assertSame('', $view->vars['value'], 'Value should be an empty string');
$this->assertSame('1', $view->vars['choices'][0]->value);
$this->assertSame('0', $view->vars['choices'][1]->value, 'Choice "false" should have "0" as value');
$this->assertFalse($view->children[1]->vars['checked'], 'Choice "false" should not be selected');
@ -767,8 +807,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(null);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertFalse($form[0]->getData());
@ -798,8 +838,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(null);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -815,8 +855,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit('');
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertFalse($form[0]->getData());
@ -846,8 +886,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit('');
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -863,8 +903,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(false);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertFalse($form[0]->getData());
@ -894,8 +934,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(false);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -911,8 +951,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(null);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertTrue($form['placeholder']->getData());
@ -944,8 +984,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(null);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -961,8 +1001,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit('');
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertTrue($form['placeholder']->getData());
@ -994,8 +1034,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit('');
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -1011,8 +1051,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(false);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
$this->assertTrue($form['placeholder']->getData());
@ -1044,8 +1084,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit(false);
$this->assertNull($form->getData());
$this->assertNull($form->getViewData());
$this->assertEmpty($form->getExtraData());
$this->assertSame('', $form->getViewData(), 'View data should always be a string');
$this->assertSame(array(), $form->getExtraData(), 'ChoiceType is compound when expanded, extra data should always be an array');
$this->assertTrue($form->isSynchronized());
}
@ -1062,7 +1102,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$form->submit('');
$this->assertNull($form->getData());
$this->assertSame('', $form->getData());
$this->assertTrue($form->isSynchronized());
$this->assertTrue($form[0]->getData());