bug #30879 [Form] Php doc fixes and cs + optimizations (Jules Pietri)
This PR was merged into the 3.4 branch.
Discussion
----------
[Form] Php doc fixes and cs + optimizations
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | no
| New feature? | no <!-- don't forget to update src/**/CHANGELOG.md files -->
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | ~
| License | MIT
| Doc PR | ~
<!--
Write a short README entry for your feature/bugfix here (replace this comment block.)
This will help people understand your PR and can be used as a start of the Doc PR.
Additionally:
- Bug fixes must be submitted against the lowest branch where they apply
(lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the master branch.
-->
Some micro optimizations may target master with some profiling but I would like some reviews first. Thanks!
Commits
-------
56429a6f08
[Form] various minor fixes
This commit is contained in:
commit
967fa368b2
@ -36,7 +36,7 @@ abstract class AbstractExtension implements FormExtensionInterface
|
|||||||
/**
|
/**
|
||||||
* The type guesser provided by this extension.
|
* The type guesser provided by this extension.
|
||||||
*
|
*
|
||||||
* @var FormTypeGuesserInterface
|
* @var FormTypeGuesserInterface|null
|
||||||
*/
|
*/
|
||||||
private $typeGuesser;
|
private $typeGuesser;
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ abstract class AbstractExtension implements FormExtensionInterface
|
|||||||
/**
|
/**
|
||||||
* Registers the type guesser.
|
* Registers the type guesser.
|
||||||
*
|
*
|
||||||
* @return FormTypeGuesserInterface|null A type guesser
|
* @return FormTypeGuesserInterface|null
|
||||||
*/
|
*/
|
||||||
protected function loadTypeGuesser()
|
protected function loadTypeGuesser()
|
||||||
{
|
{
|
||||||
|
@ -133,7 +133,7 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface
|
|||||||
* resource
|
* resource
|
||||||
* @param FormView $view The form view for finding the applying
|
* @param FormView $view The form view for finding the applying
|
||||||
* themes
|
* themes
|
||||||
* @param array $blockNameHierarchy The block hierarchy, with the most
|
* @param string[] $blockNameHierarchy The block hierarchy, with the most
|
||||||
* specific block name at the end
|
* specific block name at the end
|
||||||
* @param int $hierarchyLevel The level in the block hierarchy that
|
* @param int $hierarchyLevel The level in the block hierarchy that
|
||||||
* should be loaded
|
* should be loaded
|
||||||
|
@ -22,7 +22,7 @@ use Symfony\Component\Form\Exception\BadMethodCallException;
|
|||||||
class Button implements \IteratorAggregate, FormInterface
|
class Button implements \IteratorAggregate, FormInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var FormInterface|null
|
* @var FormInterface
|
||||||
*/
|
*/
|
||||||
private $parent;
|
private $parent;
|
||||||
|
|
||||||
@ -111,6 +111,8 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->parent = $parent;
|
$this->parent = $parent;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,11 +201,13 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
* This method should not be invoked.
|
* This method should not be invoked.
|
||||||
*
|
*
|
||||||
* @param mixed $modelData
|
* @param mixed $modelData
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setData($modelData)
|
public function setData($modelData)
|
||||||
{
|
{
|
||||||
// called during initialization of the form tree
|
// no-op, called during initialization of the form tree
|
||||||
// noop
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -211,6 +215,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,6 +223,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getNormData()
|
public function getNormData()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,6 +231,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getViewData()
|
public function getViewData()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,7 +247,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
/**
|
/**
|
||||||
* Returns the button's configuration.
|
* Returns the button's configuration.
|
||||||
*
|
*
|
||||||
* @return FormConfigInterface The configuration
|
* @return FormConfigInterface The configuration instance
|
||||||
*/
|
*/
|
||||||
public function getConfig()
|
public function getConfig()
|
||||||
{
|
{
|
||||||
@ -272,6 +279,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getPropertyPath()
|
public function getPropertyPath()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,11 +317,11 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function isDisabled()
|
public function isDisabled()
|
||||||
{
|
{
|
||||||
if (null === $this->parent || !$this->parent->isDisabled()) {
|
if ($this->parent && $this->parent->isDisabled()) {
|
||||||
return $this->config->getDisabled();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return $this->config->getDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,6 +349,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getTransformationFailure()
|
public function getTransformationFailure()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -368,7 +377,7 @@ class Button implements \IteratorAggregate, FormInterface
|
|||||||
/**
|
/**
|
||||||
* Submits data to the button.
|
* Submits data to the button.
|
||||||
*
|
*
|
||||||
* @param string|null $submittedData The data
|
* @param string|null $submittedData Not used
|
||||||
* @param bool $clearMissing Not used
|
* @param bool $clearMissing Not used
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
@ -22,9 +22,6 @@ use Symfony\Component\Form\Exception\InvalidArgumentException;
|
|||||||
*/
|
*/
|
||||||
class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $locked = false;
|
protected $locked = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,8 +50,6 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
private $options;
|
private $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new button builder.
|
|
||||||
*
|
|
||||||
* @param string $name The name of the button
|
* @param string $name The name of the button
|
||||||
* @param array $options The button's options
|
* @param array $options The button's options
|
||||||
*
|
*
|
||||||
@ -524,6 +519,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getEventDispatcher()
|
public function getEventDispatcher()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -539,6 +535,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getPropertyPath()
|
public function getPropertyPath()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,6 +603,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getDataMapper()
|
public function getDataMapper()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -643,6 +641,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getEmptyData()
|
public function getEmptyData()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -685,6 +684,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -692,6 +692,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getDataClass()
|
public function getDataClass()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -709,6 +710,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getFormFactory()
|
public function getFormFactory()
|
||||||
{
|
{
|
||||||
|
throw new BadMethodCallException('Buttons do not support adding children.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -716,6 +718,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getAction()
|
public function getAction()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -723,6 +726,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getMethod()
|
public function getMethod()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -730,6 +734,7 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface
|
|||||||
*/
|
*/
|
||||||
public function getRequestHandler()
|
public function getRequestHandler()
|
||||||
{
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,9 +11,6 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Form;
|
namespace Symfony\Component\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|
||||||
|
|
||||||
class CallbackTransformer implements DataTransformerInterface
|
class CallbackTransformer implements DataTransformerInterface
|
||||||
{
|
{
|
||||||
private $transform;
|
private $transform;
|
||||||
@ -30,14 +27,7 @@ class CallbackTransformer implements DataTransformerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a value from the original representation to a transformed representation.
|
* {@inheritdoc}
|
||||||
*
|
|
||||||
* @param mixed $data The value in the original representation
|
|
||||||
*
|
|
||||||
* @return mixed The value in the transformed representation
|
|
||||||
*
|
|
||||||
* @throws UnexpectedTypeException when the argument is not of the expected type
|
|
||||||
* @throws TransformationFailedException when the transformation fails
|
|
||||||
*/
|
*/
|
||||||
public function transform($data)
|
public function transform($data)
|
||||||
{
|
{
|
||||||
@ -45,15 +35,7 @@ class CallbackTransformer implements DataTransformerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a value from the transformed representation to its original
|
* {@inheritdoc}
|
||||||
* representation.
|
|
||||||
*
|
|
||||||
* @param mixed $data The value in the transformed representation
|
|
||||||
*
|
|
||||||
* @return mixed The value in the original representation
|
|
||||||
*
|
|
||||||
* @throws UnexpectedTypeException when the argument is not of the expected type
|
|
||||||
* @throws TransformationFailedException when the transformation fails
|
|
||||||
*/
|
*/
|
||||||
public function reverseTransform($data)
|
public function reverseTransform($data)
|
||||||
{
|
{
|
||||||
|
@ -17,22 +17,46 @@ namespace Symfony\Component\Form;
|
|||||||
interface DataMapperInterface
|
interface DataMapperInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Maps properties of some data to a list of forms.
|
* Maps the view data of a compound form to its children.
|
||||||
*
|
*
|
||||||
* @param mixed $data Structured data
|
* The method is responsible for calling {@link FormInterface::setData()}
|
||||||
|
* on the children of compound forms, defining their underlying model data.
|
||||||
|
*
|
||||||
|
* @param mixed $viewData View data of the compound form being initialized
|
||||||
* @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances
|
* @param FormInterface[]|\Traversable $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, $forms);
|
public function mapDataToForms($viewData, $forms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps the data of a list of forms into the properties of some data.
|
* Maps the model data of a list of children forms into the view data of their parent.
|
||||||
|
*
|
||||||
|
* This is the internal cascade call of FormInterface::submit for compound forms, since they
|
||||||
|
* cannot be bound to any input nor the request as scalar, but their children may:
|
||||||
|
*
|
||||||
|
* $compoundForm->submit($arrayOfChildrenViewData)
|
||||||
|
* // inside:
|
||||||
|
* $childForm->submit($childViewData);
|
||||||
|
* // for each entry, do the same and/or reverse transform
|
||||||
|
* $this->dataMapper->mapFormsToData($compoundForm, $compoundInitialViewData)
|
||||||
|
* // then reverse transform
|
||||||
|
*
|
||||||
|
* When a simple form is submitted the following is happening:
|
||||||
|
*
|
||||||
|
* $simpleForm->submit($submittedViewData)
|
||||||
|
* // inside:
|
||||||
|
* $this->viewData = $submittedViewData
|
||||||
|
* // then reverse transform
|
||||||
|
*
|
||||||
|
* The model data can be an array or an object, so this second argument is always passed
|
||||||
|
* by reference.
|
||||||
*
|
*
|
||||||
* @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances
|
* @param FormInterface[]|\Traversable $forms A list of {@link FormInterface} instances
|
||||||
* @param mixed $data Structured data
|
* @param mixed $viewData The compound form's view data that get mapped
|
||||||
|
* its children model 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($forms, &$data);
|
public function mapFormsToData($forms, &$viewData);
|
||||||
}
|
}
|
||||||
|
@ -23,23 +23,35 @@ interface DataTransformerInterface
|
|||||||
/**
|
/**
|
||||||
* Transforms a value from the original representation to a transformed representation.
|
* Transforms a value from the original representation to a transformed representation.
|
||||||
*
|
*
|
||||||
* This method is called on two occasions inside a form field:
|
* This method is called when the form field is initialized with its default data, on
|
||||||
|
* two occasions for two types of transformers:
|
||||||
*
|
*
|
||||||
* 1. When the form field is initialized with the data attached from the datasource (object or array).
|
* 1. Model transformers which normalize the model data.
|
||||||
* 2. When data from a request is submitted using {@link Form::submit()} to transform the new input data
|
* This is mainly useful when the same form type (the same configuration)
|
||||||
* back into the renderable format. For example if you have a date field and submit '2009-10-10'
|
* has to handle different kind of underlying data, e.g The DateType can
|
||||||
* you might accept this value because its easily parsed, but the transformer still writes back
|
* deal with strings or \DateTime objects as input.
|
||||||
* "2009/10/10" onto the form field (for further displaying or other purposes).
|
*
|
||||||
|
* 2. View transformers which adapt the normalized data to the view format.
|
||||||
|
* a/ When the form is simple, the value returned by convention is used
|
||||||
|
* directly in the view and thus can only be a string or an array. In
|
||||||
|
* this case the data class should be null.
|
||||||
|
*
|
||||||
|
* b/ When the form is compound the returned value should be an array or
|
||||||
|
* an object to be mapped to the children. Each property of the compound
|
||||||
|
* data will be used as model data by each child and will be transformed
|
||||||
|
* too. In this case data class should be the class of the object, or null
|
||||||
|
* when it is an array.
|
||||||
|
*
|
||||||
|
* All transformers are called in a configured order from model data to view value.
|
||||||
|
* At the end of this chain the view data will be validated against the data class
|
||||||
|
* setting.
|
||||||
*
|
*
|
||||||
* This method must be able to deal with empty values. Usually this will
|
* This method must be able to deal with empty values. Usually this will
|
||||||
* be NULL, but depending on your implementation other empty values are
|
* be NULL, but depending on your implementation other empty values are
|
||||||
* possible as well (such as empty strings). The reasoning behind this is
|
* possible as well (such as empty strings). The reasoning behind this is
|
||||||
* that value transformers must be chainable. If the transform() method
|
* that data transformers must be chainable. If the transform() method
|
||||||
* of the first value transformer outputs NULL, the second value transformer
|
* of the first data transformer outputs NULL, the second must be able to
|
||||||
* must be able to process that value.
|
* process that value.
|
||||||
*
|
|
||||||
* By convention, transform() should return an empty string if NULL is
|
|
||||||
* passed.
|
|
||||||
*
|
*
|
||||||
* @param mixed $value The value in the original representation
|
* @param mixed $value The value in the original representation
|
||||||
*
|
*
|
||||||
@ -54,7 +66,10 @@ interface DataTransformerInterface
|
|||||||
* representation.
|
* representation.
|
||||||
*
|
*
|
||||||
* This method is called when {@link Form::submit()} is called to transform the requests tainted data
|
* This method is called when {@link Form::submit()} is called to transform the requests tainted data
|
||||||
* into an acceptable format for your data processing/model layer.
|
* into an acceptable format.
|
||||||
|
*
|
||||||
|
* The same transformers are called in the reverse order so the responsibility is to
|
||||||
|
* return one of the types that would be expected as input of transform().
|
||||||
*
|
*
|
||||||
* This method must be able to deal with empty values. Usually this will
|
* This method must be able to deal with empty values. Usually this will
|
||||||
* be an empty string, but depending on your implementation other empty
|
* be an empty string, but depending on your implementation other empty
|
||||||
|
@ -43,8 +43,6 @@ class ButtonType extends BaseType implements ButtonTypeInterface
|
|||||||
{
|
{
|
||||||
parent::configureOptions($resolver);
|
parent::configureOptions($resolver);
|
||||||
|
|
||||||
$resolver->setDefaults([
|
$resolver->setDefault('auto_initialize', false);
|
||||||
'auto_initialize' => false,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,8 +328,8 @@ class ChoiceType extends AbstractType
|
|||||||
'placeholder' => $placeholderDefault,
|
'placeholder' => $placeholderDefault,
|
||||||
'error_bubbling' => false,
|
'error_bubbling' => false,
|
||||||
'compound' => $compound,
|
'compound' => $compound,
|
||||||
// The view data is always a string, even if the "data" option
|
// The view data is always a string or an array of strings,
|
||||||
// is manually set to an object.
|
// even if the "data" option is manually set to an object.
|
||||||
// See https://github.com/symfony/symfony/pull/5582
|
// See https://github.com/symfony/symfony/pull/5582
|
||||||
'data_class' => null,
|
'data_class' => null,
|
||||||
'choice_translation_domain' => true,
|
'choice_translation_domain' => true,
|
||||||
|
@ -21,6 +21,7 @@ use Symfony\Component\Form\Util\FormUtil;
|
|||||||
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
use Symfony\Component\Form\Util\InheritDataAwareIterator;
|
||||||
use Symfony\Component\Form\Util\OrderedHashMap;
|
use Symfony\Component\Form\Util\OrderedHashMap;
|
||||||
use Symfony\Component\PropertyAccess\PropertyPath;
|
use Symfony\Component\PropertyAccess\PropertyPath;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form represents a form.
|
* Form represents a form.
|
||||||
@ -63,79 +64,57 @@ use Symfony\Component\PropertyAccess\PropertyPath;
|
|||||||
class Form implements \IteratorAggregate, FormInterface
|
class Form implements \IteratorAggregate, FormInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The form's configuration.
|
|
||||||
*
|
|
||||||
* @var FormConfigInterface
|
* @var FormConfigInterface
|
||||||
*/
|
*/
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent of this form.
|
* @var FormInterface|null
|
||||||
*
|
|
||||||
* @var FormInterface
|
|
||||||
*/
|
*/
|
||||||
private $parent;
|
private $parent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The children of this form.
|
* @var FormInterface[]|OrderedHashMap A map of FormInterface instances
|
||||||
*
|
|
||||||
* @var FormInterface[] A map of FormInterface instances
|
|
||||||
*/
|
*/
|
||||||
private $children;
|
private $children;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The errors of this form.
|
|
||||||
*
|
|
||||||
* @var FormError[] An array of FormError instances
|
* @var FormError[] An array of FormError instances
|
||||||
*/
|
*/
|
||||||
private $errors = [];
|
private $errors = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this form was submitted.
|
|
||||||
*
|
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $submitted = false;
|
private $submitted = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The button that was used to submit the form.
|
* @var ClickableInterface|null The button that was used to submit the form
|
||||||
*
|
|
||||||
* @var Button
|
|
||||||
*/
|
*/
|
||||||
private $clickedButton;
|
private $clickedButton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form data in model format.
|
|
||||||
*
|
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $modelData;
|
private $modelData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form data in normalized format.
|
|
||||||
*
|
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $normData;
|
private $normData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form data in view format.
|
|
||||||
*
|
|
||||||
* @var mixed
|
* @var mixed
|
||||||
*/
|
*/
|
||||||
private $viewData;
|
private $viewData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The submitted values that don't belong to any children.
|
* @var array The submitted values that don't belong to any children
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
private $extraData = [];
|
private $extraData = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the transformation failure generated during submission, if any.
|
* @var TransformationFailedException|null The transformation failure generated during submission, if any
|
||||||
*
|
|
||||||
* @var TransformationFailedException|null
|
|
||||||
*/
|
*/
|
||||||
private $transformationFailure;
|
private $transformationFailure;
|
||||||
|
|
||||||
@ -161,8 +140,21 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
private $lockSetData = false;
|
private $lockSetData = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new form based on the given configuration.
|
* @var string|int|null
|
||||||
*
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool Whether the form inherits its underlying data from its parent
|
||||||
|
*/
|
||||||
|
private $inheritData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PropertyPathInterface|null
|
||||||
|
*/
|
||||||
|
private $propertyPath;
|
||||||
|
|
||||||
|
/**
|
||||||
* @throws LogicException if a data mapper is not provided for a compound form
|
* @throws LogicException if a data mapper is not provided for a compound form
|
||||||
*/
|
*/
|
||||||
public function __construct(FormConfigInterface $config)
|
public function __construct(FormConfigInterface $config)
|
||||||
@ -176,12 +168,13 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
|
|
||||||
// If the form inherits the data from its parent, it is not necessary
|
// If the form inherits the data from its parent, it is not necessary
|
||||||
// to call setData() with the default data.
|
// to call setData() with the default data.
|
||||||
if ($config->getInheritData()) {
|
if ($this->inheritData = $config->getInheritData()) {
|
||||||
$this->defaultDataSet = true;
|
$this->defaultDataSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
$this->children = new OrderedHashMap();
|
$this->children = new OrderedHashMap();
|
||||||
|
$this->name = $config->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
@ -206,7 +199,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getName()
|
public function getName()
|
||||||
{
|
{
|
||||||
return $this->config->getName();
|
return $this->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,11 +207,11 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getPropertyPath()
|
public function getPropertyPath()
|
||||||
{
|
{
|
||||||
if (null !== $this->config->getPropertyPath()) {
|
if ($this->propertyPath || $this->propertyPath = $this->config->getPropertyPath()) {
|
||||||
return $this->config->getPropertyPath();
|
return $this->propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null === $this->getName() || '' === $this->getName()) {
|
if (null === $this->name || '' === $this->name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,10 +222,12 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($parent && null === $parent->getConfig()->getDataClass()) {
|
if ($parent && null === $parent->getConfig()->getDataClass()) {
|
||||||
return new PropertyPath('['.$this->getName().']');
|
$this->propertyPath = new PropertyPath('['.$this->name.']');
|
||||||
|
} else {
|
||||||
|
$this->propertyPath = new PropertyPath($this->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PropertyPath($this->getName());
|
return $this->propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,7 +263,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
throw new AlreadySubmittedException('You cannot set the parent of a submitted form');
|
throw new AlreadySubmittedException('You cannot set the parent of a submitted form');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $parent && '' === $this->config->getName()) {
|
if (null !== $parent && '' === $this->name) {
|
||||||
throw new LogicException('A form with an empty name cannot have a parent form.');
|
throw new LogicException('A form with an empty name cannot have a parent form.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,7 +310,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
|
|
||||||
// If the form inherits its parent's data, disallow data setting to
|
// If the form inherits its parent's data, disallow data setting to
|
||||||
// prevent merge conflicts
|
// prevent merge conflicts
|
||||||
if ($this->config->getInheritData()) {
|
if ($this->inheritData) {
|
||||||
throw new RuntimeException('You cannot change the data of a form inheriting its parent data.');
|
throw new RuntimeException('You cannot change the data of a form inheriting its parent data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +330,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
$this->lockSetData = true;
|
$this->lockSetData = true;
|
||||||
$dispatcher = $this->config->getEventDispatcher();
|
$dispatcher = $this->config->getEventDispatcher();
|
||||||
|
|
||||||
// Hook to change content of the data
|
// Hook to change content of the model data before transformation and mapping children
|
||||||
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
|
if ($dispatcher->hasListeners(FormEvents::PRE_SET_DATA)) {
|
||||||
$event = new FormEvent($this, $modelData);
|
$event = new FormEvent($this, $modelData);
|
||||||
$dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event);
|
$dispatcher->dispatch(FormEvents::PRE_SET_DATA, $event);
|
||||||
@ -348,6 +343,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Synchronize representations - must not change the content!
|
// Synchronize representations - must not change the content!
|
||||||
|
// Transformation exceptions are not caught on initialization
|
||||||
$normData = $this->modelToNorm($modelData);
|
$normData = $this->modelToNorm($modelData);
|
||||||
$viewData = $this->normToView($normData);
|
$viewData = $this->normToView($normData);
|
||||||
|
|
||||||
@ -370,13 +366,10 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
$this->defaultDataSet = 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,
|
// Compound forms don't need to invoke this method if they don't have children
|
||||||
// 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 (unless their config data is locked)
|
||||||
$iterator = new InheritDataAwareIterator($this->children);
|
$this->config->getDataMapper()->mapDataToForms($viewData, new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children)));
|
||||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
|
||||||
$this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
|
if ($dispatcher->hasListeners(FormEvents::POST_SET_DATA)) {
|
||||||
@ -392,7 +385,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getData()
|
public function getData()
|
||||||
{
|
{
|
||||||
if ($this->config->getInheritData()) {
|
if ($this->inheritData) {
|
||||||
if (!$this->parent) {
|
if (!$this->parent) {
|
||||||
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
}
|
}
|
||||||
@ -416,7 +409,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getNormData()
|
public function getNormData()
|
||||||
{
|
{
|
||||||
if ($this->config->getInheritData()) {
|
if ($this->inheritData) {
|
||||||
if (!$this->parent) {
|
if (!$this->parent) {
|
||||||
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
}
|
}
|
||||||
@ -440,7 +433,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*/
|
*/
|
||||||
public function getViewData()
|
public function getViewData()
|
||||||
{
|
{
|
||||||
if ($this->config->getInheritData()) {
|
if ($this->inheritData) {
|
||||||
if (!$this->parent) {
|
if (!$this->parent) {
|
||||||
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
throw new RuntimeException('The form is configured to inherit its parent\'s data, but does not have a parent.');
|
||||||
}
|
}
|
||||||
@ -505,8 +498,8 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
throw new AlreadySubmittedException('A form can only be submitted once');
|
throw new AlreadySubmittedException('A form can only be submitted once');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize errors in the very beginning so that we don't lose any
|
// Initialize errors in the very beginning so we're sure
|
||||||
// errors added during listeners
|
// they are collectable during submission only
|
||||||
$this->errors = [];
|
$this->errors = [];
|
||||||
|
|
||||||
// Obviously, a disabled form should not change its data upon submission.
|
// Obviously, a disabled form should not change its data upon submission.
|
||||||
@ -605,18 +598,16 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
// changes in the grandchildren (i.e. children of the form that inherits
|
// changes in the grandchildren (i.e. children of the form that inherits
|
||||||
// its parent's data) into account.
|
// its parent's data) into account.
|
||||||
// (see InheritDataAwareIterator below)
|
// (see InheritDataAwareIterator below)
|
||||||
if (!$this->config->getInheritData()) {
|
if (!$this->inheritData) {
|
||||||
// If the form is compound, the default data in view format
|
// If the form is compound, the view data is merged with the data
|
||||||
// is reused. The data of the children is merged into this
|
// of the children using the data mapper.
|
||||||
// default data using the data mapper.
|
// If the form is not compound, the view data is assigned to the submitted data.
|
||||||
// If the form is not compound, the submitted data is also the data in view format.
|
|
||||||
$viewData = $this->config->getCompound() ? $this->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 */
|
|
||||||
$emptyData = $emptyData($this, $viewData);
|
$emptyData = $emptyData($this, $viewData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -631,9 +622,10 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
// descendants that inherit this form's data.
|
// descendants that inherit this form's data.
|
||||||
// These descendants will not be submitted normally (see the check
|
// These descendants will not be submitted normally (see the check
|
||||||
// for $this->config->getInheritData() above)
|
// for $this->config->getInheritData() above)
|
||||||
$childrenIterator = new InheritDataAwareIterator($this->children);
|
$this->config->getDataMapper()->mapFormsToData(
|
||||||
$childrenIterator = new \RecursiveIteratorIterator($childrenIterator);
|
new \RecursiveIteratorIterator(new InheritDataAwareIterator($this->children)),
|
||||||
$this->config->getDataMapper()->mapFormsToData($childrenIterator, $viewData);
|
$viewData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize data to unified representation
|
// Normalize data to unified representation
|
||||||
@ -658,7 +650,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
// the erroneous data is accessible on the form.
|
// the erroneous data is accessible on the form.
|
||||||
// Forms that inherit data never set any data, because the getters
|
// Forms that inherit data never set any data, because the getters
|
||||||
// forward to the parent form's getters anyway.
|
// forward to the parent form's getters anyway.
|
||||||
if (null === $viewData && !$this->config->getInheritData()) {
|
if (null === $viewData && !$this->inheritData) {
|
||||||
$viewData = $submittedData;
|
$viewData = $submittedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -757,8 +749,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
/**
|
/**
|
||||||
* Returns the button that was used to submit the form.
|
* Returns the button that was used to submit the form.
|
||||||
*
|
*
|
||||||
* @return Button|null The clicked button or NULL if the form was not
|
* @return ClickableInterface|null
|
||||||
* submitted
|
|
||||||
*/
|
*/
|
||||||
public function getClickedButton()
|
public function getClickedButton()
|
||||||
{
|
{
|
||||||
@ -826,29 +817,6 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?');
|
throw new LogicException('You cannot add children to a simple form. Maybe you should set the option "compound" to true?');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain the view data
|
|
||||||
$viewData = null;
|
|
||||||
|
|
||||||
// If setData() is currently being called, there is no need to call
|
|
||||||
// mapDataToForms() here, as mapDataToForms() is called at the end
|
|
||||||
// of setData() anyway. Not doing this check leads to an endless
|
|
||||||
// recursion when initializing the form lazily and an event listener
|
|
||||||
// (such as ResizeFormListener) adds fields depending on the data:
|
|
||||||
//
|
|
||||||
// * setData() is called, the form is not initialized yet
|
|
||||||
// * add() is called by the listener (setData() is not complete, so
|
|
||||||
// the form is still not initialized)
|
|
||||||
// * getViewData() is called
|
|
||||||
// * setData() is called since the form is not initialized yet
|
|
||||||
// * ... endless recursion ...
|
|
||||||
//
|
|
||||||
// Also skip data mapping if setData() has not been called yet.
|
|
||||||
// setData() will be called upon form initialization and data mapping
|
|
||||||
// will take place by then.
|
|
||||||
if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) {
|
|
||||||
$viewData = $this->getViewData();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$child instanceof FormInterface) {
|
if (!$child instanceof FormInterface) {
|
||||||
if (!\is_string($child) && !\is_int($child)) {
|
if (!\is_string($child) && !\is_int($child)) {
|
||||||
throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface');
|
throw new UnexpectedTypeException($child, 'string, integer or Symfony\Component\Form\FormInterface');
|
||||||
@ -878,10 +846,28 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
|
|
||||||
$child->setParent($this);
|
$child->setParent($this);
|
||||||
|
|
||||||
if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) {
|
// If setData() is currently being called, there is no need to call
|
||||||
$iterator = new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child]));
|
// mapDataToForms() here, as mapDataToForms() is called at the end
|
||||||
$iterator = new \RecursiveIteratorIterator($iterator);
|
// of setData() anyway. Not doing this check leads to an endless
|
||||||
$this->config->getDataMapper()->mapDataToForms($viewData, $iterator);
|
// recursion when initializing the form lazily and an event listener
|
||||||
|
// (such as ResizeFormListener) adds fields depending on the data:
|
||||||
|
//
|
||||||
|
// * setData() is called, the form is not initialized yet
|
||||||
|
// * add() is called by the listener (setData() is not complete, so
|
||||||
|
// the form is still not initialized)
|
||||||
|
// * getViewData() is called
|
||||||
|
// * setData() is called since the form is not initialized yet
|
||||||
|
// * ... endless recursion ...
|
||||||
|
//
|
||||||
|
// Also skip data mapping if setData() has not been called yet.
|
||||||
|
// setData() will be called upon form initialization and data mapping
|
||||||
|
// will take place by then.
|
||||||
|
if (!$this->lockSetData && $this->defaultDataSet && !$this->inheritData) {
|
||||||
|
$viewData = $this->getViewData();
|
||||||
|
$this->config->getDataMapper()->mapDataToForms(
|
||||||
|
$viewData,
|
||||||
|
new \RecursiveIteratorIterator(new InheritDataAwareIterator(new \ArrayIterator([$child->getName() => $child])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -1030,13 +1016,13 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes the value if a model transformer is set.
|
* Normalizes the underlying data if a model transformer is set.
|
||||||
*
|
*
|
||||||
* @param mixed $value The value to transform
|
* @param mixed $value The value to transform
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*
|
*
|
||||||
* @throws TransformationFailedException If the value cannot be transformed to "normalized" format
|
* @throws TransformationFailedException If the underlying data cannot be transformed to "normalized" format
|
||||||
*/
|
*/
|
||||||
private function modelToNorm($value)
|
private function modelToNorm($value)
|
||||||
{
|
{
|
||||||
@ -1045,7 +1031,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
$value = $transformer->transform($value);
|
$value = $transformer->transform($value);
|
||||||
}
|
}
|
||||||
} catch (TransformationFailedException $exception) {
|
} catch (TransformationFailedException $exception) {
|
||||||
throw new TransformationFailedException('Unable to transform value for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception);
|
throw new TransformationFailedException('Unable to transform data for property path "'.$this->getPropertyPath().'": '.$exception->getMessage(), $exception->getCode(), $exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $value;
|
return $value;
|
||||||
@ -1082,7 +1068,7 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*
|
*
|
||||||
* @throws TransformationFailedException If the value cannot be transformed to "view" format
|
* @throws TransformationFailedException If the normalized value cannot be transformed to "view" format
|
||||||
*/
|
*/
|
||||||
private function normToView($value)
|
private function normToView($value)
|
||||||
{
|
{
|
||||||
@ -1091,12 +1077,12 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
// Only do this for simple forms, as the resulting value in
|
// Only do this for simple forms, as the resulting value in
|
||||||
// compound forms is passed to the data mapper and thus should
|
// compound forms is passed to the data mapper and thus should
|
||||||
// not be converted to a string before.
|
// not be converted to a string before.
|
||||||
if (!$this->config->getViewTransformers() && !$this->config->getCompound()) {
|
if (!($transformers = $this->config->getViewTransformers()) && !$this->config->getCompound()) {
|
||||||
return null === $value || is_scalar($value) ? (string) $value : $value;
|
return null === $value || is_scalar($value) ? (string) $value : $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foreach ($this->config->getViewTransformers() as $transformer) {
|
foreach ($transformers as $transformer) {
|
||||||
$value = $transformer->transform($value);
|
$value = $transformer->transform($value);
|
||||||
}
|
}
|
||||||
} catch (TransformationFailedException $exception) {
|
} catch (TransformationFailedException $exception) {
|
||||||
@ -1113,13 +1099,11 @@ class Form implements \IteratorAggregate, FormInterface
|
|||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*
|
*
|
||||||
* @throws TransformationFailedException If the value cannot be transformed to "normalized" format
|
* @throws TransformationFailedException If the submitted value cannot be transformed to "normalized" format
|
||||||
*/
|
*/
|
||||||
private function viewToNorm($value)
|
private function viewToNorm($value)
|
||||||
{
|
{
|
||||||
$transformers = $this->config->getViewTransformers();
|
if (!$transformers = $this->config->getViewTransformers()) {
|
||||||
|
|
||||||
if (!$transformers) {
|
|
||||||
return '' === $value ? null : $value;
|
return '' === $value ? null : $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
private $unresolvedChildren = [];
|
private $unresolvedChildren = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new form builder.
|
|
||||||
*
|
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param string|null $dataClass
|
* @param string|null $dataClass
|
||||||
* @param EventDispatcherInterface $dispatcher
|
* @param EventDispatcherInterface $dispatcher
|
||||||
@ -81,10 +79,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
|
|
||||||
// Add to "children" to maintain order
|
// Add to "children" to maintain order
|
||||||
$this->children[$child] = null;
|
$this->children[$child] = null;
|
||||||
$this->unresolvedChildren[$child] = [
|
$this->unresolvedChildren[$child] = [$type, $options];
|
||||||
'type' => $type,
|
|
||||||
'options' => $options,
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -152,15 +147,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->unresolvedChildren[$name])) {
|
return isset($this->unresolvedChildren[$name]) || isset($this->children[$name]);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($this->children[$name])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -232,7 +219,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*
|
*
|
||||||
* @return FormBuilderInterface[]
|
* @return FormBuilderInterface[]|\Traversable
|
||||||
*/
|
*/
|
||||||
public function getIterator()
|
public function getIterator()
|
||||||
{
|
{
|
||||||
@ -252,12 +239,11 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
*/
|
*/
|
||||||
private function resolveChild($name)
|
private function resolveChild($name)
|
||||||
{
|
{
|
||||||
$info = $this->unresolvedChildren[$name];
|
list($type, $options) = $this->unresolvedChildren[$name];
|
||||||
$child = $this->create($name, $info['type'], $info['options']);
|
|
||||||
$this->children[$name] = $child;
|
|
||||||
unset($this->unresolvedChildren[$name]);
|
unset($this->unresolvedChildren[$name]);
|
||||||
|
|
||||||
return $child;
|
return $this->children[$name] = $this->create($name, $type, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -266,7 +252,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB
|
|||||||
private function resolveChildren()
|
private function resolveChildren()
|
||||||
{
|
{
|
||||||
foreach ($this->unresolvedChildren as $name => $info) {
|
foreach ($this->unresolvedChildren as $name => $info) {
|
||||||
$this->children[$name] = $this->create($name, $info['type'], $info['options']);
|
$this->children[$name] = $this->create($name, $info[0], $info[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->unresolvedChildren = [];
|
$this->unresolvedChildren = [];
|
||||||
|
@ -47,44 +47,19 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
'PATCH',
|
'PATCH',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $locked = false;
|
protected $locked = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var EventDispatcherInterface
|
|
||||||
*/
|
|
||||||
private $dispatcher;
|
private $dispatcher;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $name;
|
private $name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var PropertyPathInterface
|
* @var PropertyPathInterface|string|null
|
||||||
*/
|
*/
|
||||||
private $propertyPath;
|
private $propertyPath;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $mapped = true;
|
private $mapped = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $byReference = true;
|
private $byReference = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $inheritData = false;
|
private $inheritData = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $compound = false;
|
private $compound = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,34 +67,16 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
*/
|
*/
|
||||||
private $type;
|
private $type;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $viewTransformers = [];
|
private $viewTransformers = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $modelTransformers = [];
|
private $modelTransformers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DataMapperInterface
|
* @var DataMapperInterface|null
|
||||||
*/
|
*/
|
||||||
private $dataMapper;
|
private $dataMapper;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $required = true;
|
private $required = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $disabled = false;
|
private $disabled = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $errorBubbling = false;
|
private $errorBubbling = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,9 +84,6 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
*/
|
*/
|
||||||
private $emptyData;
|
private $emptyData;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $attributes = [];
|
private $attributes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,39 +96,26 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
*/
|
*/
|
||||||
private $dataClass;
|
private $dataClass;
|
||||||
|
|
||||||
/**
|
private $dataLocked = false;
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $dataLocked;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var FormFactoryInterface
|
* @var FormFactoryInterface|null
|
||||||
*/
|
*/
|
||||||
private $formFactory;
|
private $formFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
private $action;
|
private $action;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $method = 'POST';
|
private $method = 'POST';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var RequestHandlerInterface
|
* @var RequestHandlerInterface|null
|
||||||
*/
|
*/
|
||||||
private $requestHandler;
|
private $requestHandler;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $autoInitialize = false;
|
private $autoInitialize = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $options;
|
private $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -616,7 +557,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->errorBubbling = null === $errorBubbling ? null : (bool) $errorBubbling;
|
$this->errorBubbling = (bool) $errorBubbling;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -662,7 +603,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->mapped = $mapped;
|
$this->mapped = (bool) $mapped;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -676,7 +617,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->byReference = $byReference;
|
$this->byReference = (bool) $byReference;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -690,7 +631,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->inheritData = $inheritData;
|
$this->inheritData = (bool) $inheritData;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -704,7 +645,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->compound = $compound;
|
$this->compound = (bool) $compound;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -746,7 +687,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
throw new BadMethodCallException('FormConfigBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->dataLocked = $locked;
|
$this->dataLocked = (bool) $locked;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -774,7 +715,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
throw new BadMethodCallException('The config builder cannot be modified anymore.');
|
throw new BadMethodCallException('The config builder cannot be modified anymore.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->action = $action;
|
$this->action = (string) $action;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -790,7 +731,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
|
|
||||||
$upperCaseMethod = strtoupper($method);
|
$upperCaseMethod = strtoupper($method);
|
||||||
|
|
||||||
if (!\in_array($upperCaseMethod, self::$allowedMethods)) {
|
if (!\in_array($upperCaseMethod, self::$allowedMethods, true)) {
|
||||||
throw new InvalidArgumentException(sprintf('The form method is "%s", but should be one of "%s".', $method, implode('", "', self::$allowedMethods)));
|
throw new InvalidArgumentException(sprintf('The form method is "%s", but should be one of "%s".', $method, implode('", "', self::$allowedMethods)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,7 +787,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
/**
|
/**
|
||||||
* Validates whether the given variable is a valid form name.
|
* Validates whether the given variable is a valid form name.
|
||||||
*
|
*
|
||||||
* @param string|int $name The tested form name
|
* @param string|int|null $name The tested form name
|
||||||
*
|
*
|
||||||
* @throws UnexpectedTypeException if the name is not a string or an integer
|
* @throws UnexpectedTypeException if the name is not a string or an integer
|
||||||
* @throws InvalidArgumentException if the name contains invalid characters
|
* @throws InvalidArgumentException if the name contains invalid characters
|
||||||
@ -872,7 +813,7 @@ class FormConfigBuilder implements FormConfigBuilderInterface
|
|||||||
* * contains only letters, digits, numbers, underscores ("_"),
|
* * contains only letters, digits, numbers, underscores ("_"),
|
||||||
* hyphens ("-") and colons (":")
|
* hyphens ("-") and colons (":")
|
||||||
*
|
*
|
||||||
* @param string $name The tested form name
|
* @param string|null $name The tested form name
|
||||||
*
|
*
|
||||||
* @return bool Whether the name is valid
|
* @return bool Whether the name is valid
|
||||||
*/
|
*/
|
||||||
|
@ -108,7 +108,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface
|
|||||||
public function setDataMapper(DataMapperInterface $dataMapper = null);
|
public function setDataMapper(DataMapperInterface $dataMapper = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether the form is disabled.
|
* Sets whether the form is disabled.
|
||||||
*
|
*
|
||||||
* @param bool $disabled Whether the form is disabled
|
* @param bool $disabled Whether the form is disabled
|
||||||
*
|
*
|
||||||
@ -166,8 +166,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface
|
|||||||
/**
|
/**
|
||||||
* Sets whether the form's data should be modified by reference.
|
* Sets whether the form's data should be modified by reference.
|
||||||
*
|
*
|
||||||
* @param bool $byReference Whether the data should be
|
* @param bool $byReference Whether the data should be modified by reference
|
||||||
* modified by reference
|
|
||||||
*
|
*
|
||||||
* @return $this The configuration object
|
* @return $this The configuration object
|
||||||
*/
|
*/
|
||||||
@ -194,7 +193,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface
|
|||||||
public function setCompound($compound);
|
public function setCompound($compound);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the types.
|
* Sets the resolved type.
|
||||||
*
|
*
|
||||||
* @return $this The configuration object
|
* @return $this The configuration object
|
||||||
*/
|
*/
|
||||||
@ -203,7 +202,7 @@ interface FormConfigBuilderInterface extends FormConfigInterface
|
|||||||
/**
|
/**
|
||||||
* Sets the initial data of the form.
|
* Sets the initial data of the form.
|
||||||
*
|
*
|
||||||
* @param mixed $data The data of the form in application format
|
* @param mixed $data The data of the form in model format
|
||||||
*
|
*
|
||||||
* @return $this The configuration object
|
* @return $this The configuration object
|
||||||
*/
|
*/
|
||||||
@ -214,9 +213,12 @@ interface FormConfigBuilderInterface extends FormConfigInterface
|
|||||||
*
|
*
|
||||||
* A form with locked data is restricted to the data passed in
|
* A form with locked data is restricted to the data passed in
|
||||||
* this configuration. The data can only be modified then by
|
* this configuration. The data can only be modified then by
|
||||||
* submitting the form.
|
* submitting the form or using PRE_SET_DATA event.
|
||||||
*
|
*
|
||||||
* @param bool $locked Whether to lock the default data
|
* It means data passed to a factory method or mapped from the
|
||||||
|
* parent will be ignored.
|
||||||
|
*
|
||||||
|
* @param bool $locked Whether to lock the default configured data
|
||||||
*
|
*
|
||||||
* @return $this The configuration object
|
* @return $this The configuration object
|
||||||
*/
|
*/
|
||||||
|
@ -70,15 +70,17 @@ interface FormConfigInterface
|
|||||||
* This property is independent of whether the form actually has
|
* This property is independent of whether the form actually has
|
||||||
* children. A form can be compound and have no children at all, like
|
* children. A form can be compound and have no children at all, like
|
||||||
* for example an empty collection form.
|
* for example an empty collection form.
|
||||||
|
* The contrary is not possible, a form which is not compound
|
||||||
|
* cannot have any children.
|
||||||
*
|
*
|
||||||
* @return bool Whether the form is compound
|
* @return bool Whether the form is compound
|
||||||
*/
|
*/
|
||||||
public function getCompound();
|
public function getCompound();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the form types used to construct the form.
|
* Returns the resolved form type used to construct the form.
|
||||||
*
|
*
|
||||||
* @return ResolvedFormTypeInterface The form's type
|
* @return ResolvedFormTypeInterface The form's resolved type
|
||||||
*/
|
*/
|
||||||
public function getType();
|
public function getType();
|
||||||
|
|
||||||
@ -97,7 +99,7 @@ interface FormConfigInterface
|
|||||||
public function getModelTransformers();
|
public function getModelTransformers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data mapper of the form.
|
* Returns the data mapper of the compound form or null for a simple form.
|
||||||
*
|
*
|
||||||
* @return DataMapperInterface|null The data mapper
|
* @return DataMapperInterface|null The data mapper
|
||||||
*/
|
*/
|
||||||
@ -125,9 +127,15 @@ interface FormConfigInterface
|
|||||||
public function getErrorBubbling();
|
public function getErrorBubbling();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data that should be returned when the form is empty.
|
* Used when the view data is empty on submission.
|
||||||
*
|
*
|
||||||
* @return mixed The data returned if the form is empty
|
* When the form is compound it will also be used to map the
|
||||||
|
* children data.
|
||||||
|
*
|
||||||
|
* The empty data must match the view format as it will passed to the first view transformer's
|
||||||
|
* "reverseTransform" method.
|
||||||
|
*
|
||||||
|
* @return mixed The data used when the submitted form is initially empty
|
||||||
*/
|
*/
|
||||||
public function getEmptyData();
|
public function getEmptyData();
|
||||||
|
|
||||||
@ -165,7 +173,7 @@ interface FormConfigInterface
|
|||||||
public function getData();
|
public function getData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the class of the form data or null if the data is scalar or an array.
|
* Returns the class of the view data or null if the data is scalar or an array.
|
||||||
*
|
*
|
||||||
* @return string|null The data class or null
|
* @return string|null The data class or null
|
||||||
*/
|
*/
|
||||||
|
@ -49,7 +49,7 @@ class FormError implements \Serializable
|
|||||||
*/
|
*/
|
||||||
public function __construct($message, $messageTemplate = null, array $messageParameters = [], $messagePluralization = null, $cause = null)
|
public function __construct($message, $messageTemplate = null, array $messageParameters = [], $messagePluralization = null, $cause = null)
|
||||||
{
|
{
|
||||||
$this->message = $message;
|
$this->message = (string) $message;
|
||||||
$this->messageTemplate = $messageTemplate ?: $message;
|
$this->messageTemplate = $messageTemplate ?: $message;
|
||||||
$this->messageParameters = $messageParameters;
|
$this->messageParameters = $messageParameters;
|
||||||
$this->messagePluralization = $messagePluralization;
|
$this->messagePluralization = $messagePluralization;
|
||||||
|
@ -39,10 +39,9 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array
|
|||||||
private $errors;
|
private $errors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new iterator.
|
|
||||||
*
|
|
||||||
* @param FormInterface $form The erroneous form
|
* @param FormInterface $form The erroneous form
|
||||||
* @param FormError[]|FormErrorIterator[] $errors The form errors
|
* @param FormError[]|self[] $errors An array of form errors and instances
|
||||||
|
* of FormErrorIterator
|
||||||
*
|
*
|
||||||
* @throws InvalidArgumentException If the errors are invalid
|
* @throws InvalidArgumentException If the errors are invalid
|
||||||
*/
|
*/
|
||||||
@ -71,7 +70,7 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array
|
|||||||
if ($error instanceof FormError) {
|
if ($error instanceof FormError) {
|
||||||
$string .= 'ERROR: '.$error->getMessage()."\n";
|
$string .= 'ERROR: '.$error->getMessage()."\n";
|
||||||
} else {
|
} else {
|
||||||
/* @var $error FormErrorIterator */
|
/** @var self $error */
|
||||||
$string .= $error->form->getName().":\n";
|
$string .= $error->form->getName().":\n";
|
||||||
$string .= self::indent((string) $error);
|
$string .= self::indent((string) $error);
|
||||||
}
|
}
|
||||||
@ -93,8 +92,7 @@ class FormErrorIterator implements \RecursiveIterator, \SeekableIterator, \Array
|
|||||||
/**
|
/**
|
||||||
* Returns the current element of the iterator.
|
* Returns the current element of the iterator.
|
||||||
*
|
*
|
||||||
* @return FormError|FormErrorIterator an error or an iterator containing
|
* @return FormError|self An error or an iterator containing nested errors
|
||||||
* nested errors
|
|
||||||
*/
|
*/
|
||||||
public function current()
|
public function current()
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,10 @@ class FormEvent extends Event
|
|||||||
private $form;
|
private $form;
|
||||||
protected $data;
|
protected $data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param FormInterface $form The associated form
|
||||||
|
* @param mixed $data The data
|
||||||
|
*/
|
||||||
public function __construct(FormInterface $form, $data)
|
public function __construct(FormInterface $form, $data)
|
||||||
{
|
{
|
||||||
$this->form = $form;
|
$this->form = $form;
|
||||||
|
@ -34,21 +34,35 @@ final class FormEvents
|
|||||||
const PRE_SUBMIT = 'form.pre_bind';
|
const PRE_SUBMIT = 'form.pre_bind';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SUBMIT event is dispatched just before the Form::submit() method
|
* The SUBMIT event is dispatched after the Form::submit() method
|
||||||
* transforms back the normalized data to the model and view data.
|
* has changed the view data by the request data, or submitted and mapped
|
||||||
|
* the children if the form is compound, and after reverse transformation
|
||||||
|
* to normalized representation.
|
||||||
*
|
*
|
||||||
* It can be used to change data from the normalized representation of the data.
|
* It's also dispatched just before the Form::submit() method transforms back
|
||||||
|
* the normalized data to the model and view data.
|
||||||
|
*
|
||||||
|
* So at this stage children of compound forms are submitted and synchronized, unless
|
||||||
|
* their transformation failed, but a parent would still be at the PRE_SUBMIT level.
|
||||||
|
*
|
||||||
|
* Since the current form is not synchronized yet, it is still possible to add and
|
||||||
|
* remove fields.
|
||||||
*
|
*
|
||||||
* @Event("Symfony\Component\Form\FormEvent")
|
* @Event("Symfony\Component\Form\FormEvent")
|
||||||
*/
|
*/
|
||||||
const SUBMIT = 'form.bind';
|
const SUBMIT = 'form.bind';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The FormEvents::POST_SUBMIT event is dispatched after the Form::submit()
|
* The FormEvents::POST_SUBMIT event is dispatched at the very end of the Form::submit().
|
||||||
* once the model and view data have been denormalized.
|
*
|
||||||
|
* It this stage the model and view data may have been denormalized. Otherwise the form
|
||||||
|
* is desynchronized because transformation failed during submission.
|
||||||
*
|
*
|
||||||
* It can be used to fetch data after denormalization.
|
* It can be used to fetch data after denormalization.
|
||||||
*
|
*
|
||||||
|
* The event attaches the current view data. To know whether this is the renormalized data
|
||||||
|
* or the invalid request data, call Form::isSynchronized() first.
|
||||||
|
*
|
||||||
* @Event("Symfony\Component\Form\FormEvent")
|
* @Event("Symfony\Component\Form\FormEvent")
|
||||||
*/
|
*/
|
||||||
const POST_SUBMIT = 'form.post_bind';
|
const POST_SUBMIT = 'form.post_bind';
|
||||||
@ -58,7 +72,7 @@ final class FormEvents
|
|||||||
*
|
*
|
||||||
* It can be used to:
|
* It can be used to:
|
||||||
* - Modify the data given during pre-population;
|
* - Modify the data given during pre-population;
|
||||||
* - Modify a form depending on the pre-populated data (adding or removing fields dynamically).
|
* - Keep synchronized the form depending on the data (adding or removing fields dynamically).
|
||||||
*
|
*
|
||||||
* @Event("Symfony\Component\Form\FormEvent")
|
* @Event("Symfony\Component\Form\FormEvent")
|
||||||
*/
|
*/
|
||||||
@ -67,7 +81,8 @@ final class FormEvents
|
|||||||
/**
|
/**
|
||||||
* The FormEvents::POST_SET_DATA event is dispatched at the end of the Form::setData() method.
|
* The FormEvents::POST_SET_DATA event is dispatched at the end of the Form::setData() method.
|
||||||
*
|
*
|
||||||
* This event is mostly here for reading data after having pre-populated the form.
|
* This event can be used to modify the form depending on the final state of the underlying data
|
||||||
|
* accessible in every representation: model, normalized and view.
|
||||||
*
|
*
|
||||||
* @Event("Symfony\Component\Form\FormEvent")
|
* @Event("Symfony\Component\Form\FormEvent")
|
||||||
*/
|
*/
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Form;
|
namespace Symfony\Component\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
use Symfony\Component\PropertyAccess\PropertyPathInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A form group bundling multiple forms in a hierarchical structure.
|
* A form group bundling multiple forms in a hierarchical structure.
|
||||||
@ -23,7 +23,9 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
/**
|
/**
|
||||||
* Sets the parent form.
|
* Sets the parent form.
|
||||||
*
|
*
|
||||||
* @return self
|
* @param FormInterface|null $parent The parent form or null if it's the root
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
*
|
*
|
||||||
* @throws Exception\AlreadySubmittedException if the form has already been submitted
|
* @throws Exception\AlreadySubmittedException if the form has already been submitted
|
||||||
* @throws Exception\LogicException when trying to set a parent for a form with
|
* @throws Exception\LogicException when trying to set a parent for a form with
|
||||||
@ -45,7 +47,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
* @param string|null $type The child's type, if a name was passed
|
* @param string|null $type The child's type, if a name was passed
|
||||||
* @param array $options The child's options, if a name was passed
|
* @param array $options The child's options, if a name was passed
|
||||||
*
|
*
|
||||||
* @return self
|
* @return $this
|
||||||
*
|
*
|
||||||
* @throws Exception\AlreadySubmittedException if the form has already been submitted
|
* @throws Exception\AlreadySubmittedException if the form has already been submitted
|
||||||
* @throws Exception\LogicException when trying to add a child to a non-compound form
|
* @throws Exception\LogicException when trying to add a child to a non-compound form
|
||||||
@ -104,44 +106,70 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
public function getErrors($deep = false, $flatten = true);
|
public function getErrors($deep = false, $flatten = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the form with default data.
|
* Updates the form with default model data.
|
||||||
*
|
*
|
||||||
* @param mixed $modelData The data formatted as expected for the underlying object
|
* @param mixed $modelData The data formatted as expected for the underlying object
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*
|
*
|
||||||
* @throws Exception\AlreadySubmittedException if the form has already been submitted
|
* @throws Exception\AlreadySubmittedException If the form has already been submitted
|
||||||
* @throws Exception\LogicException If listeners try to call setData in a cycle. Or if
|
* @throws Exception\LogicException If the view data does not match the expected type
|
||||||
* the view data does not match the expected type
|
|
||||||
* according to {@link FormConfigInterface::getDataClass}.
|
* according to {@link FormConfigInterface::getDataClass}.
|
||||||
|
* @throws Exception\RuntimeException If listeners try to call setData in a cycle or if
|
||||||
|
* the form inherits data from its parent
|
||||||
|
* @throws Exception\TransformationFailedException If the synchronization failed.
|
||||||
*/
|
*/
|
||||||
public function setData($modelData);
|
public function setData($modelData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data in the format needed for the underlying object.
|
* Returns the model data in the format needed for the underlying object.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed When the field is not submitted, the default data is returned.
|
||||||
|
* When the field is submitted, the default data has been bound
|
||||||
|
* to the submitted view data.
|
||||||
|
*
|
||||||
|
* @throws Exception\RuntimeException If the form inherits data but has no parent
|
||||||
*/
|
*/
|
||||||
public function getData();
|
public function getData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the normalized data of the field.
|
* Returns the normalized data of the field, used as internal bridge
|
||||||
|
* between model data and view data.
|
||||||
*
|
*
|
||||||
* @return mixed When the field is not submitted, the default data is returned.
|
* @return mixed When the field is not submitted, the default data is returned.
|
||||||
* When the field is submitted, the normalized submitted data is
|
* When the field is submitted, the normalized submitted data
|
||||||
* returned if the field is valid, null otherwise.
|
* is returned if the field is synchronized with the view data,
|
||||||
|
* null otherwise.
|
||||||
|
*
|
||||||
|
* @throws Exception\RuntimeException If the form inherits data but has no parent
|
||||||
*/
|
*/
|
||||||
public function getNormData();
|
public function getNormData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data transformed by the value transformer.
|
* Returns the view data of the field.
|
||||||
|
*
|
||||||
|
* It may be defined by {@link FormConfigInterface::getDataClass}.
|
||||||
|
*
|
||||||
|
* There are two cases:
|
||||||
|
*
|
||||||
|
* - When the form is compound the view data is mapped to the children.
|
||||||
|
* Each child will use its mapped data as model data.
|
||||||
|
* It can be an array, an object or null.
|
||||||
|
*
|
||||||
|
* - When the form is simple its view data is used to be bound
|
||||||
|
* to the submitted data.
|
||||||
|
* It can be a string or an array.
|
||||||
|
*
|
||||||
|
* In both cases the view data is the actual altered data on submission.
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
*
|
||||||
|
* @throws Exception\RuntimeException If the form inherits data but has no parent
|
||||||
*/
|
*/
|
||||||
public function getViewData();
|
public function getViewData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the extra data.
|
* Returns the extra submitted data.
|
||||||
*
|
*
|
||||||
* @return array The submitted data which do not belong to a child
|
* @return array The submitted data which do not belong to a child
|
||||||
*/
|
*/
|
||||||
@ -150,7 +178,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the form's configuration.
|
* Returns the form's configuration.
|
||||||
*
|
*
|
||||||
* @return FormConfigInterface The configuration
|
* @return FormConfigInterface The configuration instance
|
||||||
*/
|
*/
|
||||||
public function getConfig();
|
public function getConfig();
|
||||||
|
|
||||||
@ -164,6 +192,8 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the name by which the form is identified in forms.
|
* Returns the name by which the form is identified in forms.
|
||||||
*
|
*
|
||||||
|
* Only root forms are allowed to have an empty name.
|
||||||
|
*
|
||||||
* @return string The name of the form
|
* @return string The name of the form
|
||||||
*/
|
*/
|
||||||
public function getName();
|
public function getName();
|
||||||
@ -171,7 +201,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the property path that the form is mapped to.
|
* Returns the property path that the form is mapped to.
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\PropertyAccess\PropertyPathInterface|null The property path
|
* @return PropertyPathInterface|null The property path instance
|
||||||
*/
|
*/
|
||||||
public function getPropertyPath();
|
public function getPropertyPath();
|
||||||
|
|
||||||
@ -230,14 +260,16 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
* If the data is not synchronized, you can get the transformation failure
|
* If the data is not synchronized, you can get the transformation failure
|
||||||
* by calling {@link getTransformationFailure()}.
|
* by calling {@link getTransformationFailure()}.
|
||||||
*
|
*
|
||||||
|
* If the form is not submitted, this method always returns true.
|
||||||
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isSynchronized();
|
public function isSynchronized();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data transformation failure, if any.
|
* Returns the data transformation failure, if any, during submission.
|
||||||
*
|
*
|
||||||
* @return TransformationFailedException|null The transformation failure
|
* @return Exception\TransformationFailedException|null The transformation failure or null
|
||||||
*/
|
*/
|
||||||
public function getTransformationFailure();
|
public function getTransformationFailure();
|
||||||
|
|
||||||
@ -247,6 +279,8 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
* Should be called on the root form after constructing the tree.
|
* Should be called on the root form after constructing the tree.
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
|
*
|
||||||
|
* @throws Exception\RuntimeException If the form is not the root
|
||||||
*/
|
*/
|
||||||
public function initialize();
|
public function initialize();
|
||||||
|
|
||||||
@ -265,11 +299,13 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
public function handleRequest($request = null);
|
public function handleRequest($request = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submits data to the form, transforms and validates it.
|
* Submits data to the form.
|
||||||
*
|
*
|
||||||
* @param mixed $submittedData The submitted data
|
* @param string|array|null $submittedData The submitted data
|
||||||
* @param bool $clearMissing Whether to set fields to NULL when they
|
* @param bool $clearMissing Whether to set fields to NULL
|
||||||
* are missing in the submitted data
|
* when they are missing in the
|
||||||
|
* submitted data. This argument
|
||||||
|
* is only used in compound form
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*
|
*
|
||||||
@ -280,7 +316,7 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
/**
|
/**
|
||||||
* Returns the root of the form tree.
|
* Returns the root of the form tree.
|
||||||
*
|
*
|
||||||
* @return self The root of the tree
|
* @return self The root of the tree, may be the instance itself
|
||||||
*/
|
*/
|
||||||
public function getRoot();
|
public function getRoot();
|
||||||
|
|
||||||
@ -292,8 +328,6 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable
|
|||||||
public function isRoot();
|
public function isRoot();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a view.
|
|
||||||
*
|
|
||||||
* @return FormView The view
|
* @return FormView The view
|
||||||
*/
|
*/
|
||||||
public function createView(FormView $parent = null);
|
public function createView(FormView $parent = null);
|
||||||
|
@ -26,7 +26,7 @@ class FormRegistry implements FormRegistryInterface
|
|||||||
/**
|
/**
|
||||||
* Extensions.
|
* Extensions.
|
||||||
*
|
*
|
||||||
* @var FormExtensionInterface[] An array of FormExtensionInterface
|
* @var FormExtensionInterface[]
|
||||||
*/
|
*/
|
||||||
private $extensions = [];
|
private $extensions = [];
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ interface FormRendererEngineInterface
|
|||||||
* First the themes attached directly to
|
* First the themes attached directly to
|
||||||
* the view with {@link setTheme()} are
|
* the view with {@link setTheme()} are
|
||||||
* considered, then the ones of its parent etc.
|
* considered, then the ones of its parent etc.
|
||||||
* @param array $blockNameHierarchy The block name hierarchy, with the root block
|
* @param string[] $blockNameHierarchy The block name hierarchy, with the root block
|
||||||
* at the beginning
|
* at the beginning
|
||||||
* @param int $hierarchyLevel The level in the hierarchy at which to start
|
* @param int $hierarchyLevel The level in the hierarchy at which to start
|
||||||
* looking. Level 0 indicates the root block, i.e.
|
* looking. Level 0 indicates the root block, i.e.
|
||||||
@ -112,7 +112,7 @@ interface FormRendererEngineInterface
|
|||||||
* First the themes attached directly to
|
* First the themes attached directly to
|
||||||
* the view with {@link setTheme()} are
|
* the view with {@link setTheme()} are
|
||||||
* considered, then the ones of its parent etc.
|
* considered, then the ones of its parent etc.
|
||||||
* @param array $blockNameHierarchy The block name hierarchy, with the root block
|
* @param string[] $blockNameHierarchy The block name hierarchy, with the root block
|
||||||
* at the beginning
|
* at the beginning
|
||||||
* @param int $hierarchyLevel The level in the hierarchy at which to start
|
* @param int $hierarchyLevel The level in the hierarchy at which to start
|
||||||
* looking. Level 0 indicates the root block, i.e.
|
* looking. Level 0 indicates the root block, i.e.
|
||||||
|
@ -19,7 +19,7 @@ class FormTypeGuesserChain implements FormTypeGuesserInterface
|
|||||||
private $guessers = [];
|
private $guessers = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param FormTypeGuesserInterface[] $guessers Guessers as instances of FormTypeGuesserInterface
|
* @param FormTypeGuesserInterface[] $guessers
|
||||||
*
|
*
|
||||||
* @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface
|
* @throws UnexpectedTypeException if any guesser does not implement FormTypeGuesserInterface
|
||||||
*/
|
*/
|
||||||
|
@ -49,8 +49,8 @@ interface FormTypeGuesserInterface
|
|||||||
/**
|
/**
|
||||||
* Returns a guess about the field's pattern.
|
* Returns a guess about the field's pattern.
|
||||||
*
|
*
|
||||||
* - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE) , lines below
|
* - When you have a min value, you guess a min length of this min (LOW_CONFIDENCE)
|
||||||
* - If this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess.
|
* - Then line below, if this value is a float type, this is wrong so you guess null with MEDIUM_CONFIDENCE to override the previous guess.
|
||||||
* Example:
|
* Example:
|
||||||
* You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5)
|
* You want a float greater than 5, 4.512313 is not valid but length(4.512314) > length(5)
|
||||||
*
|
*
|
||||||
|
@ -41,6 +41,8 @@ class NativeRequestHandler implements RequestHandlerInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @throws Exception\UnexpectedTypeException If the $request is not null
|
||||||
*/
|
*/
|
||||||
public function handleRequest(FormInterface $form, $request = null)
|
public function handleRequest(FormInterface $form, $request = null)
|
||||||
{
|
{
|
||||||
|
@ -450,6 +450,31 @@ class CompoundFormTest extends AbstractFormTest
|
|||||||
$form->setData('foo');
|
$form->setData('foo');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetDataDoesNotMapViewDataToChildrenWithLockedSetData()
|
||||||
|
{
|
||||||
|
$mapper = new PropertyPathMapper();
|
||||||
|
$viewData = [
|
||||||
|
'firstName' => 'Fabien',
|
||||||
|
'lastName' => 'Pot',
|
||||||
|
];
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setCompound(true)
|
||||||
|
->setDataMapper($mapper)
|
||||||
|
->addViewTransformer(new FixedDataTransformer([
|
||||||
|
'' => '',
|
||||||
|
'foo' => $viewData,
|
||||||
|
]))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->add($child1 = $this->getBuilder('firstName')->getForm());
|
||||||
|
$form->add($child2 = $this->getBuilder('lastName')->setData('Potencier')->setDataLocked(true)->getForm());
|
||||||
|
|
||||||
|
$form->setData('foo');
|
||||||
|
|
||||||
|
$this->assertSame('Fabien', $form->get('firstName')->getData());
|
||||||
|
$this->assertSame('Potencier', $form->get('lastName')->getData());
|
||||||
|
}
|
||||||
|
|
||||||
public function testSubmitSupportsDynamicAdditionAndRemovalOfChildren()
|
public function testSubmitSupportsDynamicAdditionAndRemovalOfChildren()
|
||||||
{
|
{
|
||||||
$form = $this->form;
|
$form = $this->form;
|
||||||
|
@ -54,6 +54,26 @@ class SimpleFormTest_Traversable implements \IteratorAggregate
|
|||||||
|
|
||||||
class SimpleFormTest extends AbstractFormTest
|
class SimpleFormTest extends AbstractFormTest
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider provideFormNames
|
||||||
|
*/
|
||||||
|
public function testGetPropertyPath($name, $propertyPath)
|
||||||
|
{
|
||||||
|
$config = new FormConfigBuilder($name, null, $this->dispatcher);
|
||||||
|
$form = new Form($config);
|
||||||
|
|
||||||
|
$this->assertEquals(new PropertyPath($propertyPath), $form->getPropertyPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideFormNames()
|
||||||
|
{
|
||||||
|
yield [null, null];
|
||||||
|
yield ['', null];
|
||||||
|
yield ['0', '0'];
|
||||||
|
yield [0, '0'];
|
||||||
|
yield ['name', 'name'];
|
||||||
|
}
|
||||||
|
|
||||||
public function testDataIsInitializedToConfiguredValue()
|
public function testDataIsInitializedToConfiguredValue()
|
||||||
{
|
{
|
||||||
$model = new FixedDataTransformer([
|
$model = new FixedDataTransformer([
|
||||||
@ -76,7 +96,7 @@ class SimpleFormTest extends AbstractFormTest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||||
* @expectedExceptionMessage Unable to transform value for property path "name": No mapping for value "arg"
|
* @expectedExceptionMessage Unable to transform data for property path "name": No mapping for value "arg"
|
||||||
*/
|
*/
|
||||||
public function testDataTransformationFailure()
|
public function testDataTransformationFailure()
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user