[Form] Moved parent data inheritance from data mappers to Form
This commit is contained in:
parent
8ea5e1a678
commit
ac2ca44b5a
|
@ -10,6 +10,9 @@ CHANGELOG
|
||||||
* deprecated passing a Request instance to FormInterface::bind()
|
* deprecated passing a Request instance to FormInterface::bind()
|
||||||
* added options "method" and "action" to FormType
|
* added options "method" and "action" to FormType
|
||||||
* deprecated option "virtual", renamed it to "inherit_data"
|
* deprecated option "virtual", renamed it to "inherit_data"
|
||||||
|
* deprecated VirtualFormAwareIterator in favor of InheritDataAwareIterator
|
||||||
|
* [BC BREAK] removed the "array" type hint from DataMapperInterface
|
||||||
|
* improved forms inheriting their parent data to actually return that data from getData(), getNormData() and getViewData()
|
||||||
|
|
||||||
2.2.0
|
2.2.0
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -19,20 +19,20 @@ interface DataMapperInterface
|
||||||
/**
|
/**
|
||||||
* Maps properties of some data to a list of forms.
|
* Maps properties of some data to a list of forms.
|
||||||
*
|
*
|
||||||
* @param mixed $data Structured data.
|
* @param mixed $data Structured data.
|
||||||
* @param array $forms A list of {@link FormInterface} instances.
|
* @param FormInterface[] $forms A list of {@link FormInterface} instances.
|
||||||
*
|
*
|
||||||
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
|
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
|
||||||
*/
|
*/
|
||||||
public function mapDataToForms($data, array $forms);
|
public function mapDataToForms($data, $forms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps the data of a list of forms into the properties of some data.
|
* Maps the data of a list of forms into the properties of some data.
|
||||||
*
|
*
|
||||||
* @param array $forms A list of {@link FormInterface} instances.
|
* @param FormInterface[] $forms A list of {@link FormInterface} instances.
|
||||||
* @param mixed $data Structured data.
|
* @param mixed $data Structured data.
|
||||||
*
|
*
|
||||||
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
|
* @throws Exception\UnexpectedTypeException if the type of the data parameter is not supported.
|
||||||
*/
|
*/
|
||||||
public function mapFormsToData(array $forms, &$data);
|
public function mapFormsToData($forms, &$data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base RuntimeException for the Form component.
|
||||||
|
*
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class RuntimeException extends \RuntimeException implements ExceptionInterface
|
||||||
|
{
|
||||||
|
}
|
|
@ -12,7 +12,6 @@
|
||||||
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
namespace Symfony\Component\Form\Extension\Core\DataMapper;
|
||||||
|
|
||||||
use Symfony\Component\Form\DataMapperInterface;
|
use Symfony\Component\Form\DataMapperInterface;
|
||||||
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
|
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
@ -42,7 +41,7 @@ class PropertyPathMapper implements DataMapperInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function mapDataToForms($data, array $forms)
|
public function mapDataToForms($data, $forms)
|
||||||
{
|
{
|
||||||
if (null === $data || array() === $data) {
|
if (null === $data || array() === $data) {
|
||||||
return;
|
return;
|
||||||
|
@ -52,11 +51,7 @@ class PropertyPathMapper implements DataMapperInterface
|
||||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
$iterator = new VirtualFormAwareIterator($forms);
|
foreach ($forms as $form) {
|
||||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
|
||||||
|
|
||||||
foreach ($iterator as $form) {
|
|
||||||
/* @var \Symfony\Component\Form\FormInterface $form */
|
|
||||||
$propertyPath = $form->getPropertyPath();
|
$propertyPath = $form->getPropertyPath();
|
||||||
$config = $form->getConfig();
|
$config = $form->getConfig();
|
||||||
|
|
||||||
|
@ -69,7 +64,7 @@ class PropertyPathMapper implements DataMapperInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function mapFormsToData(array $forms, &$data)
|
public function mapFormsToData($forms, &$data)
|
||||||
{
|
{
|
||||||
if (null === $data) {
|
if (null === $data) {
|
||||||
return;
|
return;
|
||||||
|
@ -79,11 +74,7 @@ class PropertyPathMapper implements DataMapperInterface
|
||||||
throw new UnexpectedTypeException($data, 'object, array or empty');
|
throw new UnexpectedTypeException($data, 'object, array or empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
$iterator = new VirtualFormAwareIterator($forms);
|
foreach ($forms as $form) {
|
||||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
|
||||||
|
|
||||||
foreach ($iterator as $form) {
|
|
||||||
/* @var \Symfony\Component\Form\FormInterface $form */
|
|
||||||
$propertyPath = $form->getPropertyPath();
|
$propertyPath = $form->getPropertyPath();
|
||||||
$config = $form->getConfig();
|
$config = $form->getConfig();
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
|
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
|
||||||
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
|
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPathIterator;
|
use Symfony\Component\PropertyAccess\PropertyPathIterator;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPathBuilder;
|
use Symfony\Component\PropertyAccess\PropertyPathBuilder;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
|
use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
|
||||||
|
@ -100,6 +100,9 @@ class ViolationMapper implements ViolationMapperInterface
|
||||||
$scope = $form;
|
$scope = $form;
|
||||||
$it = new ViolationPathIterator($violationPath);
|
$it = new ViolationPathIterator($violationPath);
|
||||||
|
|
||||||
|
// Note: acceptsErrors() will always return true for forms inheriting
|
||||||
|
// their parent data, because these forms can never be non-synchronized
|
||||||
|
// (they don't do any data transformation on their own)
|
||||||
while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) {
|
while ($this->acceptsErrors($scope) && $it->valid() && $it->mapsForm()) {
|
||||||
if (!$scope->has($it->current())) {
|
if (!$scope->has($it->current())) {
|
||||||
// Break if we find a reference to a non-existing child
|
// Break if we find a reference to a non-existing child
|
||||||
|
@ -164,7 +167,7 @@ class ViolationMapper implements ViolationMapperInterface
|
||||||
|
|
||||||
// Skip forms inheriting their parent data when iterating the children
|
// Skip forms inheriting their parent data when iterating the children
|
||||||
$childIterator = new \RecursiveIteratorIterator(
|
$childIterator = new \RecursiveIteratorIterator(
|
||||||
new VirtualFormAwareIterator($form->all())
|
new InheritDataAwareIterator($form->all())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make the path longer until we find a matching child
|
// Make the path longer until we find a matching child
|
||||||
|
|
|
@ -11,11 +11,14 @@
|
||||||
|
|
||||||
namespace Symfony\Component\Form;
|
namespace Symfony\Component\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\Exception\BadMethodCallException;
|
||||||
use Symfony\Component\Form\Exception\Exception;
|
use Symfony\Component\Form\Exception\Exception;
|
||||||
|
use Symfony\Component\Form\Exception\RuntimeException;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Form\Exception\AlreadyBoundException;
|
use Symfony\Component\Form\Exception\AlreadyBoundException;
|
||||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
use Symfony\Component\Form\Util\FormUtil;
|
use Symfony\Component\Form\Util\FormUtil;
|
||||||
|
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +133,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
*
|
*
|
||||||
* @var Boolean
|
* @var Boolean
|
||||||
*/
|
*/
|
||||||
private $initialized = false;
|
private $defaultDataSet = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether setData() is currently being called.
|
* Whether setData() is currently being called.
|
||||||
|
@ -143,7 +146,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
*
|
*
|
||||||
* @param FormConfigInterface $config The form configuration.
|
* @param FormConfigInterface $config The form configuration.
|
||||||
*
|
*
|
||||||
* @throws FormException if a data mapper is not provided for a compound form
|
* @throws Exception if a data mapper is not provided for a compound form
|
||||||
*/
|
*/
|
||||||
public function __construct(FormConfigInterface $config)
|
public function __construct(FormConfigInterface $config)
|
||||||
{
|
{
|
||||||
|
@ -154,6 +157,12 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
throw new Exception('Compound forms need a data mapper');
|
throw new Exception('Compound forms need a data mapper');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the form inherits the data from its parent, it is not necessary
|
||||||
|
// to call setData() with the default data.
|
||||||
|
if ($config->getInheritData()) {
|
||||||
|
$this->defaultDataSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +202,13 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->parent && null === $this->parent->getConfig()->getDataClass()) {
|
$parent = $this->parent;
|
||||||
|
|
||||||
|
while ($parent && $parent->getConfig()->getInheritData()) {
|
||||||
|
$parent = $parent->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parent && null === $parent->getConfig()->getDataClass()) {
|
||||||
return new PropertyPath('['.$this->getName().']');
|
return new PropertyPath('['.$this->getName().']');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,8 +289,14 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
// If the form is bound while disabled, it is set to bound, but the data is not
|
// If the form is bound while disabled, it is set to bound, but the data is not
|
||||||
// changed. In such cases (i.e. when the form is not initialized yet) don't
|
// changed. In such cases (i.e. when the form is not initialized yet) don't
|
||||||
// abort this method.
|
// abort this method.
|
||||||
if ($this->bound && $this->initialized) {
|
if ($this->bound && $this->defaultDataSet) {
|
||||||
throw new AlreadyBoundException('You cannot change the data of a bound form');
|
throw new AlreadyBoundException('You cannot change the data of a bound form.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the form inherits its parent's data, disallow data setting to
|
||||||
|
// prevent merge conflicts
|
||||||
|
if ($this->config->getInheritData()) {
|
||||||
|
throw new RuntimeException('You cannot change the data of a form inheriting its parent data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't allow modifications of the configured data if the data is locked
|
// Don't allow modifications of the configured data if the data is locked
|
||||||
|
@ -288,7 +309,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->lockSetData) {
|
if ($this->lockSetData) {
|
||||||
throw new Exception('A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead.');
|
throw new RuntimeException('A cycle was detected. Listeners to the PRE_SET_DATA event must not call setData(). You should call setData() on the FormEvent object instead.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->lockSetData = true;
|
$this->lockSetData = true;
|
||||||
|
@ -342,14 +363,16 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
$this->modelData = $modelData;
|
$this->modelData = $modelData;
|
||||||
$this->normData = $normData;
|
$this->normData = $normData;
|
||||||
$this->viewData = $viewData;
|
$this->viewData = $viewData;
|
||||||
$this->initialized = true;
|
$this->defaultDataSet = true;
|
||||||
$this->lockSetData = false;
|
$this->lockSetData = false;
|
||||||
|
|
||||||
// It is not necessary to invoke this method if the form doesn't have children,
|
// It is not necessary to invoke this method if the form doesn't have children,
|
||||||
// even if the form is compound.
|
// even if the form is compound.
|
||||||
if (count($this->children) > 0) {
|
if (count($this->children) > 0) {
|
||||||
// Update child forms from the data
|
// Update child forms from the data
|
||||||
$this->config->getDataMapper()->mapDataToForms($viewData, $this->children);
|
$childrenIterator = new InheritDataAwareIterator($this->children);
|
||||||
|
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||||
|
$this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
|
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
|
||||||
|
@ -365,7 +388,15 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
if (!$this->initialized) {
|
if ($this->config->getInheritData()) {
|
||||||
|
if (!$this->parent) {
|
||||||
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parent->getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->defaultDataSet) {
|
||||||
$this->setData($this->config->getData());
|
$this->setData($this->config->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +408,15 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
*/
|
*/
|
||||||
public function getNormData()
|
public function getNormData()
|
||||||
{
|
{
|
||||||
if (!$this->initialized) {
|
if ($this->config->getInheritData()) {
|
||||||
|
if (!$this->parent) {
|
||||||
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parent->getNormData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->defaultDataSet) {
|
||||||
$this->setData($this->config->getData());
|
$this->setData($this->config->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +428,15 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
*/
|
*/
|
||||||
public function getViewData()
|
public function getViewData()
|
||||||
{
|
{
|
||||||
if (!$this->initialized) {
|
if ($this->config->getInheritData()) {
|
||||||
|
if (!$this->parent) {
|
||||||
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->parent->getViewData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->defaultDataSet) {
|
||||||
$this->setData($this->config->getData());
|
$this->setData($this->config->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,6 +470,11 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
throw new AlreadyBoundException('A form can only be bound once');
|
throw new AlreadyBoundException('A form can only be bound once');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize errors in the very beginning so that we don't lose any
|
||||||
|
// errors added during listeners
|
||||||
|
$this->errors = array();
|
||||||
|
|
||||||
|
// Obviously, a disabled form should not change its data upon binding.
|
||||||
if ($this->isDisabled()) {
|
if ($this->isDisabled()) {
|
||||||
$this->bound = true;
|
$this->bound = true;
|
||||||
|
|
||||||
|
@ -432,7 +484,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
// The data must be initialized if it was not initialized yet.
|
// The data must be initialized if it was not initialized yet.
|
||||||
// This is necessary to guarantee that the *_SET_DATA listeners
|
// This is necessary to guarantee that the *_SET_DATA listeners
|
||||||
// are always invoked before bind() takes place.
|
// are always invoked before bind() takes place.
|
||||||
if (!$this->initialized) {
|
if (!$this->defaultDataSet) {
|
||||||
$this->setData($this->config->getData());
|
$this->setData($this->config->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,10 +496,6 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
$submittedData = (string) $submittedData;
|
$submittedData = (string) $submittedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize errors in the very beginning so that we don't lose any
|
|
||||||
// errors added during listeners
|
|
||||||
$this->errors = array();
|
|
||||||
|
|
||||||
$dispatcher = $this->config->getEventDispatcher();
|
$dispatcher = $this->config->getEventDispatcher();
|
||||||
|
|
||||||
// Hook to change content of the data bound by the browser
|
// Hook to change content of the data bound by the browser
|
||||||
|
@ -472,63 +520,83 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->extraData = $submittedData;
|
$this->extraData = $submittedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forms that inherit their parents' data also are not processed,
|
||||||
|
// because then it would be too difficult to merge the changes in
|
||||||
|
// the child and the parent form. Instead, the parent form also takes
|
||||||
|
// changes in the grandchildren (i.e. children of the form that inherits
|
||||||
|
// its parent's data) into account.
|
||||||
|
// (see InheritDataAwareIterator below)
|
||||||
|
if ($this->config->getInheritData()) {
|
||||||
|
$this->bound = true;
|
||||||
|
|
||||||
|
// When POST_BIND is reached, the data is not yet updated, so pass
|
||||||
|
// NULL to prevent hard-to-debug bugs.
|
||||||
|
$dataForPostBind = null;
|
||||||
|
} else {
|
||||||
// If the form is compound, the default data in view format
|
// If the form is compound, the default data in view format
|
||||||
// is reused. The data of the children is merged into this
|
// is reused. The data of the children is merged into this
|
||||||
// default data using the data mapper.
|
// default data using the data mapper.
|
||||||
$viewData = $this->viewData;
|
|
||||||
} else {
|
|
||||||
// If the form is not compound, the submitted data is also the data in view format.
|
// If the form is not compound, the submitted data is also the data in view format.
|
||||||
$viewData = $submittedData;
|
$viewData = $this->config->getCompound() ? $this->viewData : $submittedData;
|
||||||
}
|
|
||||||
|
|
||||||
if (FormUtil::isEmpty($viewData)) {
|
if (FormUtil::isEmpty($viewData)) {
|
||||||
$emptyData = $this->config->getEmptyData();
|
$emptyData = $this->config->getEmptyData();
|
||||||
|
|
||||||
if ($emptyData instanceof \Closure) {
|
if ($emptyData instanceof \Closure) {
|
||||||
/* @var \Closure $emptyData */
|
/* @var \Closure $emptyData */
|
||||||
$emptyData = $emptyData($this, $viewData);
|
$emptyData = $emptyData($this, $viewData);
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewData = $emptyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
$viewData = $emptyData;
|
// Merge form data from children into existing view data
|
||||||
}
|
// It is not necessary to invoke this method if the form has no children,
|
||||||
|
// even if it is compound.
|
||||||
// Merge form data from children into existing view data
|
if (count($this->children) > 0) {
|
||||||
// It is not necessary to invoke this method if the form has no children,
|
// Use InheritDataAwareIterator to process children of
|
||||||
// even if it is compound.
|
// descendants that inherit this form's data.
|
||||||
if (count($this->children) > 0) {
|
// These descendants will not be bound normally (see the check
|
||||||
$this->config->getDataMapper()->mapFormsToData($this->children, $viewData);
|
// for $this->config->getInheritData() above)
|
||||||
}
|
$childrenIterator = new InheritDataAwareIterator($this->children);
|
||||||
|
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||||
$modelData = null;
|
$this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData);
|
||||||
$normData = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Normalize data to unified representation
|
|
||||||
$normData = $this->viewToNorm($viewData);
|
|
||||||
|
|
||||||
// Hook to change content of the data into the normalized
|
|
||||||
// representation
|
|
||||||
if ($dispatcher->hasListeners(FormEvents::BIND)) {
|
|
||||||
$event = new FormEvent($this, $normData);
|
|
||||||
$dispatcher->dispatch(FormEvents::BIND, $event);
|
|
||||||
$normData = $event->getData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize representations - must not change the content!
|
$modelData = null;
|
||||||
$modelData = $this->normToModel($normData);
|
$normData = null;
|
||||||
$viewData = $this->normToView($normData);
|
|
||||||
} catch (TransformationFailedException $e) {
|
|
||||||
$this->synchronized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->bound = true;
|
try {
|
||||||
$this->modelData = $modelData;
|
// Normalize data to unified representation
|
||||||
$this->normData = $normData;
|
$normData = $this->viewToNorm($viewData);
|
||||||
$this->viewData = $viewData;
|
|
||||||
|
// Hook to change content of the data into the normalized
|
||||||
|
// representation
|
||||||
|
if ($dispatcher->hasListeners(FormEvents::BIND)) {
|
||||||
|
$event = new FormEvent($this, $normData);
|
||||||
|
$dispatcher->dispatch(FormEvents::BIND, $event);
|
||||||
|
$normData = $event->getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronize representations - must not change the content!
|
||||||
|
$modelData = $this->normToModel($normData);
|
||||||
|
$viewData = $this->normToView($normData);
|
||||||
|
} catch (TransformationFailedException $e) {
|
||||||
|
$this->synchronized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->bound = true;
|
||||||
|
$this->modelData = $modelData;
|
||||||
|
$this->normData = $normData;
|
||||||
|
$this->viewData = $viewData;
|
||||||
|
|
||||||
|
$dataForPostBind = $viewData;
|
||||||
|
}
|
||||||
|
|
||||||
if ($dispatcher->hasListeners(FormEvents::POST_BIND)) {
|
if ($dispatcher->hasListeners(FormEvents::POST_BIND)) {
|
||||||
$event = new FormEvent($this, $viewData);
|
$event = new FormEvent($this, $dataForPostBind);
|
||||||
$dispatcher->dispatch(FormEvents::POST_BIND, $event);
|
$dispatcher->dispatch(FormEvents::POST_BIND, $event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -679,7 +747,7 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
// * getViewData() is called
|
// * getViewData() is called
|
||||||
// * setData() is called since the form is not initialized yet
|
// * setData() is called since the form is not initialized yet
|
||||||
// * ... endless recursion ...
|
// * ... endless recursion ...
|
||||||
if (!$this->lockSetData) {
|
if (!$this->lockSetData && !$this->config->getInheritData()) {
|
||||||
$viewData = $this->getViewData();
|
$viewData = $this->getViewData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,8 +771,10 @@ class Form implements \IteratorAggregate, FormInterface
|
||||||
|
|
||||||
$child->setParent($this);
|
$child->setParent($this);
|
||||||
|
|
||||||
if (!$this->lockSetData) {
|
if (!$this->lockSetData && !$this->config->getInheritData()) {
|
||||||
$this->config->getDataMapper()->mapDataToForms($viewData, array($child));
|
$childrenIterator = new InheritDataAwareIterator(array($child));
|
||||||
|
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
||||||
|
$this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
@ -264,6 +264,7 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
|
|
||||||
public function testAddMapsViewDataToForm()
|
public function testAddMapsViewDataToForm()
|
||||||
{
|
{
|
||||||
|
$test = $this;
|
||||||
$mapper = $this->getDataMapper();
|
$mapper = $this->getDataMapper();
|
||||||
$form = $this->getBuilder()
|
$form = $this->getBuilder()
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
|
@ -278,13 +279,34 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
$child = $this->getBuilder()->getForm();
|
$child = $this->getBuilder()->getForm();
|
||||||
$mapper->expects($this->once())
|
$mapper->expects($this->once())
|
||||||
->method('mapDataToForms')
|
->method('mapDataToForms')
|
||||||
->with('bar', array($child));
|
->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator'))
|
||||||
|
->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child, $test) {
|
||||||
|
$test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator());
|
||||||
|
$test->assertSame(array($child), iterator_to_array($iterator));
|
||||||
|
}));
|
||||||
|
|
||||||
|
$form->add($child);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddDoesNotMapViewDataToFormIfInheritData()
|
||||||
|
{
|
||||||
|
$mapper = $this->getDataMapper();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($mapper)
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$child = $this->getBuilder()->getForm();
|
||||||
|
$mapper->expects($this->never())
|
||||||
|
->method('mapDataToForms');
|
||||||
|
|
||||||
$form->add($child);
|
$form->add($child);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetDataMapsViewDataToChildren()
|
public function testSetDataMapsViewDataToChildren()
|
||||||
{
|
{
|
||||||
|
$test = $this;
|
||||||
$mapper = $this->getDataMapper();
|
$mapper = $this->getDataMapper();
|
||||||
$form = $this->getBuilder()
|
$form = $this->getBuilder()
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
|
@ -300,7 +322,11 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
|
|
||||||
$mapper->expects($this->once())
|
$mapper->expects($this->once())
|
||||||
->method('mapDataToForms')
|
->method('mapDataToForms')
|
||||||
->with('bar', array('firstName' => $child1, 'lastName' => $child2));
|
->with('bar', $this->isInstanceOf('\RecursiveIteratorIterator'))
|
||||||
|
->will($this->returnCallback(function ($data, \RecursiveIteratorIterator $iterator) use ($child1, $child2, $test) {
|
||||||
|
$test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator());
|
||||||
|
$test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator));
|
||||||
|
}));
|
||||||
|
|
||||||
$form->setData('foo');
|
$form->setData('foo');
|
||||||
}
|
}
|
||||||
|
@ -324,10 +350,12 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
|
|
||||||
$mapper->expects($this->once())
|
$mapper->expects($this->once())
|
||||||
->method('mapFormsToData')
|
->method('mapFormsToData')
|
||||||
->with(array('firstName' => $child1, 'lastName' => $child2), 'bar')
|
->with($this->isInstanceOf('\RecursiveIteratorIterator'), 'bar')
|
||||||
->will($this->returnCallback(function ($children, $bar) use ($test) {
|
->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child1, $child2, $test) {
|
||||||
$test->assertEquals('Bernhard', $children['firstName']->getData());
|
$test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator());
|
||||||
$test->assertEquals('Schussek', $children['lastName']->getData());
|
$test->assertSame(array('firstName' => $child1, 'lastName' => $child2), iterator_to_array($iterator));
|
||||||
|
$test->assertEquals('Bernhard', $child1->getData());
|
||||||
|
$test->assertEquals('Schussek', $child2->getData());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$form->bind(array(
|
$form->bind(array(
|
||||||
|
@ -336,6 +364,31 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMapFormsToDataIsNotInvokedIfInheritData()
|
||||||
|
{
|
||||||
|
$mapper = $this->getDataMapper();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($mapper)
|
||||||
|
->setInheritData(true)
|
||||||
|
->addViewTransformer(new FixedDataTransformer(array(
|
||||||
|
'' => '',
|
||||||
|
'foo' => 'bar',
|
||||||
|
)))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->add($child1 = $this->getBuilder('firstName')->setCompound(false)->getForm());
|
||||||
|
$form->add($child2 = $this->getBuilder('lastName')->setCompound(false)->getForm());
|
||||||
|
|
||||||
|
$mapper->expects($this->never())
|
||||||
|
->method('mapFormsToData');
|
||||||
|
|
||||||
|
$form->bind(array(
|
||||||
|
'firstName' => 'Bernhard',
|
||||||
|
'lastName' => 'Schussek',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* https://github.com/symfony/symfony/issues/4480
|
* https://github.com/symfony/symfony/issues/4480
|
||||||
*/
|
*/
|
||||||
|
@ -356,6 +409,7 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
|
|
||||||
public function testBindMapsBoundChildrenOntoEmptyData()
|
public function testBindMapsBoundChildrenOntoEmptyData()
|
||||||
{
|
{
|
||||||
|
$test = $this;
|
||||||
$mapper = $this->getDataMapper();
|
$mapper = $this->getDataMapper();
|
||||||
$object = new \stdClass();
|
$object = new \stdClass();
|
||||||
$form = $this->getBuilder()
|
$form = $this->getBuilder()
|
||||||
|
@ -369,7 +423,11 @@ class CompoundFormTest extends AbstractFormTest
|
||||||
|
|
||||||
$mapper->expects($this->once())
|
$mapper->expects($this->once())
|
||||||
->method('mapFormsToData')
|
->method('mapFormsToData')
|
||||||
->with(array('name' => $child), $object);
|
->with($this->isInstanceOf('\RecursiveIteratorIterator'), $object)
|
||||||
|
->will($this->returnCallback(function (\RecursiveIteratorIterator $iterator) use ($child, $test) {
|
||||||
|
$test->assertInstanceOf('Symfony\Component\Form\Util\InheritDataAwareIterator', $iterator->getInnerIterator());
|
||||||
|
$test->assertSame(array('name' => $child), iterator_to_array($iterator));
|
||||||
|
}));
|
||||||
|
|
||||||
$form->bind(array(
|
$form->bind(array(
|
||||||
'name' => 'Bernhard',
|
'name' => 'Bernhard',
|
||||||
|
|
|
@ -182,37 +182,6 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertNull($form->getData());
|
$this->assertNull($form->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMapDataToFormsSkipsFormsInheritingParentData()
|
|
||||||
{
|
|
||||||
$car = new \stdClass();
|
|
||||||
$engine = new \stdClass();
|
|
||||||
$propertyPath = $this->getPropertyPath('engine');
|
|
||||||
|
|
||||||
$this->propertyAccessor->expects($this->once())
|
|
||||||
->method('getValue')
|
|
||||||
->with($car, $propertyPath)
|
|
||||||
->will($this->returnValue($engine));
|
|
||||||
|
|
||||||
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
|
|
||||||
$config->setByReference(true);
|
|
||||||
$config->setInheritData(true);
|
|
||||||
$config->setCompound(true);
|
|
||||||
$config->setDataMapper($this->getDataMapper());
|
|
||||||
$form = $this->getForm($config);
|
|
||||||
|
|
||||||
$config = new FormConfigBuilder('engine', '\stdClass', $this->dispatcher);
|
|
||||||
$config->setByReference(true);
|
|
||||||
$config->setPropertyPath($propertyPath);
|
|
||||||
$child = $this->getForm($config);
|
|
||||||
|
|
||||||
$form->add($child);
|
|
||||||
|
|
||||||
$this->mapper->mapDataToForms($car, array($form));
|
|
||||||
|
|
||||||
$this->assertNull($form->getData());
|
|
||||||
$this->assertSame($engine, $child->getData());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testMapFormsToDataWritesBackIfNotByReference()
|
public function testMapFormsToDataWritesBackIfNotByReference()
|
||||||
{
|
{
|
||||||
$car = new \stdClass();
|
$car = new \stdClass();
|
||||||
|
@ -347,38 +316,4 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
$this->mapper->mapFormsToData(array($form), $car);
|
$this->mapper->mapFormsToData(array($form), $car);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMapFormsToDataSkipsFormsInheritingParentData()
|
|
||||||
{
|
|
||||||
$car = new \stdClass();
|
|
||||||
$engine = new \stdClass();
|
|
||||||
$parentPath = $this->getPropertyPath('name');
|
|
||||||
$childPath = $this->getPropertyPath('engine');
|
|
||||||
|
|
||||||
// getValue() and setValue() must never be invoked for $parentPath
|
|
||||||
|
|
||||||
$this->propertyAccessor->expects($this->once())
|
|
||||||
->method('getValue')
|
|
||||||
->with($car, $childPath);
|
|
||||||
$this->propertyAccessor->expects($this->once())
|
|
||||||
->method('setValue')
|
|
||||||
->with($car, $childPath, $engine);
|
|
||||||
|
|
||||||
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
|
|
||||||
$config->setPropertyPath($parentPath);
|
|
||||||
$config->setInheritData(true);
|
|
||||||
$config->setCompound(true);
|
|
||||||
$config->setDataMapper($this->getDataMapper());
|
|
||||||
$form = $this->getForm($config);
|
|
||||||
|
|
||||||
$config = new FormConfigBuilder('engine', '\stdClass', $this->dispatcher);
|
|
||||||
$config->setByReference(true);
|
|
||||||
$config->setPropertyPath($childPath);
|
|
||||||
$config->setData($engine);
|
|
||||||
$child = $this->getForm($config);
|
|
||||||
|
|
||||||
$form->add($child);
|
|
||||||
|
|
||||||
$this->mapper->mapFormsToData(array($form), $car);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,28 +183,6 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
|
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAbortFormInheritingParentDataMappingIfNotSynchronized()
|
|
||||||
{
|
|
||||||
$violation = $this->getConstraintViolation('children[address].children[street].data.foo');
|
|
||||||
$parent = $this->getForm('parent');
|
|
||||||
$child = $this->getForm('address', 'address', null, array(), true, false);
|
|
||||||
// even though "street" is synchronized, it should not have any errors
|
|
||||||
// due to its parent not being synchronized
|
|
||||||
$grandChild = $this->getForm('street' , 'street', null, array(), true);
|
|
||||||
|
|
||||||
$parent->add($child);
|
|
||||||
$child->add($grandChild);
|
|
||||||
|
|
||||||
// bind to invoke the transformer and mark the form unsynchronized
|
|
||||||
$parent->bind(array());
|
|
||||||
|
|
||||||
$this->mapper->mapViolation($violation, $parent);
|
|
||||||
|
|
||||||
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
|
|
||||||
$this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one');
|
|
||||||
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAbortDotRuleMappingIfNotSynchronized()
|
public function testAbortDotRuleMappingIfNotSynchronized()
|
||||||
{
|
{
|
||||||
$violation = $this->getConstraintViolation('data.address');
|
$violation = $this->getConstraintViolation('data.address');
|
||||||
|
@ -1446,7 +1424,7 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideFormInheritingParentDataErrorTests()
|
public function provideErrorTestsForFormInheritingParentData()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
// mapping target, child name, its property path, grand child name, its property path, violation path
|
// mapping target, child name, its property path, grand child name, its property path, violation path
|
||||||
|
@ -1472,9 +1450,9 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider provideFormInheritingParentDataErrorTests
|
* @dataProvider provideErrorTestsForFormInheritingParentData
|
||||||
*/
|
*/
|
||||||
public function testFormInheritingParentDataErrorMapping($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath)
|
public function testErrorMappingForFormInheritingParentData($target, $childName, $childPath, $grandChildName, $grandChildPath, $violationPath)
|
||||||
{
|
{
|
||||||
$violation = $this->getConstraintViolation($violationPath);
|
$violation = $this->getConstraintViolation($violationPath);
|
||||||
$parent = $this->getForm('parent');
|
$parent = $this->getForm('parent');
|
||||||
|
|
|
@ -763,6 +763,42 @@ class SimpleFormTest extends AbstractFormTest
|
||||||
$this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath());
|
$this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetPropertyPathDefaultsToNameIfFirstParentWithoutInheritDataHasDataClass()
|
||||||
|
{
|
||||||
|
$grandParent = $this->getBuilder(null, null, 'stdClass')
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($this->getDataMapper())
|
||||||
|
->getForm();
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($this->getDataMapper())
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name')->getForm();
|
||||||
|
$grandParent->add($parent);
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$this->assertEquals(new PropertyPath('name'), $form->getPropertyPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPropertyPathDefaultsToIndexedNameIfDataClassOfFirstParentWithoutInheritDataIsNull()
|
||||||
|
{
|
||||||
|
$grandParent = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($this->getDataMapper())
|
||||||
|
->getForm();
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($this->getDataMapper())
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name')->getForm();
|
||||||
|
$grandParent->add($parent);
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Form\Exception\Exception
|
* @expectedException \Symfony\Component\Form\Exception\Exception
|
||||||
*/
|
*/
|
||||||
|
@ -809,7 +845,7 @@ class SimpleFormTest extends AbstractFormTest
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Form\Exception\Exception
|
* @expectedException \Symfony\Component\Form\Exception\RuntimeException
|
||||||
*/
|
*/
|
||||||
public function testSetDataCannotInvokeItself()
|
public function testSetDataCannotInvokeItself()
|
||||||
{
|
{
|
||||||
|
@ -857,6 +893,103 @@ class SimpleFormTest extends AbstractFormTest
|
||||||
$this->assertSame($form, $form->process('REQUEST'));
|
$this->assertSame($form, $form->process('REQUEST'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFormInheritsParentData()
|
||||||
|
{
|
||||||
|
$child = $this->getBuilder('child')
|
||||||
|
->setInheritData(true);
|
||||||
|
|
||||||
|
$parent = $this->getBuilder('parent')
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($this->getDataMapper())
|
||||||
|
->setData('foo')
|
||||||
|
->addModelTransformer(new FixedDataTransformer(array(
|
||||||
|
'foo' => 'norm[foo]',
|
||||||
|
)))
|
||||||
|
->addViewTransformer(new FixedDataTransformer(array(
|
||||||
|
'norm[foo]' => 'view[foo]',
|
||||||
|
)))
|
||||||
|
->add($child)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->assertSame('foo', $parent->get('child')->getData());
|
||||||
|
$this->assertSame('norm[foo]', $parent->get('child')->getNormData());
|
||||||
|
$this->assertSame('view[foo]', $parent->get('child')->getViewData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\FormException
|
||||||
|
*/
|
||||||
|
public function testInheritDataDisallowsSetData()
|
||||||
|
{
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->setData('foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\FormException
|
||||||
|
*/
|
||||||
|
public function testGetDataRequiresParentToBeSetIfInheritData()
|
||||||
|
{
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\FormException
|
||||||
|
*/
|
||||||
|
public function testGetNormDataRequiresParentToBeSetIfInheritData()
|
||||||
|
{
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->getNormData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Form\Exception\FormException
|
||||||
|
*/
|
||||||
|
public function testGetViewDataRequiresParentToBeSetIfInheritData()
|
||||||
|
{
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->getViewData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostBindDataIsNullIfInheritData()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->addEventListener(FormEvents::POST_BIND, function (FormEvent $event) use ($test) {
|
||||||
|
$test->assertNull($event->getData());
|
||||||
|
})
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->bind('foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBindIsNeverFiredIfInheritData()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->addEventListener(FormEvents::BIND, function (FormEvent $event) use ($test) {
|
||||||
|
$test->fail('The BIND event should not be fired');
|
||||||
|
})
|
||||||
|
->setInheritData(true)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->bind('foo');
|
||||||
|
}
|
||||||
|
|
||||||
protected function createForm()
|
protected function createForm()
|
||||||
{
|
{
|
||||||
return $this->getBuilder()->getForm();
|
return $this->getBuilder()->getForm();
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Form\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator that returns only forms from a form tree that do not inherit their
|
||||||
|
* parent data.
|
||||||
|
*
|
||||||
|
* If the iterator encounters a form that inherits its parent data, it enters
|
||||||
|
* the form and traverses its children as well.
|
||||||
|
*
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class InheritDataAwareIterator extends VirtualFormAwareIterator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new iterator.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Form\FormInterface[] $forms An array
|
||||||
|
*/
|
||||||
|
public function __construct(array $forms)
|
||||||
|
{
|
||||||
|
// Skip the deprecation error
|
||||||
|
\ArrayIterator::__construct($forms);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,12 +19,27 @@ namespace Symfony\Component\Form\Util;
|
||||||
* the form and traverses its children as well.
|
* the form and traverses its children as well.
|
||||||
*
|
*
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated since version 2.2, to be removed in 2.3. Use
|
||||||
|
* {@link InheritDataAwareIterator} instead.
|
||||||
*/
|
*/
|
||||||
class VirtualFormAwareIterator extends \ArrayIterator implements \RecursiveIterator
|
class VirtualFormAwareIterator extends \ArrayIterator implements \RecursiveIterator
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new iterator.
|
||||||
|
*
|
||||||
|
* @param \Symfony\Component\Form\FormInterface[] $forms An array
|
||||||
|
*/
|
||||||
|
public function __construct(array $forms)
|
||||||
|
{
|
||||||
|
trigger_error('VirtualFormAwareIterator is deprecated since version 2.2 and will be removed in 2.3. Use InheritDataAwareIterator instead.', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
parent::__construct($forms);
|
||||||
|
}
|
||||||
|
|
||||||
public function getChildren()
|
public function getChildren()
|
||||||
{
|
{
|
||||||
return new self($this->current()->all());
|
return new static($this->current()->all());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasChildren()
|
public function hasChildren()
|
||||||
|
|
Reference in New Issue