Skip to content

[RFC] [Form] Add Partial Bind Support #5576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
3 changes: 3 additions & 0 deletions src/Symfony/Component/Form/Extension/Core/Type/FormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->setByReference($options['by_reference'])
->setVirtual($options['virtual'])
->setCompound($options['compound'])
->setIgnoreMissing($options['ignore_missing'])
->setData(isset($options['data']) ? $options['data'] : null)
->setDataLocked(isset($options['data']))
->setDataMapper($options['compound'] ? new PropertyPathMapper() : null)
Expand Down Expand Up @@ -125,6 +126,7 @@ public function buildView(FormView $view, FormInterface $form, array $options)
'attr' => $options['attr'],
'label_attr' => $options['label_attr'],
'compound' => $form->getConfig()->getCompound(),
'ignore_missing' => $form->getConfig()->getIgnoreMissing(),
'block_prefixes' => $blockPrefixes,
'unique_block_prefix' => $uniqueBlockPrefix,
'translation_domain' => $translationDomain,
Expand Down Expand Up @@ -216,6 +218,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
'label_attr' => array(),
'virtual' => false,
'compound' => true,
'ignore_missing' => null,
'translation_domain' => null,
));

Expand Down
11 changes: 11 additions & 0 deletions src/Symfony/Component/Form/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,15 @@ public function getName()
return $this->config->getName();
}

public function getIgnoreMissing()
{
if ($this->parent && null === $this->config->getIgnoreMissing()) {
return $this->parent->getIgnoreMissing();
}

return $this->config->getIgnoreMissing();
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -509,6 +518,8 @@ public function bind($submittedData)
// and radio buttons with empty values.
if (is_scalar($submittedData)) {
$submittedData = (string) $submittedData;
} elseif ($this->getIgnoreMissing() && null === $submittedData) {
$submittedData = $this->getViewData();
}

// Initialize errors in the very beginning so that we don't lose any
Expand Down
27 changes: 27 additions & 0 deletions src/Symfony/Component/Form/FormConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ class FormConfigBuilder implements FormConfigBuilderInterface
*/
private $compound = false;

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

/**
* @var ResolvedFormTypeInterface
*/
Expand Down Expand Up @@ -445,6 +450,14 @@ public function getCompound()
return $this->compound;
}

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

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -787,6 +800,20 @@ public function setCompound($compound)
return $this;
}

/**
* {@inheritdoc}
*/
public function setIgnoreMissing($ignoreMissing)
{
if ($this->locked) {
throw new FormException('The config builder cannot be modified anymore.');
}

$this->ignoreMissing = $ignoreMissing;

return $this;
}

/**
* {@inheritdoc}
*/
Expand Down
11 changes: 11 additions & 0 deletions src/Symfony/Component/Form/FormConfigBuilderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,17 @@ public function setVirtual($virtual);
*/
public function setCompound($compound);

/**
* Sets whether the form should support partial binding.
*
* @param Boolean $ignoreMissing Whether the form should support partial binding.
*
* @return self The configuration object.
*
* @see FormConfigInterface::getPartial()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong method name

*/
public function setIgnoreMissing($ignoreMissing);

/**
* Set the types.
*
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Form/FormConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ public function getVirtual();
*/
public function getCompound();

/**
* Returns whether the form explicitly supports missing children
*
* When enabled, missing child data uses pre-existing model data
* rather than defaulting to `null`.
*
* @return null|Boolean Whether the form explicitly supports missing children
*/
public function getIgnoreMissing();

/**
* Returns the form types used to construct the form.
*
Expand Down
57 changes: 57 additions & 0 deletions src/Symfony/Component/Form/Tests/SimpleFormTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,63 @@ public function testBindIsIgnoredIfDisabled()
$this->assertTrue($form->isBound());
}

public function testBindUsesViewDataIfNullAndIgnoreMissing()
{
$form = $this->getBuilder('name', new EventDispatcher())
->setIgnoreMissing(true)
->addViewTransformer(new FixedDataTransformer(array(
'' => '',
'norm' => 'client',
)))
->addModelTransformer(new FixedDataTransformer(array(
'' => '',
'app' => 'norm',
)))
->getForm()
;

$form->setData('app');

$form->bind(null);

$this->assertEquals('app', $form->getData());
$this->assertEquals('norm', $form->getNormData());
$this->assertEquals('client', $form->getClientData());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getClientData is deprecated in favor of getViewData

}

public function testIgnoreMissingUsesParentSettingByDefault()
{
$parent = $this->getBuilder()->setIgnoreMissing(true)->getForm();
$child = $this->getBuilder()->getForm();

$child->setParent($parent);

$this->assertTrue($child->getIgnoreMissing());
}

public function testIgnoreMissingDefaultsToNull()
{
$form = $this->getBuilder()->getForm();
$this->assertNull($form->getIgnoreMissing());
}

public function testIgnoreMissingUsesSetValue()
{
$parent = $this->getBuilder()->setIgnoreMissing(false)->getForm();
$child = $this->getBuilder()->setIgnoreMissing(true)->getForm();

$child->setParent($parent);

$this->assertTrue($child->getIgnoreMissing());

$parent = $this->getBuilder()->setIgnoreMissing(true)->getForm();
$child = $this->getBuilder()->setIgnoreMissing(false)->getForm();

$child->setParent($parent);

$this->assertFalse($child->getIgnoreMissing());
}

public function testNeverRequiredIfParentNotRequired()
{
$parent = $this->getBuilder()->setRequired(false)->getForm();
Expand Down