diff --git a/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php b/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php index c3218ae4ec..4cef560ee6 100644 --- a/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php +++ b/src/Symfony/Component/Form/Extension/Core/EventListener/ResizeFormListener.php @@ -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); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php index 64ae8832ff..e342efa524 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CollectionType.php @@ -100,6 +100,7 @@ class CollectionType extends AbstractType )); $resolver->setNormalizer('entry_options', $entryOptionsNormalizer); + $resolver->setAllowedTypes('delete_empty', array('bool', 'callable')); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index 7ed25ce27e..977cb8cf43 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -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(