Skip to content

Commit

Permalink
[Form] Added accessor FormConfigInterface::getByReference() and let F…
Browse files Browse the repository at this point in the history
…orm clone objects if not by reference
  • Loading branch information
webmozart committed May 23, 2012
1 parent fc23701 commit bad6d04
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 25 deletions.
Expand Up @@ -37,13 +37,7 @@ public function mapDataToForms($data, array $forms)
$config = $form->getConfig();

if (null !== $propertyPath && $config->getMapped()) {
$propertyData = $propertyPath->getValue($data);

if (is_object($propertyData) && !$form->getAttribute('by_reference')) {
$propertyData = clone $propertyData;
}

$form->setData($propertyData);
$form->setData($propertyPath->getValue($data));
}
}
}
Expand All @@ -68,9 +62,8 @@ public function mapFormsToData(array $forms, &$data)
// If the data is identical to the value in $data, we are
// dealing with a reference
$isReference = $form->getData() === $propertyPath->getValue($data);
$byReference = $form->getAttribute('by_reference');

if (!(is_object($data) && $isReference && $byReference)) {
if (!is_object($data) || !$isReference || !$config->getByReference()) {
$propertyPath->setValue($data, $form->getData());
}
}
Expand Down
Expand Up @@ -46,9 +46,9 @@ public function buildForm(FormBuilder $builder, array $options)
// BC compatibility, when "property_path" could be false
->setPropertyPath(is_string($options['property_path']) ? $options['property_path'] : null)
->setMapped($options['mapped'])
->setByReference($options['by_reference'])
->setVirtual($options['virtual'])
->setAttribute('read_only', $options['read_only'])
->setAttribute('by_reference', $options['by_reference'])
->setAttribute('max_length', $options['max_length'])
->setAttribute('pattern', $options['pattern'])
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
Expand Down
4 changes: 4 additions & 0 deletions src/Symfony/Component/Form/Form.php
Expand Up @@ -319,6 +319,10 @@ public function setData($appData)
throw new AlreadyBoundException('You cannot change the data of a bound form');
}

if (is_object($appData) && !$this->config->getByReference()) {
$appData = clone $appData;
}

$event = new DataEvent($this, $appData);
$this->config->getEventDispatcher()->dispatch(FormEvents::PRE_SET_DATA, $event);

Expand Down
28 changes: 28 additions & 0 deletions src/Symfony/Component/Form/FormConfig.php
Expand Up @@ -43,6 +43,11 @@ class FormConfig implements FormConfigInterface
*/
private $mapped = true;

/**
* @var Boolean
*/
private $byReference = true;

/**
* @var Boolean
*/
Expand Down Expand Up @@ -294,6 +299,14 @@ public function getMapped()
return $this->mapped;
}

/**
* {@inheritdoc}
*/
public function getByReference()
{
return $this->byReference;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -550,6 +563,21 @@ public function setMapped($mapped)
return $this;
}

/**
* Sets whether the form's data should be modified by reference.
*
* @param Boolean $byReference Whether the data should be
modified by reference.
*
* @return self The configuration object.
*/
public function setByReference($byReference)
{
$this->byReference = $byReference;

return $this;
}

/**
* Sets whether the form should be virtual.
*
Expand Down
7 changes: 7 additions & 0 deletions src/Symfony/Component/Form/FormConfigInterface.php
Expand Up @@ -47,6 +47,13 @@ function getPropertyPath();
*/
function getMapped();

/**
* Returns whether the form's data should be modified by reference.
*
* @return Boolean Whether to modify the form's data by reference.
*/
function getByReference();

/**
* Returns whether the form should be virtual.
*
Expand Down
Expand Up @@ -110,7 +110,7 @@ public function testMapDataToFormsPassesObjectRefIfByReference()
->will($this->returnValue($engine));

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);

Expand All @@ -133,7 +133,7 @@ public function testMapDataToFormsPassesObjectCloneIfNotByReference()
->will($this->returnValue($engine));

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', false);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);

Expand All @@ -148,7 +148,7 @@ public function testMapDataToFormsIgnoresEmptyPropertyPath()
$car = new \stdClass();

$config = new FormConfig(null, '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$form = $this->getForm($config);

$this->assertNull($form->getPropertyPath());
Expand All @@ -167,7 +167,7 @@ public function testMapDataToFormsIgnoresUnmapped()
->method('getValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setMapped(false);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);
Expand All @@ -185,7 +185,7 @@ public function testMapDataToFormsIgnoresEmptyData()
->method('getValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$form = $this->getForm($config);

Expand All @@ -206,12 +206,12 @@ public function testMapDataToFormsSkipsVirtualForms()
->will($this->returnValue($engine));

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setVirtual(true);
$form = $this->getForm($config);

$config = new FormConfig('engine', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$child = $this->getForm($config);

Expand All @@ -234,7 +234,7 @@ public function testMapFormsToDataWritesBackIfNotByReference()
->with($car, $engine);

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', false);
$config->setByReference(false);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$form = $this->getForm($config);
Expand All @@ -253,7 +253,7 @@ public function testMapFormsToDataWritesBackIfByReferenceButNoReference()
->with($car, $engine);

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$form = $this->getForm($config);
Expand All @@ -277,7 +277,7 @@ public function testMapFormsToDataWritesBackIfByReferenceAndReference()
->method('setValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$form = $this->getForm($config);
Expand All @@ -295,7 +295,7 @@ public function testMapFormsToDataIgnoresUnmapped()
->method('setValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$config->setMapped(false);
Expand All @@ -313,7 +313,7 @@ public function testMapFormsToDataIgnoresEmptyData()
->method('setValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData(null);
$form = $this->getForm($config);
Expand All @@ -331,7 +331,7 @@ public function testMapFormsToDataIgnoresUnsynchronized()
->method('setValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$form = $this->getForm($config, false);
Expand All @@ -349,7 +349,7 @@ public function testMapFormsToDataIgnoresDisabled()
->method('setValue');

$config = new FormConfig('name', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($propertyPath);
$config->setData($engine);
$config->setDisabled(true);
Expand Down Expand Up @@ -380,7 +380,7 @@ public function testMapFormsToDataSkipsVirtualForms()
$form = $this->getForm($config);

$config = new FormConfig('engine', '\stdClass', $this->dispatcher);
$config->setAttribute('by_reference', true);
$config->setByReference(true);
$config->setPropertyPath($childPath);
$config->setData($engine);
$child = $this->getForm($config);
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Component/Form/Tests/FormTest.php
Expand Up @@ -464,6 +464,25 @@ public function testSetDataThrowsExceptionIfAlreadyBound()
$this->form->setData(null);
}

public function testSetDataClonesObjectIfNotByReference()
{
$data = new \stdClass();
$form = $this->getBuilder('name', null, '\stdClass')->setByReference(false)->getForm();
$form->setData($data);

$this->assertNotSame($data, $form->getData());
$this->assertEquals($data, $form->getData());
}

public function testSetDataDoesNotCloneObjectIfByReference()
{
$data = new \stdClass();
$form = $this->getBuilder('name', null, '\stdClass')->setByReference(true)->getForm();
$form->setData($data);

$this->assertSame($data, $form->getData());
}

public function testSetDataExecutesTransformationChain()
{
// use real event dispatcher now
Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Component/Form/UnmodifiableFormConfig.php
Expand Up @@ -42,6 +42,11 @@ class UnmodifiableFormConfig implements FormConfigInterface
*/
private $mapped;

/**
* @var Boolean
*/
private $byReference;

/**
* @var Boolean
*/
Expand Down Expand Up @@ -123,6 +128,7 @@ public function __construct(FormConfigInterface $config)
$this->name = $config->getName();
$this->propertyPath = $config->getPropertyPath();
$this->mapped = $config->getMapped();
$this->byReference = $config->getByReference();
$this->virtual = $config->getVirtual();
$this->types = $config->getTypes();
$this->clientTransformers = $config->getClientTransformers();
Expand Down Expand Up @@ -170,6 +176,14 @@ public function getMapped()
return $this->mapped;
}

/**
* {@inheritdoc}
*/
public function getByReference()
{
return $this->byReference;
}

/**
* {@inheritdoc}
*/
Expand Down

0 comments on commit bad6d04

Please sign in to comment.