feature #20496 [Form] Allow pass filter callback to delete_empty option. (Koc)

This PR was merged into the 3.4 branch.

Discussion
----------

[Form] Allow pass filter callback to delete_empty option.

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #13601, #13940, #22008, #22014
| License       | MIT
| Doc PR        | coming soon

Commits
-------

8630abe27a [Form] Allow pass filter callback to delete_empty option.
This commit is contained in:
Fabien Potencier 2017-07-26 08:57:20 +02:00
commit c0d99d13c0
3 changed files with 50 additions and 4 deletions

View File

@ -48,7 +48,7 @@ class ResizeFormListener implements EventSubscriberInterface
protected $allowDelete;
/**
* @var bool
* @var bool|callable
*/
private $deleteEmpty;
@ -148,14 +148,15 @@ class ResizeFormListener implements EventSubscriberInterface
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
}
if ($this->deleteEmpty) {
$previousData = $event->getForm()->getData();
if ($entryFilter = $this->deleteEmpty) {
$previousData = $form->getData();
foreach ($form as $name => $child) {
$isNew = !isset($previousData[$name]);
$isEmpty = is_callable($entryFilter) ? $entryFilter($child->getData()) : $child->isEmpty();
// $isNew can only be true if allowAdd is true, so we don't
// need to check allowAdd again
if ($child->isEmpty() && ($isNew || $this->allowDelete)) {
if ($isEmpty && ($isNew || $this->allowDelete)) {
unset($data[$name]);
$form->remove($name);
}

View File

@ -100,6 +100,7 @@ class CollectionType extends AbstractType
));
$resolver->setNormalizer('entry_options', $entryOptionsNormalizer);
$resolver->setAllowedTypes('delete_empty', array('bool', 'callable'));
}
/**

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Tests\Fixtures\Author;
use Symfony\Component\Form\Tests\Fixtures\AuthorType;
class CollectionTypeTest extends BaseTypeTest
{
@ -110,6 +111,49 @@ class CollectionTypeTest extends BaseTypeTest
$this->assertEquals(array('foo@foo.com'), $form->getData());
}
public function testResizedDownWithDeleteEmptyCallable()
{
$form = $this->factory->create(static::TESTED_TYPE, null, array(
'entry_type' => AuthorType::class,
'allow_delete' => true,
'delete_empty' => function (Author $obj = null) {
return null === $obj || empty($obj->firstName);
},
));
$form->setData(array(new Author('Bob'), new Author('Alice')));
$form->submit(array(array('firstName' => 'Bob'), array('firstName' => '')));
$this->assertTrue($form->has('0'));
$this->assertFalse($form->has('1'));
$this->assertEquals(new Author('Bob'), $form[0]->getData());
$this->assertEquals(array(new Author('Bob')), $form->getData());
}
public function testResizedDownIfSubmittedWithCompoundEmptyDataDeleteEmptyAndNoDataClass()
{
$form = $this->factory->create(static::TESTED_TYPE, null, array(
'entry_type' => AuthorType::class,
// If the field is not required, no new Author will be created if the
// form is completely empty
'entry_options' => array('data_class' => null),
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => function ($author) {
return empty($author['firstName']);
},
));
$form->setData(array(array('firstName' => 'first', 'lastName' => 'last')));
$form->submit(array(
array('firstName' => 's_first', 'lastName' => 's_last'),
array('firstName' => '', 'lastName' => ''),
));
$this->assertTrue($form->has('0'));
$this->assertFalse($form->has('1'));
$this->assertEquals(array('firstName' => 's_first', 'lastName' => 's_last'), $form[0]->getData());
$this->assertEquals(array(array('firstName' => 's_first', 'lastName' => 's_last')), $form->getData());
}
public function testDontAddEmptyDataIfDeleteEmpty()
{
$form = $this->factory->create(static::TESTED_TYPE, null, array(