[Form] Added delete_empty option to allow proper emptyData handling of collections

This commit is contained in:
Peter Rehm 2013-12-14 15:36:37 +01:00 committed by Fabien Potencier
parent efcca3e2e1
commit 8bdb7a0460
5 changed files with 112 additions and 4 deletions

View File

@ -45,12 +45,18 @@ class ResizeFormListener implements EventSubscriberInterface
*/
protected $allowDelete;
public function __construct($type, array $options = array(), $allowAdd = false, $allowDelete = false)
/**
* @var bool
*/
private $deleteEmpty;
public function __construct($type, array $options = array(), $allowAdd = false, $allowDelete = false, $deleteEmpty = false)
{
$this->type = $type;
$this->allowAdd = $allowAdd;
$this->allowDelete = $allowDelete;
$this->options = $options;
$this->deleteEmpty = $deleteEmpty;
}
public static function getSubscribedEvents()
@ -126,8 +132,13 @@ class ResizeFormListener implements EventSubscriberInterface
public function onSubmit(FormEvent $event)
{
$form = $event->getForm();
$previousData = $event->getForm()->getData();
$data = $event->getData();
// At this point, $data is an array or an array-like object that already contains the
// new entries, which were added by the data mapper. The data mapper ignores existing
// entries, so we need to manually unset removed entries in the collection.
if (null === $data) {
$data = array();
}
@ -136,10 +147,23 @@ class ResizeFormListener implements EventSubscriberInterface
throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
}
if ($this->deleteEmpty) {
foreach ($form as $name => $child) {
$isNew = !isset($previousData[$name]);
// $isNew can only be true if allowAdd is true, so we don't
// need to check allowAdd again
if ($child->isEmpty() && ($isNew || $this->allowDelete)) {
unset($data[$name]);
$form->remove($name);
}
}
}
// The data mapper only adds, but does not remove items, so do this
// here
if ($this->allowDelete) {
foreach ($data as $name => $child) {
foreach ($data as $name => $childData) {
if (!$form->has($name)) {
unset($data[$name]);
}

View File

@ -37,7 +37,8 @@ class CollectionType extends AbstractType
$options['type'],
$options['options'],
$options['allow_add'],
$options['allow_delete']
$options['allow_delete'],
$options['delete_empty']
);
$builder->addEventSubscriber($resizeListener);
@ -86,6 +87,7 @@ class CollectionType extends AbstractType
'prototype_name' => '__name__',
'type' => 'text',
'options' => array(),
'delete_empty' => false,
));
$resolver->setNormalizers(array(

View File

@ -899,7 +899,9 @@ class Form implements \IteratorAggregate, FormInterface
}
if (isset($this->children[$name])) {
$this->children[$name]->setParent(null);
if (!$this->children[$name]->isSubmitted()) {
$this->children[$name]->setParent(null);
}
unset($this->children[$name]);
}

View File

@ -12,6 +12,8 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\Tests\Fixtures\Author;
use Symfony\Component\Form\Tests\Fixtures\AuthorType;
class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
{
@ -88,6 +90,78 @@ class CollectionTypeTest extends \Symfony\Component\Form\Test\TypeTestCase
$this->assertEquals(array('foo@foo.com'), $form->getData());
}
public function testResizedDownIfSubmittedWithEmptyDataAndDeleteEmpty()
{
$form = $this->factory->create('collection', null, array(
'type' => 'text',
'allow_delete' => true,
'delete_empty' => true,
));
$form->setData(array('foo@foo.com', 'bar@bar.com'));
$form->submit(array('foo@foo.com', ''));
$this->assertTrue($form->has('0'));
$this->assertFalse($form->has('1'));
$this->assertEquals('foo@foo.com', $form[0]->getData());
$this->assertEquals(array('foo@foo.com'), $form->getData());
}
public function testDontAddEmptyDataIfDeleteEmpty()
{
$form = $this->factory->create('collection', null, array(
'type' => 'text',
'allow_add' => true,
'delete_empty' => true,
));
$form->setData(array('foo@foo.com'));
$form->submit(array('foo@foo.com', ''));
$this->assertTrue($form->has('0'));
$this->assertFalse($form->has('1'));
$this->assertEquals('foo@foo.com', $form[0]->getData());
$this->assertEquals(array('foo@foo.com'), $form->getData());
}
public function testNoDeleteEmptyIfDeleteNotAllowed()
{
$form = $this->factory->create('collection', null, array(
'type' => 'text',
'allow_delete' => false,
'delete_empty' => true,
));
$form->setData(array('foo@foo.com'));
$form->submit(array(''));
$this->assertTrue($form->has('0'));
$this->assertEquals('', $form[0]->getData());
}
public function testResizedDownIfSubmittedWithCompoundEmptyDataAndDeleteEmpty()
{
$form = $this->factory->create('collection', null, array(
'type' => new AuthorType(),
// If the field is not required, no new Author will be created if the
// form is completely empty
'options' => array('required' => false),
'allow_add' => true,
'delete_empty' => true,
));
$form->setData(array(new Author('first', '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(new Author('s_first', 's_last'), $form[0]->getData());
$this->assertEquals(array(new Author('s_first', 's_last')), $form->getData());
}
public function testNotResizedIfSubmittedWithExtraData()
{
$form = $this->factory->create('collection', null, array(

View File

@ -21,6 +21,12 @@ class Author
private $privateProperty;
public function __construct($firstName = null, $lastName = null)
{
$this->firstName = $firstName;
$this->lastName = $lastName;
}
public function setLastName($lastName)
{
$this->lastName = $lastName;