merged branch bschussek/issue3899 (PR #4813)

Commits
-------

9c94b48 [Form] Fixed the "data" option to supersede default data set in the model

Discussion
----------

[Form] Fixed the "data" option to supersede default data set in the model

Bug fix: yes
Feature addition: no
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: #3899
Todo: -
This commit is contained in:
Fabien Potencier 2012-07-10 08:22:37 +02:00
commit 4bab36b2c7
9 changed files with 103 additions and 5 deletions

View File

@ -146,3 +146,4 @@ CHANGELOG
* [BC BREAK] fixed rendering of errors for DateType, BirthdayType and similar ones * [BC BREAK] fixed rendering of errors for DateType, BirthdayType and similar ones
* [BC BREAK] fixed: form constraints are only validated if they belong to the validated group * [BC BREAK] fixed: form constraints are only validated if they belong to the validated group
* deprecated `bindRequest` in `Form` and replaced it by a listener to FormEvents::PRE_BIND * deprecated `bindRequest` in `Form` and replaced it by a listener to FormEvents::PRE_BIND
* fixed: the "data" option supersedes default values from the model

View File

@ -43,7 +43,8 @@ class FormType extends AbstractType
->setByReference($options['by_reference']) ->setByReference($options['by_reference'])
->setVirtual($options['virtual']) ->setVirtual($options['virtual'])
->setCompound($options['compound']) ->setCompound($options['compound'])
->setData($options['data']) ->setData(isset($options['data']) ? $options['data'] : null)
->setDataLocked(isset($options['data']))
->setDataMapper($options['compound'] ? new PropertyPathMapper() : null) ->setDataMapper($options['compound'] ? new PropertyPathMapper() : null)
->addEventSubscriber(new BindRequestListener()) ->addEventSubscriber(new BindRequestListener())
; ;
@ -183,7 +184,6 @@ class FormType extends AbstractType
}; };
$resolver->setDefaults(array( $resolver->setDefaults(array(
'data' => null,
'data_class' => $dataClass, 'data_class' => $dataClass,
'empty_data' => $emptyData, 'empty_data' => $emptyData,
'trim' => true, 'trim' => true,
@ -205,6 +205,12 @@ class FormType extends AbstractType
'inline' => $inline, 'inline' => $inline,
)); ));
// If data is given, the form is locked to that data
// (independent of its value)
$resolver->setOptional(array(
'data',
));
$resolver->setAllowedTypes(array( $resolver->setAllowedTypes(array(
'attr' => 'array', 'attr' => 'array',
'label_attr' => 'array', 'label_attr' => 'array',

View File

@ -331,6 +331,11 @@ class Form implements \IteratorAggregate, FormInterface
throw new AlreadyBoundException('You cannot change the data of a bound form'); throw new AlreadyBoundException('You cannot change the data of a bound form');
} }
// Don't allow modifications of the configured data if the data is locked
if ($this->config->getDataLocked() && $modelData !== $this->config->getData()) {
return $this;
}
if (is_object($modelData) && !$this->config->getByReference()) { if (is_object($modelData) && !$this->config->getByReference()) {
$modelData = clone $modelData; $modelData = clone $modelData;
} }

View File

@ -118,6 +118,11 @@ class FormConfig implements FormConfigEditorInterface
*/ */
private $dataClass; private $dataClass;
/**
* @var Boolean
*/
private $dataLocked;
/** /**
* @var array * @var array
*/ */
@ -507,6 +512,14 @@ class FormConfig implements FormConfigEditorInterface
return $this->dataClass; return $this->dataClass;
} }
/**
* {@inheritdoc}
*/
public function getDataLocked()
{
return $this->dataLocked;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -675,6 +688,16 @@ class FormConfig implements FormConfigEditorInterface
return $this; return $this;
} }
/**
* {@inheritdoc}
*/
public function setDataLocked($locked)
{
$this->dataLocked = $locked;
return $this;
}
/** /**
* Validates whether the given variable is a valid form name. * Validates whether the given variable is a valid form name.
* *

View File

@ -227,4 +227,17 @@ interface FormConfigEditorInterface extends FormConfigInterface
* @return self The configuration object. * @return self The configuration object.
*/ */
public function setData($data); public function setData($data);
/**
* Locks the form's data to the data passed in the configuration.
*
* A form with locked data is restricted to the data passed in
* this configuration. The data can only be modified then by
* binding the form.
*
* @param Boolean $locked Whether to lock the default data.
*
* @return self The configuration object.
*/
public function setDataLocked($locked);
} }

View File

@ -181,6 +181,17 @@ interface FormConfigInterface
*/ */
public function getDataClass(); public function getDataClass();
/**
* Returns whether the form's data is locked.
*
* A form with locked data is restricted to the data passed in
* this configuration. The data can only be modified then by
* binding the form.
*
* @return Boolean Whether the data is locked.
*/
public function getDataLocked();
/** /**
* Returns all options passed during the construction of the form. * Returns all options passed during the construction of the form.
* *

View File

@ -633,4 +633,16 @@ class FormTypeTest extends TypeTestCase
$view = $form->createView(); $view = $form->createView();
$this->assertFalse($view->getVar('valid')); $this->assertFalse($view->getVar('valid'));
} }
public function testDataOptionSupersedesSetDataCalls()
{
$form = $this->factory->create('form', null, array(
'data' => 'default',
'compound' => false,
));
$form->setData('foobar');
$this->assertSame('default', $form->getData());
}
} }

View File

@ -23,10 +23,10 @@ use Symfony\Component\Form\Tests\Fixtures\FixedFilterListener;
class SimpleFormTest extends AbstractFormTest class SimpleFormTest extends AbstractFormTest
{ {
public function testDataIsInitializedEmpty() public function testDataIsInitializedToConfiguredValue()
{ {
$model = new FixedDataTransformer(array( $model = new FixedDataTransformer(array(
'' => 'foo', 'default' => 'foo',
)); ));
$view = new FixedDataTransformer(array( $view = new FixedDataTransformer(array(
'foo' => 'bar', 'foo' => 'bar',
@ -35,9 +35,10 @@ class SimpleFormTest extends AbstractFormTest
$config = new FormConfig('name', null, $this->dispatcher); $config = new FormConfig('name', null, $this->dispatcher);
$config->addViewTransformer($view); $config->addViewTransformer($view);
$config->addModelTransformer($model); $config->addModelTransformer($model);
$config->setData('default');
$form = new Form($config); $form = new Form($config);
$this->assertNull($form->getData()); $this->assertSame('default', $form->getData());
$this->assertSame('foo', $form->getNormData()); $this->assertSame('foo', $form->getNormData());
$this->assertSame('bar', $form->getViewData()); $this->assertSame('bar', $form->getViewData());
} }
@ -376,6 +377,18 @@ class SimpleFormTest extends AbstractFormTest
$this->assertSame('', $form->getViewData()); $this->assertSame('', $form->getViewData());
} }
public function testSetDataIsIgnoredIfDataIsLocked()
{
$form = $this->getBuilder()
->setData('default')
->setDataLocked(true)
->getForm();
$form->setData('foobar');
$this->assertSame('default', $form->getData());
}
public function testBindConvertsEmptyToNullIfNoTransformer() public function testBindConvertsEmptyToNullIfNoTransformer()
{ {
$form = $this->getBuilder()->getForm(); $form = $this->getBuilder()->getForm();

View File

@ -116,6 +116,11 @@ class UnmodifiableFormConfig implements FormConfigInterface
*/ */
private $dataClass; private $dataClass;
/**
* @var Boolean
*/
private $dataLocked;
/** /**
* @var array * @var array
*/ */
@ -152,6 +157,7 @@ class UnmodifiableFormConfig implements FormConfigInterface
$this->attributes = $config->getAttributes(); $this->attributes = $config->getAttributes();
$this->data = $config->getData(); $this->data = $config->getData();
$this->dataClass = $config->getDataClass(); $this->dataClass = $config->getDataClass();
$this->dataLocked = $config->getDataLocked();
$this->options = $config->getOptions(); $this->options = $config->getOptions();
} }
@ -325,6 +331,14 @@ class UnmodifiableFormConfig implements FormConfigInterface
return $this->dataClass; return $this->dataClass;
} }
/**
* {@inheritdoc}
*/
public function getDataLocked()
{
return $this->dataLocked;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */