From 441222eda7b0885c836bc240e375824d86e2e95b Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Mon, 29 Apr 2013 17:31:30 +0200 Subject: [PATCH] [Form] *_SET_DATA events are now guaranteed to be fired *after* the initial children were added --- UPGRADE-2.3.md | 60 +++++++++++++ src/Symfony/Component/Form/Button.php | 12 ++- src/Symfony/Component/Form/ButtonBuilder.php | 26 ++++++ src/Symfony/Component/Form/CHANGELOG.md | 6 ++ .../Form/Extension/Core/Type/FormType.php | 2 + src/Symfony/Component/Form/Form.php | 36 +++++++- src/Symfony/Component/Form/FormBuilder.php | 10 ++- .../Component/Form/FormConfigBuilder.php | 28 ++++-- .../Form/FormConfigBuilderInterface.php | 16 ++++ .../Component/Form/FormConfigInterface.php | 12 ++- src/Symfony/Component/Form/FormInterface.php | 20 +++-- .../Form/Tests/AbstractDivLayoutTest.php | 13 ++- .../Component/Form/Tests/AbstractFormTest.php | 4 + .../Form/Tests/AbstractLayoutTest.php | 14 +-- .../Component/Form/Tests/CompoundFormTest.php | 34 ++++++- .../EventListener/ResizeFormListenerTest.php | 6 +- .../Extension/Core/Type/BaseTypeTest.php | 58 ++++++------ .../Extension/Core/Type/FormTypeTest.php | 88 +++++++++++-------- .../Tests/Fixtures/AlternatingRowType.php | 2 +- .../Component/Form/Tests/SimpleFormTest.php | 32 ++++++- 20 files changed, 374 insertions(+), 105 deletions(-) diff --git a/UPGRADE-2.3.md b/UPGRADE-2.3.md index cba8a0541b..9bb123e32a 100644 --- a/UPGRADE-2.3.md +++ b/UPGRADE-2.3.md @@ -110,6 +110,66 @@ Form } ``` + * The *_SET_DATA events are now guaranteed to be fired *after* the children + were added by the FormBuilder (unless setData() is called manually). Before, + the *_SET_DATA events were sometimes thrown before adding child forms, + which made it impossible to remove child forms dynamically. + + A consequence of this change is that you need to set the "auto_initialize" + option to `false` for `FormInterface` instances that you pass to + `FormInterface::add()`: + + Before: + + ``` + $form = $factory->create('form'); + $form->add($factory->createNamed('field', 'text')); + ``` + + This code will now throw an exception with the following message: + + Automatic initialization is only supported on root forms. You should set the + "auto_initialize" option to false on the field "field". + + Consequently, you need to set the "auto_initialize" option: + + After (Alternative 1): + + ``` + $form = $factory->create('form'); + $form->add($factory->createNamed('field', 'text', array( + 'auto_initialize' => false, + ))); + ``` + + The problem also disappears if you work with `FormBuilder` instances instead + of `Form` instances: + + After (Alternative 2): + + ``` + $builder = $factory->createBuilder('form'); + $builder->add($factory->createBuilder('field', 'text')); + $form = $builder->getForm(); + ``` + + The best solution is in most cases to let `add()` handle the field creation: + + After (Alternative 3): + + ``` + $form = $factory->create('form'); + $form->add('field', 'text'); + ``` + + After (Alternative 4): + + ``` + $builder = $factory->createBuilder('form'); + $builder->add('field', 'text'); + $form = $builder->getForm(); + ``` + PropertyAccess -------------- diff --git a/src/Symfony/Component/Form/Button.php b/src/Symfony/Component/Form/Button.php index 8c5247d45f..7d75ecaa5a 100644 --- a/src/Symfony/Component/Form/Button.php +++ b/src/Symfony/Component/Form/Button.php @@ -343,6 +343,16 @@ class Button implements \IteratorAggregate, FormInterface return true; } + /** + * Unsupported method. + * + * @throws BadMethodCallException + */ + public function initialize() + { + throw new BadMethodCallException('Buttons cannot be initialized. Call initialized() on the root form instead.'); + } + /** * Unsupported method. * @@ -352,7 +362,7 @@ class Button implements \IteratorAggregate, FormInterface */ public function handleRequest($request = null) { - throw new BadMethodCallException('Buttons cannot be processed. Call process() on the root form instead.'); + throw new BadMethodCallException('Buttons cannot handle requests. Call handleRequest() on the root form instead.'); } /** diff --git a/src/Symfony/Component/Form/ButtonBuilder.php b/src/Symfony/Component/Form/ButtonBuilder.php index 3d6b840299..1c8e9244a2 100644 --- a/src/Symfony/Component/Form/ButtonBuilder.php +++ b/src/Symfony/Component/Form/ButtonBuilder.php @@ -492,6 +492,22 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface throw new BadMethodCallException('Buttons do not support form processors.'); } + /** + * Unsupported method. + * + * @param Boolean $initialize + * + * @throws BadMethodCallException + */ + public function setAutoInitialize($initialize) + { + if (true === $initialize) { + throw new BadMethodCallException('Buttons do not support automatic initialization.'); + } + + return $this; + } + /** * Unsupported method. * @@ -771,6 +787,16 @@ class ButtonBuilder implements \IteratorAggregate, FormBuilderInterface return null; } + /** + * Unsupported method. + * + * @return null Always returns null. + */ + public function getAutoInitialize() + { + return null; + } + /** * Unsupported method. * diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 9a9f577a9b..88a5714053 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -25,6 +25,12 @@ CHANGELOG * deprecated bind() and isBound() in Form * deprecated AlreadyBoundException in favor of AlreadySubmittedException * added support for PATCH requests + * [BC BREAK] added initialize() to FormInterface + * [BC BREAK] added getAutoInitialize() to FormConfigInterface + * [BC BREAK] added setAutoInitialize() to FormConfigBuilderInterface + * [BC BREAK] initialization for Form instances added to a form tree must be manually disabled + * PRE_SET_DATA is now guaranteed to be called after children were added by the form builder, + unless FormInterface::setData() is called manually 2.2.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 190bb52cec..eb1897cee4 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -55,6 +55,7 @@ class FormType extends BaseType ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null) ->setMethod($options['method']) ->setAction($options['action']) + ->setAutoInitialize($options['auto_initialize']) ; if ($options['trim']) { @@ -188,6 +189,7 @@ class FormType extends BaseType // According to RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt) // section 4.2., empty URIs are considered same-document references 'action' => '', + 'auto_initialize' => true, )); $resolver->setAllowedTypes(array( diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 974168a9a7..35135274a7 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -451,6 +451,25 @@ class Form implements \IteratorAggregate, FormInterface return $this->extraData; } + /** + * {@inheritdoc} + */ + public function initialize() + { + if (null !== $this->parent) { + throw new RuntimeException('Only root forms should be initialized.'); + } + + // Guarantee that the *_SET_DATA events have been triggered once the + // form is initialized. This makes sure that dynamically added or + // removed fields are already visible after initialization. + if (!$this->defaultDataSet) { + $this->setData($this->config->getData()); + } + + return $this; + } + /** * {@inheritdoc} */ @@ -774,7 +793,11 @@ class Form implements \IteratorAggregate, FormInterface // * getViewData() is called // * setData() is called since the form is not initialized yet // * ... endless recursion ... - if (!$this->lockSetData && !$this->config->getInheritData()) { + // + // 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(); } @@ -787,18 +810,27 @@ class Form implements \IteratorAggregate, FormInterface throw new UnexpectedTypeException($type, 'string or Symfony\Component\Form\FormTypeInterface'); } + // Never initialize child forms automatically + $options['auto_initialize'] = false; + if (null === $type) { $child = $this->config->getFormFactory()->createForProperty($this->config->getDataClass(), $child, null, $options); } else { $child = $this->config->getFormFactory()->createNamed($child, $type, null, $options); } + } elseif ($child->getConfig()->getAutoInitialize()) { + throw new RuntimeException(sprintf( + 'Automatic initialization is only supported on root forms. You '. + 'should set the "auto_initialize" option to false on the field "%s".', + $child->getName() + )); } $this->children[$child->getName()] = $child; $child->setParent($this); - if (!$this->lockSetData && !$this->config->getInheritData()) { + if (!$this->lockSetData && $this->defaultDataSet && !$this->config->getInheritData()) { $childrenIterator = new InheritDataAwareIterator(array($child)); $childrenIterator = new \RecursiveIteratorIterator($childrenIterator); $this->config->getDataMapper()->mapDataToForms($viewData, $childrenIterator); diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 986f43c5ae..2caefe48be 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -26,7 +26,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB /** * The children of the form builder. * - * @var array + * @var FormBuilderInterface[] */ private $children = array(); @@ -220,7 +220,13 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB $form = new Form($this->getFormConfig()); foreach ($this->children as $child) { - $form->add($child->getForm()); + // Automatic initialization is only supported on root forms + $form->add($child->setAutoInitialize(false)->getForm()); + } + + if ($this->getAutoInitialize()) { + // Automatically initialize the form if it is configured so + $form->initialize(); } return $form; diff --git a/src/Symfony/Component/Form/FormConfigBuilder.php b/src/Symfony/Component/Form/FormConfigBuilder.php index b6e57f6201..dc9c8f038e 100644 --- a/src/Symfony/Component/Form/FormConfigBuilder.php +++ b/src/Symfony/Component/Form/FormConfigBuilder.php @@ -107,11 +107,6 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $dataMapper; - /** - * @var array - */ - private $validators = array(); - /** * @var Boolean */ @@ -172,6 +167,11 @@ class FormConfigBuilder implements FormConfigBuilderInterface */ private $requestHandler; + /** + * @var Boolean + */ + private $autoInitialize = false; + /** * @var array */ @@ -521,6 +521,14 @@ class FormConfigBuilder implements FormConfigBuilderInterface return $this->requestHandler; } + /** + * {@inheritdoc} + */ + public function getAutoInitialize() + { + return $this->autoInitialize; + } + /** * {@inheritdoc} */ @@ -843,6 +851,16 @@ class FormConfigBuilder implements FormConfigBuilderInterface return $this; } + /** + * {@inheritdoc} + */ + public function setAutoInitialize($initialize) + { + $this->autoInitialize = (Boolean) $initialize; + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/FormConfigBuilderInterface.php b/src/Symfony/Component/Form/FormConfigBuilderInterface.php index 60a8d11116..62d12c09cb 100644 --- a/src/Symfony/Component/Form/FormConfigBuilderInterface.php +++ b/src/Symfony/Component/Form/FormConfigBuilderInterface.php @@ -256,12 +256,28 @@ interface FormConfigBuilderInterface extends FormConfigInterface public function setMethod($method); /** + * Sets the request handler used by the form. + * * @param RequestHandlerInterface $requestHandler * * @return self The configuration object. */ public function setRequestHandler(RequestHandlerInterface $requestHandler); + /** + * Sets whether the form should be initialized automatically. + * + * Should be set to true only for root forms. + * + * @param Boolean $initialize True to initialize the form automatically, + * false to suppress automatic initialization. + * In the second case, you need to call + * {@link FormInterface::initialize()} manually. + * + * @return self The configuration object. + */ + public function setAutoInitialize($initialize); + /** * Builds and returns the form configuration. * diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php index 5e944775b8..576fcd8142 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -201,10 +201,20 @@ interface FormConfigInterface public function getMethod(); /** - * @return RequestHandlerInterface The form processor. + * Returns the request handler used by the form. + * + * @return RequestHandlerInterface The request handler. */ public function getRequestHandler(); + /** + * Returns whether the form should be initialized upon creation. + * + * @return Boolean Returns true if the form should be initialized + * when created, false otherwise. + */ + public function getAutoInitialize(); + /** * Returns all options passed during the construction of the form. * diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 1e35f788cb..5a852e864f 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -227,13 +227,23 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable public function isSynchronized(); /** - * Processes the given request and calls {@link submit()} if it was submitted. + * Initializes the form tree. * - * Internally, the request is forwarded to a {@link RequestHandlerInterface} - * instance. This instance determines the allowed value of the - * $request parameter. + * Should be called on the root form after constructing the tree. * - * @param mixed $request The request to check. + * @return FormInterface The form instance. + */ + public function initialize(); + + /** + * Inspects the given request and calls {@link submit()} if the form was + * submitted. + * + * Internally, the request is forwarded to the configured + * {@link RequestHandlerInterface} instance, which determines whether to + * submit the form or not. + * + * @param mixed $request The request to handle. * * @return FormInterface The form instance. */ diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index 23cecaee94..ee9ed8f2a6 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -143,18 +143,16 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest { $child1 = $this->factory->createNamedBuilder('child1', 'form') ->add('field1', 'text') - ->add('field2', 'text') - ->getForm(); + ->add('field2', 'text'); $child2 = $this->factory->createNamedBuilder('child2', 'form') ->add('field1', 'text') - ->add('field2', 'text') - ->getForm(); + ->add('field2', 'text'); $view = $this->factory->createNamedBuilder('parent', 'form') - ->getForm() ->add($child1) ->add($child2) + ->getForm() ->createView(); // Render child1.field1 row @@ -627,13 +625,12 @@ abstract class AbstractDivLayoutTest extends AbstractLayoutTest public function testThemeInheritance($parentTheme, $childTheme) { $child = $this->factory->createNamedBuilder('child', 'form') - ->add('field', 'text') - ->getForm(); + ->add('field', 'text'); $view = $this->factory->createNamedBuilder('parent', 'form') ->add('field', 'text') - ->getForm() ->add($child) + ->getForm() ->createView() ; diff --git a/src/Symfony/Component/Form/Tests/AbstractFormTest.php b/src/Symfony/Component/Form/Tests/AbstractFormTest.php index 42543b63cf..2911818752 100644 --- a/src/Symfony/Component/Form/Tests/AbstractFormTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractFormTest.php @@ -77,10 +77,14 @@ abstract class AbstractFormTest extends \PHPUnit_Framework_TestCase protected function getMockForm($name = 'name') { $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $config = $this->getMock('Symfony\Component\Form\FormConfigInterface'); $form->expects($this->any()) ->method('getName') ->will($this->returnValue($name)); + $form->expects($this->any()) + ->method('getConfig') + ->will($this->returnValue($config)); return $form; } diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 8049b85013..1a48fae194 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -1152,9 +1152,10 @@ abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormInteg public function testDateErrorBubbling() { - $child = $this->factory->createNamed('date', 'date'); - $form = $this->factory->createNamed('form', 'form')->add($child); - $child->addError(new FormError('[trans]Error![/trans]')); + $form = $this->factory->createNamedBuilder('form', 'form') + ->add('date', 'date') + ->getForm(); + $form->get('date')->addError(new FormError('[trans]Error![/trans]')); $view = $form->createView(); $this->assertEmpty($this->renderErrors($view)); @@ -1678,9 +1679,10 @@ abstract class AbstractLayoutTest extends \Symfony\Component\Form\Test\FormInteg public function testTimeErrorBubbling() { - $child = $this->factory->createNamed('time', 'time'); - $form = $this->factory->createNamed('form', 'form')->add($child); - $child->addError(new FormError('[trans]Error![/trans]')); + $form = $this->factory->createNamedBuilder('form', 'form') + ->add('time', 'time') + ->getForm(); + $form->get('time')->addError(new FormError('[trans]Error![/trans]')); $view = $form->createView(); $this->assertEmpty($this->renderErrors($view)); diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index fdd1a0e9b7..b240d2d0b3 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -154,7 +154,10 @@ class CompoundFormTest extends AbstractFormTest $this->factory->expects($this->once()) ->method('createNamed') - ->with('foo', 'text', null, array('bar' => 'baz')) + ->with('foo', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) ->will($this->returnValue($child)); $this->form->add('foo', 'text', array('bar' => 'baz')); @@ -170,7 +173,10 @@ class CompoundFormTest extends AbstractFormTest $this->factory->expects($this->once()) ->method('createNamed') - ->with('0', 'text', null, array('bar' => 'baz')) + ->with('0', 'text', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) ->will($this->returnValue($child)); // in order to make casting unnecessary @@ -213,7 +219,10 @@ class CompoundFormTest extends AbstractFormTest $this->factory->expects($this->once()) ->method('createForProperty') - ->with('\stdClass', 'foo', null, array('bar' => 'baz')) + ->with('\stdClass', 'foo', null, array( + 'bar' => 'baz', + 'auto_initialize' => false, + )) ->will($this->returnValue($child)); $this->form->add('foo', null, array('bar' => 'baz')); @@ -287,7 +296,7 @@ class CompoundFormTest extends AbstractFormTest $this->assertSame($this->form->all(), iterator_to_array($this->form)); } - public function testAddMapsViewDataToForm() + public function testAddMapsViewDataToFormIfInitialized() { $test = $this; $mapper = $this->getDataMapper(); @@ -310,6 +319,22 @@ class CompoundFormTest extends AbstractFormTest $test->assertSame(array($child), iterator_to_array($iterator)); })); + $form->initialize(); + $form->add($child); + } + + public function testAddDoesNotMapViewDataToFormIfNotInitialized() + { + $mapper = $this->getDataMapper(); + $form = $this->getBuilder() + ->setCompound(true) + ->setDataMapper($mapper) + ->getForm(); + + $child = $this->getBuilder()->getForm(); + $mapper->expects($this->never()) + ->method('mapDataToForms'); + $form->add($child); } @@ -326,6 +351,7 @@ class CompoundFormTest extends AbstractFormTest $mapper->expects($this->never()) ->method('mapDataToForms'); + $form->initialize(); $form->add($child); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php index d77531ede4..1367b3ef02 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -72,11 +72,11 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase $this->factory->expects($this->at(0)) ->method('createNamed') - ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10)) + ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10, 'auto_initialize' => false)) ->will($this->returnValue($this->getForm('1'))); $this->factory->expects($this->at(1)) ->method('createNamed') - ->with(2, 'text', null, array('property_path' => '[2]', 'max_length' => 10)) + ->with(2, 'text', null, array('property_path' => '[2]', 'max_length' => 10, 'auto_initialize' => false)) ->will($this->returnValue($this->getForm('2'))); $data = array(1 => 'string', 2 => 'string'); @@ -116,7 +116,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase $this->factory->expects($this->once()) ->method('createNamed') - ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10)) + ->with(1, 'text', null, array('property_path' => '[1]', 'max_length' => 10, 'auto_initialize' => false)) ->will($this->returnValue($this->getForm('1'))); $data = array(0 => 'string', 1 => 'string'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php index 34a54ac95a..ef36f1c052 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/BaseTypeTest.php @@ -25,8 +25,8 @@ abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testPassIdAndNameToView() { - $form = $this->factory->createNamed('name', $this->getTestedType()); - $view = $form->createView(); + $view = $this->factory->createNamed('name', $this->getTestedType()) + ->createView(); $this->assertEquals('name', $view->vars['id']); $this->assertEquals('name', $view->vars['name']); @@ -35,8 +35,8 @@ abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testStripLeadingUnderscoresAndDigitsFromId() { - $form = $this->factory->createNamed('_09name', $this->getTestedType()); - $view = $form->createView(); + $view = $this->factory->createNamed('_09name', $this->getTestedType()) + ->createView(); $this->assertEquals('name', $view->vars['id']); $this->assertEquals('_09name', $view->vars['name']); @@ -45,9 +45,10 @@ abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testPassIdAndNameToViewWithParent() { - $parent = $this->factory->createNamed('parent', 'form'); - $parent->add($this->factory->createNamed('child', $this->getTestedType())); - $view = $parent->createView(); + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); $this->assertEquals('parent_child', $view['child']->vars['id']); $this->assertEquals('child', $view['child']->vars['name']); @@ -56,10 +57,10 @@ abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testPassIdAndNameToViewWithGrandParent() { - $parent = $this->factory->createNamed('parent', 'form'); - $parent->add($this->factory->createNamed('child', 'form')); - $parent['child']->add($this->factory->createNamed('grand_child', $this->getTestedType())); - $view = $parent->createView(); + $builder = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form'); + $builder->get('child')->add('grand_child', $this->getTestedType()); + $view = $builder->getForm()->createView(); $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->vars['id']); $this->assertEquals('grand_child', $view['child']['grand_child']->vars['name']); @@ -78,33 +79,38 @@ abstract class BaseTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testInheritTranslationDomainFromParent() { - $parent = $this->factory->createNamed('parent', 'form', null, array( - 'translation_domain' => 'domain', - )); - $child = $this->factory->createNamed('child', $this->getTestedType()); - $view = $parent->add($child)->createView(); + $view = $this->factory + ->createNamedBuilder('parent', 'form', null, array( + 'translation_domain' => 'domain', + )) + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); $this->assertEquals('domain', $view['child']->vars['translation_domain']); } public function testPreferOwnTranslationDomain() { - $parent = $this->factory->createNamed('parent', 'form', null, array( - 'translation_domain' => 'parent_domain', - )); - $child = $this->factory->createNamed('child', $this->getTestedType(), null, array( - 'translation_domain' => 'domain', - )); - $view = $parent->add($child)->createView(); + $view = $this->factory + ->createNamedBuilder('parent', 'form', null, array( + 'translation_domain' => 'parent_domain', + )) + ->add('child', $this->getTestedType(), array( + 'translation_domain' => 'domain', + )) + ->getForm() + ->createView(); $this->assertEquals('domain', $view['child']->vars['translation_domain']); } public function testDefaultTranslationDomain() { - $parent = $this->factory->createNamed('parent', 'form'); - $child = $this->factory->createNamed('child', $this->getTestedType()); - $view = $parent->add($child)->createView(); + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', $this->getTestedType()) + ->getForm() + ->createView(); $this->assertEquals('messages', $view['child']->vars['translation_domain']); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php index 3bc648a3d8..cced82f4cf 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -100,29 +100,32 @@ class FormTypeTest extends BaseTypeTest $this->assertEquals('reverse[ a ]', $form->getData()); } - public function testNonReadOnlyFormWithReadOnlyParentBeingReadOnly() + public function testNonReadOnlyFormWithReadOnlyParentIsReadOnly() { - $parent = $this->factory->createNamed('parent', 'form', null, array('read_only' => true)); - $child = $this->factory->createNamed('child', 'form'); - $view = $parent->add($child)->createView(); + $view = $this->factory->createNamedBuilder('parent', 'form', null, array('read_only' => true)) + ->add('child', 'form') + ->getForm() + ->createView(); $this->assertTrue($view['child']->vars['read_only']); } - public function testReadOnlyFormWithNonReadOnlyParentBeingReadOnly() + public function testReadOnlyFormWithNonReadOnlyParentIsReadOnly() { - $parent = $this->factory->createNamed('parent', 'form'); - $child = $this->factory->createNamed('child', 'form', null, array('read_only' => true)); - $view = $parent->add($child)->createView(); + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form', array('read_only' => true)) + ->getForm() + ->createView(); $this->assertTrue($view['child']->vars['read_only']); } - public function testNonReadOnlyFormWithNonReadOnlyParentBeingNonReadOnly() + public function testNonReadOnlyFormWithNonReadOnlyParentIsNotReadOnly() { - $parent = $this->factory->createNamed('parent', 'form'); - $child = $this->factory->createNamed('child', 'form'); - $view = $parent->add($child)->createView(); + $view = $this->factory->createNamedBuilder('parent', 'form') + ->add('child', 'form') + ->getForm() + ->createView(); $this->assertFalse($view['child']->vars['read_only']); } @@ -137,12 +140,13 @@ class FormTypeTest extends BaseTypeTest public function testSubmitWithEmptyDataCreatesObjectIfClassAvailable() { - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', 'required' => false, )); - $form->add($this->factory->createNamed('firstName', 'text')); - $form->add($this->factory->createNamed('lastName', 'text')); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); $form->setData(null); // partially empty, still an object is created @@ -157,13 +161,14 @@ class FormTypeTest extends BaseTypeTest public function testSubmitWithEmptyDataCreatesObjectIfInitiallySubmittedWithObject() { - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( // data class is inferred from the passed object 'data' => new Author(), 'required' => false, )); - $form->add($this->factory->createNamed('firstName', 'text')); - $form->add($this->factory->createNamed('lastName', 'text')); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); $form->setData(null); // partially empty, still an object is created @@ -178,11 +183,12 @@ class FormTypeTest extends BaseTypeTest public function testSubmitWithEmptyDataCreatesArrayIfDataClassIsNull() { - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'data_class' => null, 'required' => false, )); - $form->add($this->factory->createNamed('firstName', 'text')); + $builder->add('firstName', 'text'); + $form = $builder->getForm(); $form->setData(null); $form->submit(array('firstName' => 'Bernhard')); @@ -192,12 +198,13 @@ class FormTypeTest extends BaseTypeTest public function testSubmitEmptyWithEmptyDataCreatesNoObjectIfNotRequired() { - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', 'required' => false, )); - $form->add($this->factory->createNamed('firstName', 'text')); - $form->add($this->factory->createNamed('lastName', 'text')); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); $form->setData(null); $form->submit(array('firstName' => '', 'lastName' => '')); @@ -207,12 +214,13 @@ class FormTypeTest extends BaseTypeTest public function testSubmitEmptyWithEmptyDataCreatesObjectIfRequired() { - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', 'required' => true, )); - $form->add($this->factory->createNamed('firstName', 'text')); - $form->add($this->factory->createNamed('lastName', 'text')); + $builder->add('firstName', 'text'); + $builder->add('lastName', 'text'); + $form = $builder->getForm(); $form->setData(null); $form->submit(array('firstName' => '', 'lastName' => '')); @@ -225,8 +233,9 @@ class FormTypeTest extends BaseTypeTest */ public function testSubmitWithEmptyDataStoresArrayIfNoClassAvailable() { - $form = $this->factory->create('form'); - $form->add($this->factory->createNamed('firstName', 'text')); + $form = $this->factory->createBuilder('form') + ->add('firstName', 'text') + ->getForm(); $form->setData(null); $form->submit(array('firstName' => 'Bernhard')); @@ -255,11 +264,12 @@ class FormTypeTest extends BaseTypeTest { $author = new Author(); - $form = $this->factory->create('form', null, array( + $builder = $this->factory->createBuilder('form', null, array( 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', 'empty_data' => $author, )); - $form->add($this->factory->createNamed('firstName', 'text')); + $builder->add('firstName', 'text'); + $form = $builder->getForm(); $form->submit(array('firstName' => 'Bernhard')); @@ -430,19 +440,21 @@ class FormTypeTest extends BaseTypeTest public function testPassMultipartTrueIfAnyChildIsMultipartToView() { - $form = $this->factory->create('form'); - $form->add($this->factory->create('text')); - $form->add($this->factory->create('file')); - $view = $form->createView(); + $view = $this->factory->createBuilder('form') + ->add('foo', 'text') + ->add('bar', 'file') + ->getForm() + ->createView(); $this->assertTrue($view->vars['multipart']); } - public function testCreateViewDoNoMarkItAsRendered() + public function testViewIsNotRenderedByDefault() { - $form = $this->factory->create('form'); - $form->add($this->factory->create('form')); - $view = $form->createView(); + $view = $this->factory->createBuilder('form') + ->add('foo', 'form') + ->getForm() + ->createView(); $this->assertFalse($view->isRendered()); } diff --git a/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php b/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php index e53d19ffe9..ee7d135339 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php +++ b/src/Symfony/Component/Form/Tests/Fixtures/AlternatingRowType.php @@ -16,7 +16,7 @@ class AlternatingRowType extends AbstractType $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) { $form = $event->getForm(); $type = $form->getName() % 2 === 0 ? 'text' : 'textarea'; - $form->add($formFactory->createNamed('title', $type)); + $form->add('title', $type); }); } diff --git a/src/Symfony/Component/Form/Tests/SimpleFormTest.php b/src/Symfony/Component/Form/Tests/SimpleFormTest.php index 79232df7d0..bedad6761f 100644 --- a/src/Symfony/Component/Form/Tests/SimpleFormTest.php +++ b/src/Symfony/Component/Form/Tests/SimpleFormTest.php @@ -902,13 +902,13 @@ class SimpleFormTest extends AbstractFormTest public function testHandleRequestForwardsToRequestHandler() { - $processor = $this->getMock('Symfony\Component\Form\RequestHandlerInterface'); + $handler = $this->getMock('Symfony\Component\Form\RequestHandlerInterface'); $form = $this->getBuilder() - ->setRequestHandler($processor) + ->setRequestHandler($handler) ->getForm(); - $processor->expects($this->once()) + $handler->expects($this->once()) ->method('handleRequest') ->with($this->identicalTo($form), 'REQUEST'); @@ -1012,6 +1012,32 @@ class SimpleFormTest extends AbstractFormTest $form->submit('foo'); } + public function testInitializeSetsDefaultData() + { + $config = $this->getBuilder()->setData('DEFAULT')->getFormConfig(); + $form = $this->getMock('Symfony\Component\Form\Form', array('setData'), array($config)); + + $form->expects($this->once()) + ->method('setData') + ->with($this->identicalTo('DEFAULT')); + + /* @var Form $form */ + $form->initialize(); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\RuntimeException + */ + public function testInitializeFailsIfParent() + { + $parent = $this->getBuilder()->setRequired(false)->getForm(); + $child = $this->getBuilder()->setRequired(true)->getForm(); + + $child->setParent($parent); + + $child->initialize(); + } + protected function createForm() { return $this->getBuilder()->getForm();