bug #35938 [Form] Handle false as empty value on expanded choices (fancyweb)

This PR was merged into the 3.4 branch.

Discussion
----------

[Form] Handle false as empty value on expanded choices

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | https://github.com/symfony/symfony/issues/31572
| License       | MIT
| Doc PR        | -

This is the 3.4 version of https://github.com/symfony/symfony/pull/32747. The tests are the same. The added code has to be removed from master (if accepted).

Commits
-------

1a366bc378 [Form] Handle false as empty value on expanded choices
This commit is contained in:
Nicolas Grekas 2020-03-12 17:44:30 +01:00
commit aaddef3c57
4 changed files with 54 additions and 1 deletions

View File

@ -33,6 +33,7 @@ class CheckboxType extends AbstractType
// doing so also calls setDataLocked(true).
$builder->setData(isset($options['data']) ? $options['data'] : false);
$builder->addViewTransformer(new BooleanToStringTransformer($options['value']));
$builder->setAttribute('_false_is_empty', true); // @internal - A boolean flag to treat false as empty, see Form::isEmpty() - Do not rely on it, it will be removed in Symfony 5.1.
}
/**

View File

@ -725,7 +725,9 @@ class Form implements \IteratorAggregate, FormInterface
// arrays, countables
((\is_array($this->modelData) || $this->modelData instanceof \Countable) && 0 === \count($this->modelData)) ||
// traversables that are not countable
($this->modelData instanceof \Traversable && 0 === iterator_count($this->modelData));
($this->modelData instanceof \Traversable && 0 === iterator_count($this->modelData)) ||
// @internal - Do not rely on it, it will be removed in Symfony 5.1.
(false === $this->modelData && $this->config->getAttribute('_false_is_empty'));
}
/**

View File

@ -190,4 +190,13 @@ class CheckboxTypeTest extends BaseTypeTest
$this->assertSame($expectedData, $form->getNormData());
$this->assertSame($expectedData, $form->getData());
}
public function testSubmitNullIsEmpty()
{
$form = $this->factory->create(static::TESTED_TYPE);
$form->submit(null);
$this->assertTrue($form->isEmpty());
}
}

View File

@ -2046,4 +2046,45 @@ class ChoiceTypeTest extends BaseTypeTest
'Multiple expanded' => [true, true],
];
}
/**
* @dataProvider expandedIsEmptyWhenNoRealChoiceIsSelectedProvider
*/
public function testExpandedIsEmptyWhenNoRealChoiceIsSelected($expected, $submittedData, $multiple, $required, $placeholder)
{
$options = [
'expanded' => true,
'choices' => [
'foo' => 'bar',
],
'multiple' => $multiple,
'required' => $required,
];
if (!$multiple) {
$options['placeholder'] = $placeholder;
}
$form = $this->factory->create(static::TESTED_TYPE, null, $options);
$form->submit($submittedData);
$this->assertSame($expected, $form->isEmpty());
}
public function expandedIsEmptyWhenNoRealChoiceIsSelectedProvider()
{
// Some invalid cases are voluntarily not tested:
// - multiple with placeholder
// - required with placeholder
return [
'Nothing submitted / single / not required / without a placeholder -> should be empty' => [true, null, false, false, null],
'Nothing submitted / single / not required / with a placeholder -> should not be empty' => [false, null, false, false, 'ccc'], // It falls back on the placeholder
'Nothing submitted / single / required / without a placeholder -> should be empty' => [true, null, false, true, null],
'Nothing submitted / single / required / with a placeholder -> should be empty' => [true, null, false, true, 'ccc'],
'Nothing submitted / multiple / not required / without a placeholder -> should be empty' => [true, null, true, false, null],
'Nothing submitted / multiple / required / without a placeholder -> should be empty' => [true, null, true, true, null],
'Placeholder submitted / single / not required / with a placeholder -> should not be empty' => [false, '', false, false, 'ccc'], // The placeholder is a selected value
];
}
}