From 860dd1f7d839dc75607cbe3027ba6c4bd19afacf Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 17 May 2012 16:09:13 +0200 Subject: [PATCH] [Form] Adapted Form to create a deterministic property path by default --- .../Core/DataMapper/PropertyPathMapper.php | 20 +--- .../Form/Extension/Core/Type/FormType.php | 18 +--- src/Symfony/Component/Form/Form.php | 60 +++++++++++- src/Symfony/Component/Form/FormBuilder.php | 20 ++-- src/Symfony/Component/Form/FormConfig.php | 92 ++++++++++++++++++- .../Component/Form/FormConfigInterface.php | 22 +++++ src/Symfony/Component/Form/FormFactory.php | 1 - src/Symfony/Component/Form/FormInterface.php | 21 +++-- .../Component/Form/ImmutableFormConfig.php | 44 ++++++++- .../DataMapper/PropertyPathMapperTest.php | 5 +- ...MergeCollectionListenerArrayObjectTest.php | 7 ++ .../MergeCollectionListenerArrayTest.php | 7 ++ ...ollectionListenerCustomArrayObjectTest.php | 6 ++ .../MergeCollectionListenerTest.php | 11 +-- .../EventListener/ResizeFormListenerTest.php | 2 +- .../Extension/Core/Type/FormTypeTest.php | 86 ++++++----------- .../Extension/Core/Type/TypeTestCase.php | 11 ++- .../DelegatingValidationListenerTest.php | 22 ++--- .../Component/Form/Tests/Fixtures/FooType.php | 2 +- .../Component/Form/Tests/FormBuilderTest.php | 14 +-- .../Component/Form/Tests/FormConfigTest.php | 2 +- .../Component/Form/Tests/FormFactoryTest.php | 2 +- src/Symfony/Component/Form/Tests/FormTest.php | 36 +++++++- 23 files changed, 355 insertions(+), 156 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php index f8ccaaa2c9..f8f2984d4a 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php @@ -18,18 +18,6 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException; class PropertyPathMapper implements DataMapperInterface { - /** - * Stores the class that the data of this form must be instances of. - * - * @var string - */ - private $dataClass; - - public function __construct($dataClass = null) - { - $this->dataClass = $dataClass; - } - /** * {@inheritdoc} */ @@ -40,10 +28,6 @@ class PropertyPathMapper implements DataMapperInterface } if (!empty($data)) { - if (null !== $this->dataClass && !$data instanceof $this->dataClass) { - throw new UnexpectedTypeException($data, $this->dataClass); - } - $iterator = new VirtualFormAwareIterator($forms); $iterator = new \RecursiveIteratorIterator($iterator); @@ -59,7 +43,7 @@ class PropertyPathMapper implements DataMapperInterface public function mapDataToForm($data, FormInterface $form) { if (!empty($data)) { - $propertyPath = $form->getAttribute('property_path'); + $propertyPath = $form->getPropertyPath(); if (null !== $propertyPath) { $propertyData = $propertyPath->getValue($data); @@ -91,7 +75,7 @@ class PropertyPathMapper implements DataMapperInterface */ public function mapFormToData(FormInterface $form, &$data) { - $propertyPath = $form->getAttribute('property_path'); + $propertyPath = $form->getPropertyPath(); if (null !== $propertyPath && $form->isSynchronized() && !$form->isDisabled()) { // If the data is identical to the value in $data, we are diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index a00524af19..2bed666237 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -31,16 +31,6 @@ class FormType extends AbstractType */ public function buildForm(FormBuilder $builder, array $options) { - if (null === $options['property_path']) { - $options['property_path'] = $builder->getName(); - } - - if (false === $options['property_path'] || '' === $options['property_path']) { - $options['property_path'] = null; - } else { - $options['property_path'] = new PropertyPath($options['property_path']); - } - if (!is_array($options['attr'])) { throw new FormException('The "attr" option must be an "array".'); } @@ -54,9 +44,9 @@ class FormType extends AbstractType ->setDisabled($options['disabled']) ->setErrorBubbling($options['error_bubbling']) ->setEmptyData($options['empty_data']) + ->setPropertyPath($options['property_path']) ->setAttribute('read_only', $options['read_only']) ->setAttribute('by_reference', $options['by_reference']) - ->setAttribute('property_path', $options['property_path']) ->setAttribute('error_mapping', $options['error_mapping']) ->setAttribute('max_length', $options['max_length']) ->setAttribute('pattern', $options['pattern']) @@ -69,7 +59,7 @@ class FormType extends AbstractType ->setAttribute('virtual', $options['virtual']) ->setAttribute('single_control', $options['single_control']) ->setData($options['data']) - ->setDataMapper(new PropertyPathMapper($options['data_class'])) + ->setDataMapper(new PropertyPathMapper()) ->addEventSubscriber(new ValidationListener()) ; @@ -112,7 +102,7 @@ class FormType extends AbstractType } $types = array(); - foreach ($form->getTypes() as $type) { + foreach ($form->getConfig()->getTypes() as $type) { $types[] = $type->getName(); } @@ -229,7 +219,7 @@ class FormType extends AbstractType */ public function createBuilder($name, FormFactoryInterface $factory, array $options) { - return new FormBuilder($name, $factory, new EventDispatcher(), $options['data_class']); + return new FormBuilder($name, $options['data_class'], new EventDispatcher(), $factory); } /** diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 92d0532870..2401c61230 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -17,6 +17,7 @@ use Symfony\Component\Form\Exception\FormException; use Symfony\Component\Form\Exception\AlreadyBoundException; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Exception\TransformationFailedException; +use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -162,10 +163,33 @@ class Form implements \IteratorAggregate, FormInterface return $this->config->getName(); } + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + if (!$this->hasParent() || null !== $this->config->getPropertyPath()) { + return $this->config->getPropertyPath(); + } + + if (null === $this->getName() || '' === $this->getName()) { + return null; + } + + if (null === $this->getParent()->getConfig()->getDataClass()) { + return new PropertyPath('[' . $this->getName() . ']'); + } + + return new PropertyPath($this->getName()); + } + /** * Returns the types used by this form. * * @return array An array of FormTypeInterface + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::getTypes()} instead. */ public function getTypes() { @@ -261,9 +285,9 @@ class Form implements \IteratorAggregate, FormInterface /** * Returns whether the form has an attribute with the given name. * - * @param string $name The name of the attribute + * @param string $name The name of the attribute. * - * @return Boolean + * @return Boolean Whether the attribute exists. */ public function hasAttribute($name) { @@ -273,7 +297,9 @@ class Form implements \IteratorAggregate, FormInterface /** * Returns the value of the attributes with the given name. * - * @param string $name The name of the attribute + * @param string $name The name of the attribute + * + * @return mixed The attribute value. */ public function getAttribute($name) { @@ -310,6 +336,25 @@ class Form implements \IteratorAggregate, FormInterface $normData = $this->appToNorm($appData); $clientData = $this->normToClient($normData); + // Validate if client data matches data class (unless empty) + if (!empty($clientData)) { + // TESTME + $dataClass = $this->config->getDataClass(); + + if (null === $dataClass && is_object($clientData)) { + // TODO clarify error message: should not be transformed to object if data class is null + // possible solutions: set data class or change client transformers + throw new UnexpectedTypeException($clientData, 'scalar or array'); + } + + // TESTME + if (null !== $dataClass && !$clientData instanceof $dataClass) { + // TODO clarify error message: should be transformed to object if data class is set + // possible solutions: change data class or client transformers + throw new UnexpectedTypeException($clientData, $dataClass); + } + } + $this->appData = $appData; $this->normData = $normData; $this->clientData = $clientData; @@ -568,6 +613,9 @@ class Form implements \IteratorAggregate, FormInterface * Returns whether errors bubble up to the parent. * * @return Boolean + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::getErrorBubbling()} instead. */ public function getErrorBubbling() { @@ -693,6 +741,9 @@ class Form implements \IteratorAggregate, FormInterface * Returns the DataTransformers. * * @return array An array of DataTransformerInterface + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::getNormTransformers()} instead. */ public function getNormTransformers() { @@ -703,6 +754,9 @@ class Form implements \IteratorAggregate, FormInterface * Returns the DataTransformers. * * @return array An array of DataTransformerInterface + * + * @deprecated Deprecated since version 2.1, to be removed in 2.3. Use + * {@link getConfig()} and {@link FormConfigInterface::getClientTransformers()} instead. */ public function getClientTransformers() { diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 6ee94014a5..6dafd341e7 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -31,11 +31,6 @@ class FormBuilder extends FormConfig */ private $factory; - /** - * @var string - */ - private $dataClass; - /** * The children of the form builder. * @@ -59,19 +54,18 @@ class FormBuilder extends FormConfig private $parent; /** - * Constructor. + * Creates a new form builder. * * @param string $name - * @param FormFactoryInterface $factory - * @param EventDispatcherInterface $dispatcher * @param string $dataClass + * @param EventDispatcherInterface $dispatcher + * @param FormFactoryInterface $factory */ - public function __construct($name, FormFactoryInterface $factory, EventDispatcherInterface $dispatcher, $dataClass = null) + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher, FormFactoryInterface $factory) { - parent::__construct($name, $dispatcher); + parent::__construct($name, $dataClass, $dispatcher); $this->factory = $factory; - $this->dataClass = $dataClass; } /** @@ -140,7 +134,7 @@ class FormBuilder extends FormConfig */ public function create($name, $type = null, array $options = array()) { - if (null === $type && !$this->dataClass) { + if (null === $type && null === $this->getDataClass()) { $type = 'text'; } @@ -148,7 +142,7 @@ class FormBuilder extends FormConfig return $this->getFormFactory()->createNamedBuilder($type, $name, null, $options, $this); } - return $this->getFormFactory()->createBuilderForProperty($this->dataClass, $name, null, $options, $this); + return $this->getFormFactory()->createBuilderForProperty($this->getDataClass(), $name, null, $options, $this); } /** diff --git a/src/Symfony/Component/Form/FormConfig.php b/src/Symfony/Component/Form/FormConfig.php index 7aa31f5d45..ac50e00ebd 100644 --- a/src/Symfony/Component/Form/FormConfig.php +++ b/src/Symfony/Component/Form/FormConfig.php @@ -33,6 +33,16 @@ class FormConfig implements FormConfigInterface */ private $name; + /** + * @var PropertyPath + */ + private $propertyPath; + + /** + * @var Boolean + */ + private $mapped; + /** * @var array */ @@ -88,19 +98,34 @@ class FormConfig implements FormConfigInterface */ private $data; + /** + * @var string + */ + private $dataClass; + /** * Creates an empty form configuration. * - * @param string $name The form name. - * @param EventDispatcherInterface $dispatcher The event dispatcher. + * @param string $name The form name. + * @param string $dataClass The class of the form's data. + * @param EventDispatcherInterface $dispatcher The event dispatcher. + * + * @throws UnexpectedTypeException If the name is not a string. + * @throws \InvalidArgumentException If the data class is not a valid class or if + * the name contains invalid characters. */ - public function __construct($name, EventDispatcherInterface $dispatcher) + public function __construct($name, $dataClass, EventDispatcherInterface $dispatcher) { $name = (string) $name; self::validateName($name); + if (null !== $dataClass && !class_exists($dataClass)) { + throw new \InvalidArgumentException(sprintf('The data class "%s" is not a valid class.', $dataClass)); + } + $this->name = $name; + $this->dataClass = $dataClass; $this->dispatcher = $dispatcher; } @@ -248,6 +273,22 @@ class FormConfig implements FormConfigInterface return $this->name; } + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + return $this->propertyPath; + } + + /** + * {@inheritdoc} + */ + public function getMapped() + { + return $this->mapped; + } + /** * {@inheritdoc} */ @@ -354,6 +395,14 @@ class FormConfig implements FormConfigInterface return $this->data; } + /** + * {@inheritdoc} + */ + public function getDataClass() + { + return $this->dataClass; + } + /** * Sets the value for an attribute. * @@ -453,6 +502,41 @@ class FormConfig implements FormConfigInterface return $this; } + /** + * Sets the property path that the form should be mapped to. + * + * @param string|PropertyPath $propertyPath The property path or null if the path + * should be set automatically based on + * the form's name. + * + * @return self The configuration object. + */ + public function setPropertyPath($propertyPath) + { + if (null !== $propertyPath && !$propertyPath instanceof PropertyPath) { + $propertyPath = new PropertyPath($propertyPath); + } + + $this->propertyPath = $propertyPath; + + return $this; + } + + /** + * Sets whether the form should be mapped to an element of its + * parent's data. + * + * @param Boolean $mapped Whether the form should be mapped. + * + * @return self The configuration object. + */ + public function setMapped($mapped) + { + $this->mapped = $mapped; + + return $this; + } + /** * Set the types. * @@ -486,7 +570,7 @@ class FormConfig implements FormConfigInterface * * @param string $name The tested form name. * - * @throws UnexpectedTypeException If the name is not a string. + * @throws UnexpectedTypeException If the name is not a string. * @throws \InvalidArgumentException If the name contains invalid characters. */ static public function validateName($name) diff --git a/src/Symfony/Component/Form/FormConfigInterface.php b/src/Symfony/Component/Form/FormConfigInterface.php index 363dae5575..36345a2863 100644 --- a/src/Symfony/Component/Form/FormConfigInterface.php +++ b/src/Symfony/Component/Form/FormConfigInterface.php @@ -32,6 +32,21 @@ interface FormConfigInterface */ function getName(); + /** + * Returns the property path that the form should be mapped to. + * + * @return Util\PropertyPath The property path. + */ + function getPropertyPath(); + + /** + * Returns whether the form should be mapped to an element of its + * parent's data. + * + * @return Boolean Whether the form is mapped. + */ + function getMapped(); + /** * Returns the form types used to construct the form. * @@ -128,4 +143,11 @@ interface FormConfigInterface * @return mixed The initial form data. */ function getData(); + + /** + * Returns the class of the form data or null if the data is scalar or an array. + * + * @return string The data class or null. + */ + function getDataClass(); } diff --git a/src/Symfony/Component/Form/FormFactory.php b/src/Symfony/Component/Form/FormFactory.php index f97c9536a1..4a5d13ace4 100644 --- a/src/Symfony/Component/Form/FormFactory.php +++ b/src/Symfony/Component/Form/FormFactory.php @@ -151,7 +151,6 @@ class FormFactory implements FormFactoryInterface $builder = null; $types = array(); - $optionValues = array(); $knownOptions = array(); $optionsResolver = new OptionsResolver(); diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index 7d2539e940..42e2a472a3 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -131,6 +131,13 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable */ function getExtraData(); + /** + * Returns the form's configuration. + * + * @return FormConfigInterface The configuration. + */ + function getConfig(); + /** * Returns whether the field is bound. * @@ -138,13 +145,6 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable */ function isBound(); - /** - * Returns the supported types. - * - * @return array An array of FormTypeInterface - */ - function getTypes(); - /** * Returns the name by which the form is identified in forms. * @@ -152,6 +152,13 @@ interface FormInterface extends \ArrayAccess, \Traversable, \Countable */ function getName(); + /** + * Returns the property path that the form is mapped to. + * + * @return Util\PropertyPath The property path. + */ + function getPropertyPath(); + /** * Adds an error to this form. * diff --git a/src/Symfony/Component/Form/ImmutableFormConfig.php b/src/Symfony/Component/Form/ImmutableFormConfig.php index e457322327..fbf739d3cb 100644 --- a/src/Symfony/Component/Form/ImmutableFormConfig.php +++ b/src/Symfony/Component/Form/ImmutableFormConfig.php @@ -31,6 +31,16 @@ class ImmutableFormConfig implements FormConfigInterface */ private $name; + /** + * @var PropertyPath + */ + private $propertyPath; + + /** + * @var Boolean + */ + private $mapped; + /** * @var array */ @@ -86,6 +96,11 @@ class ImmutableFormConfig implements FormConfigInterface */ private $data; + /** + * @var string + */ + private $dataClass; + /** * Creates an immutable copy of a given configuration. * @@ -95,6 +110,8 @@ class ImmutableFormConfig implements FormConfigInterface { $this->dispatcher = $config->getEventDispatcher(); $this->name = $config->getName(); + $this->propertyPath = $config->getPropertyPath(); + $this->mapped = $config->getMapped(); $this->types = $config->getTypes(); $this->clientTransformers = $config->getClientTransformers(); $this->normTransformers = $config->getNormTransformers(); @@ -104,8 +121,9 @@ class ImmutableFormConfig implements FormConfigInterface $this->disabled = $config->getDisabled(); $this->errorBubbling = $config->getErrorBubbling(); $this->emptyData = $config->getEmptyData(); - $this->data = $config->getData(); $this->attributes = $config->getAttributes(); + $this->data = $config->getData(); + $this->dataClass = $config->getDataClass(); } /** @@ -124,6 +142,22 @@ class ImmutableFormConfig implements FormConfigInterface return $this->name; } + /** + * {@inheritdoc} + */ + public function getPropertyPath() + { + return $this->propertyPath; + } + + /** + * {@inheritdoc} + */ + public function getMapped() + { + return $this->mapped; + } + /** * {@inheritdoc} */ @@ -229,4 +263,12 @@ class ImmutableFormConfig implements FormConfigInterface { return $this->data; } + + /** + * {@inheritdoc} + */ + public function getDataClass() + { + return $this->dataClass; + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php index 4708db0ece..13e14330c1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -74,9 +74,12 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase ->setMethods(array('foo')) ->getMockForAbstractClass(); - $form->setAttribute('property_path', $propertyPath); $form->setAttribute('by_reference', $byReference); + $form->expects($this->any()) + ->method('getPropertyPath') + ->will($this->returnValue($propertyPath)); + $form->expects($this->any()) ->method('isSynchronized') ->will($this->returnValue($synchronized)); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php index 295589a924..6f46c9d7fa 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayObjectTest.php @@ -11,10 +11,17 @@ namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; +use Symfony\Component\Form\FormBuilder; + class MergeCollectionListenerArrayObjectTest extends MergeCollectionListenerTest { protected function getData(array $data) { return new \ArrayObject($data); } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, '\ArrayObject', $this->dispatcher, $this->factory); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php index 1fe80c1b6a..c0f3d59734 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerArrayTest.php @@ -11,10 +11,17 @@ namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; +use Symfony\Component\Form\FormBuilder; + class MergeCollectionListenerArrayTest extends MergeCollectionListenerTest { protected function getData(array $data) { return $data; } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, null, $this->dispatcher, $this->factory); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php index 9557216aa8..5eb6c7b9fd 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerCustomArrayObjectTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\EventListener; use Symfony\Component\Form\Tests\Fixtures\CustomArrayObject; +use Symfony\Component\Form\FormBuilder; class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListenerTest { @@ -19,4 +20,9 @@ class MergeCollectionListenerCustomArrayObjectTest extends MergeCollectionListen { return new CustomArrayObject($data); } + + protected function getBuilder($name = 'name') + { + return new FormBuilder($name, 'Symfony\Component\Form\Tests\Fixtures\CustomArrayObject', $this->dispatcher, $this->factory); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php index d798f8d484..81982169c9 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/MergeCollectionListenerTest.php @@ -17,9 +17,9 @@ use Symfony\Component\Form\FormBuilder; abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase { - private $dispatcher; - private $factory; - private $form; + protected $dispatcher; + protected $factory; + protected $form; protected function setUp() { @@ -39,10 +39,7 @@ abstract class MergeCollectionListenerTest extends \PHPUnit_Framework_TestCase $this->form = null; } - protected function getBuilder($name = 'name') - { - return new FormBuilder($name, $this->factory, $this->dispatcher); - } + abstract protected function getBuilder($name = 'name'); protected function getForm($name = 'name', $propertyPath = null) { 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 8b2e703794..d6a306f5cb 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -42,7 +42,7 @@ class ResizeFormListenerTest extends \PHPUnit_Framework_TestCase protected function getBuilder($name = 'name') { - return new FormBuilder($name, $this->factory, $this->dispatcher); + return new FormBuilder($name, null, $this->dispatcher, $this->factory); } protected function getForm($name = 'name') 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 6df8130e38..b575b140d2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -51,41 +51,6 @@ class FormTest_AuthorWithoutRefSetter class FormTypeTest extends TypeTestCase { - public function testGetPropertyPathDefaultPath() - { - $form = $this->factory->createNamed('form', 'title'); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsZero() - { - $form = $this->factory->create('form', null, array('property_path' => '0')); - - $this->assertEquals(new PropertyPath('0'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsEmpty() - { - $form = $this->factory->create('form', null, array('property_path' => '')); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsFalse() - { - $form = $this->factory->create('form', null, array('property_path' => false)); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsNull() - { - $form = $this->factory->createNamed('form', 'title', null, array('property_path' => null)); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - public function testPassRequiredAsOption() { $form = $this->factory->create('form', null, array('required' => false)); @@ -285,10 +250,9 @@ class FormTypeTest extends TypeTestCase $this->assertEquals($author, $form->getData()); } - public function testBindWithEmptyDataDoesNotCreateObjectIfDataClassIsNull() + public function testBindWithEmptyDataCreatesArrayIfDataClassIsNull() { $form = $this->factory->create('form', null, array( - 'data' => new Author(), 'data_class' => null, 'required' => false, )); @@ -365,6 +329,7 @@ class FormTypeTest extends TypeTestCase $author = new Author(); $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', 'empty_data' => $author, )); $form->add($this->factory->createNamed('form', 'firstName')); @@ -430,10 +395,11 @@ class FormTypeTest extends TypeTestCase { $author = new FormTest_AuthorWithoutRefSetter(new Author()); - $builder = $this->factory->createBuilder('form'); - $builder->add('reference', 'form'); + $builder = $this->factory->createBuilder('form', $author); + $builder->add('reference', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); $builder->get('reference')->add('firstName', 'form'); - $builder->setData($author); $form = $builder->getForm(); $form->bind(array( @@ -452,10 +418,11 @@ class FormTypeTest extends TypeTestCase $author = new FormTest_AuthorWithoutRefSetter(null); $newReference = new Author(); - $builder = $this->factory->createBuilder('form'); - $builder->add('referenceCopy', 'form'); + $builder = $this->factory->createBuilder('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + )); $builder->get('referenceCopy')->add('firstName', 'form'); - $builder->setData($author); $form = $builder->getForm(); $form['referenceCopy']->setData($newReference); // new author object @@ -474,17 +441,19 @@ class FormTypeTest extends TypeTestCase { $author = new FormTest_AuthorWithoutRefSetter(new Author()); - $builder = $this->factory->createBuilder('form'); - $builder->add('referenceCopy', 'form', array('by_reference' => false)); + $builder = $this->factory->createBuilder('form', $author); + $builder->add('referenceCopy', 'form', array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'by_reference' => false + )); $builder->get('referenceCopy')->add('firstName', 'form'); - $builder->setData($author); $form = $builder->getForm(); $form->bind(array( - // referenceCopy has a getter that returns a copy + // referenceCopy has a getter that returns a copy 'referenceCopy' => array( 'firstName' => 'Foo', - ) + ) )); // firstName can only be updated if setReferenceCopy() was called @@ -495,16 +464,14 @@ class FormTypeTest extends TypeTestCase { $author = new FormTest_AuthorWithoutRefSetter('scalar'); - $builder = $this->factory->createBuilder('form'); + $builder = $this->factory->createBuilder('form', $author); $builder->add('referenceCopy', 'form'); $builder->get('referenceCopy')->appendClientTransformer(new CallbackTransformer( - function () {}, - function ($value) { // reverseTransform - - return 'foobar'; - } + function () {}, + function ($value) { // reverseTransform + return 'foobar'; + } )); - $builder->setData($author); $form = $builder->getForm(); $form->bind(array( @@ -525,11 +492,10 @@ class FormTypeTest extends TypeTestCase $builder->setData($author); $builder->add('referenceCopy', 'form'); $builder->get('referenceCopy')->appendClientTransformer(new CallbackTransformer( - function () {}, - function ($value) use ($ref2) { // reverseTransform - - return $ref2; - } + function () {}, + function ($value) use ($ref2) { // reverseTransform + return $ref2; + } )); $form = $builder->getForm(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php index 8545b1ac57..6acda436c4 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TypeTestCase.php @@ -18,10 +18,19 @@ use Symfony\Component\EventDispatcher\EventDispatcher; abstract class TypeTestCase extends \PHPUnit_Framework_TestCase { + /** + * @var FormFactory + */ protected $factory; + /** + * @var FormBuilder + */ protected $builder; + /** + * @var EventDispatcher + */ protected $dispatcher; protected function setUp() @@ -32,7 +41,7 @@ abstract class TypeTestCase extends \PHPUnit_Framework_TestCase $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); $this->factory = new FormFactory($this->getExtensions()); - $this->builder = new FormBuilder(null, $this->factory, $this->dispatcher); + $this->builder = new FormBuilder(null, null, $this->dispatcher, $this->factory); } protected function tearDown() diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php index 187ce1a2b7..8c7d00a017 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php @@ -88,9 +88,9 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase return new FormError($this->message, $this->params); } - protected function getBuilder($name = 'name', $propertyPath = null) + protected function getBuilder($name = 'name', $propertyPath = null, $dataClass = null) { - $builder = new FormBuilder($name, $this->factory, $this->dispatcher); + $builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory); $builder->setAttribute('property_path', new PropertyPath($propertyPath ?: $name)); $builder->setAttribute('error_mapping', array()); $builder->setErrorBubbling(false); @@ -98,9 +98,9 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase return $builder; } - protected function getForm($name = 'name', $propertyPath = null) + protected function getForm($name = 'name', $propertyPath = null, $dataClass = null) { - return $this->getBuilder($name, $propertyPath)->getForm(); + return $this->getBuilder($name, $propertyPath, $dataClass)->getForm(); } protected function getMockForm() @@ -608,7 +608,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder() + $form = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', array('group1', 'group2')) ->getForm(); @@ -629,7 +629,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder() + $form = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', array($this, 'getValidationGroups')) ->getForm(); @@ -650,7 +650,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext(); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getBuilder() + $form = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', function(FormInterface $form){ return array('group1', 'group2'); }) @@ -677,7 +677,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $parent = $this->getBuilder() ->setAttribute('validation_groups', 'group') ->getForm(); - $child = $this->getBuilder() + $child = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', null) ->getForm(); $parent->add($child); @@ -700,7 +700,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $parent = $this->getBuilder() ->setAttribute('validation_groups', array($this, 'getValidationGroups')) ->getForm(); - $child = $this->getBuilder() + $child = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', null) ->getForm(); $parent->add($child); @@ -728,7 +728,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase return array('group1', 'group2'); }) ->getForm(); - $child = $this->getBuilder() + $child = $this->getBuilder('name', null, '\stdClass') ->setAttribute('validation_groups', null) ->getForm(); $parent->add($child); @@ -750,7 +750,7 @@ class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase $context = $this->getExecutionContext('foo.bar'); $graphWalker = $context->getGraphWalker(); $object = $this->getMock('\stdClass'); - $form = $this->getForm(); + $form = $this->getForm('name', null, '\stdClass'); $graphWalker->expects($this->once()) ->method('walkReference') diff --git a/src/Symfony/Component/Form/Tests/Fixtures/FooType.php b/src/Symfony/Component/Form/Tests/Fixtures/FooType.php index d85ae2c72a..7c8b0dd964 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/FooType.php +++ b/src/Symfony/Component/Form/Tests/Fixtures/FooType.php @@ -31,7 +31,7 @@ class FooType extends AbstractType public function createBuilder($name, FormFactoryInterface $factory, array $options) { - return new FormBuilder($name, $factory, new EventDispatcher()); + return new FormBuilder($name, null, new EventDispatcher(), $factory); } public function getDefaultOptions() diff --git a/src/Symfony/Component/Form/Tests/FormBuilderTest.php b/src/Symfony/Component/Form/Tests/FormBuilderTest.php index f21ccd0e14..68db15fd40 100644 --- a/src/Symfony/Component/Form/Tests/FormBuilderTest.php +++ b/src/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -25,7 +25,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase { $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); $this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface'); - $this->builder = new FormBuilder('name', $this->factory, $this->dispatcher); + $this->builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); } protected function tearDown() @@ -60,7 +60,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase public function testAddWithGuessFluent() { - $this->builder = new FormBuilder('name', $this->factory, $this->dispatcher, 'stdClass'); + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); $builder = $this->builder->add('foo'); $this->assertSame($builder, $this->builder); } @@ -83,7 +83,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase $this->factory->expects($this->once()) ->method('createNamedBuilder') ->with('text', 'foo') - ->will($this->returnValue(new FormBuilder('foo', $this->factory, $this->dispatcher))); + ->will($this->returnValue(new FormBuilder('foo', null, $this->dispatcher, $this->factory))); $this->assertCount(0, $this->builder->all()); $this->assertFalse($this->builder->has('foo')); @@ -159,7 +159,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase ->with('stdClass', $expectedName, null, $expectedOptions) ->will($this->returnValue($this->getFormBuilder())); - $this->builder = new FormBuilder('name', $this->factory, $this->dispatcher, 'stdClass'); + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); $this->builder->add($expectedName, null, $expectedOptions); $builder = $this->builder->get($expectedName); @@ -173,14 +173,14 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase public function testGetParentForAddedBuilder() { - $builder = new FormBuilder('name', $this->factory, $this->dispatcher); + $builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); $this->builder->add($builder); $this->assertSame($this->builder, $builder->getParent()); } public function testGetParentForRemovedBuilder() { - $builder = new FormBuilder('name', $this->factory, $this->dispatcher); + $builder = new FormBuilder('name', null, $this->dispatcher, $this->factory); $this->builder->add($builder); $this->builder->remove('name'); $this->assertNull($builder->getParent()); @@ -188,7 +188,7 @@ class FormBuilderTest extends \PHPUnit_Framework_TestCase public function testGetParentForCreatedBuilder() { - $this->builder = new FormBuilder('name', $this->factory, $this->dispatcher, 'stdClass'); + $this->builder = new FormBuilder('name', 'stdClass', $this->dispatcher, $this->factory); $this->factory ->expects($this->once()) ->method('createNamedBuilder') diff --git a/src/Symfony/Component/Form/Tests/FormConfigTest.php b/src/Symfony/Component/Form/Tests/FormConfigTest.php index 9d8a5f97e6..4e6c2cf271 100644 --- a/src/Symfony/Component/Form/Tests/FormConfigTest.php +++ b/src/Symfony/Component/Form/Tests/FormConfigTest.php @@ -64,7 +64,7 @@ class FormConfigTest extends \PHPUnit_Framework_TestCase $dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); try { - new FormConfig($name, $dispatcher); + new FormConfig($name, null, $dispatcher); if (!$accepted) { $this->fail(sprintf('The value "%s" should not be accepted', $name)); } diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 40c5137df4..e4f59b751a 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -598,7 +598,7 @@ class FormFactoryTest extends \PHPUnit_Framework_TestCase $this->extension1->addType($type); $parentBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilder') - ->setConstructorArgs(array('name', $this->factory, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'))) + ->setConstructorArgs(array('name', null, $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'), $this->factory)) ->getMock() ; diff --git a/src/Symfony/Component/Form/Tests/FormTest.php b/src/Symfony/Component/Form/Tests/FormTest.php index aa11b30360..f8e820d4af 100644 --- a/src/Symfony/Component/Form/Tests/FormTest.php +++ b/src/Symfony/Component/Form/Tests/FormTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Tests; use Symfony\Component\Form\Form; +use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\FormConfig; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormBuilder; @@ -61,7 +62,7 @@ class FormTest extends \PHPUnit_Framework_TestCase 'foo' => 'bar', )); - $config = new FormConfig('name', $this->dispatcher); + $config = new FormConfig('name', null, $this->dispatcher); $config->appendClientTransformer($client); $config->appendNormTransformer($norm); $form = new Form($config); @@ -1201,14 +1202,41 @@ class FormTest extends \PHPUnit_Framework_TestCase */ public function testFormCannotHaveEmptyNameNotInRootLevel() { - $parent = $this->getBuilder() + $this->getBuilder() ->add($this->getBuilder('')) ->getForm(); } - protected function getBuilder($name = 'name', EventDispatcherInterface $dispatcher = null) + public function testGetPropertyPathReturnsConfiguredPath() { - return new FormBuilder($name, $this->factory, $dispatcher ?: $this->dispatcher); + $form = $this->getBuilder()->setPropertyPath('address.street')->getForm(); + + $this->assertEquals(new PropertyPath('address.street'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToNameIfParentHasDataClass() + { + $parent = $this->getBuilder(null, null, 'stdClass')->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('name'), $form->getPropertyPath()); + } + + // see https://github.com/symfony/symfony/issues/3903 + public function testGetPropertyPathDefaultsToIndexedNameIfParentDataClassIsNull() + { + $parent = $this->getBuilder()->getForm(); + $form = $this->getBuilder('name')->getForm(); + $parent->add($form); + + $this->assertEquals(new PropertyPath('[name]'), $form->getPropertyPath()); + } + + protected function getBuilder($name = 'name', EventDispatcherInterface $dispatcher = null, $dataClass = null) + { + return new FormBuilder($name, $dataClass, $dispatcher ?: $this->dispatcher, $this->factory); } protected function getMockForm($name = 'name')