diff --git a/UPGRADE-5.2.md b/UPGRADE-5.2.md index 0f0c4567ad1d..99747ca93c59 100644 --- a/UPGRADE-5.2.md +++ b/UPGRADE-5.2.md @@ -16,6 +16,28 @@ FrameworkBundle used to be added by default to the seed, which is not the case anymore. This allows sharing caches between apps or different environments. +Form +---- + + * Deprecated `PropertyPathMapper` in favor of `DataMapper` and `PropertyPathAccessor`. + + Before: + + ```php + use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; + + $builder->setDataMapper(new PropertyPathMapper()); + ``` + + After: + + ```php + use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor; + use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; + + $builder->setDataMapper(new DataMapper(new PropertyPathAccessor())); + ``` + Lock ---- diff --git a/UPGRADE-6.0.md b/UPGRADE-6.0.md index f104b11b52d5..43c1a910a326 100644 --- a/UPGRADE-6.0.md +++ b/UPGRADE-6.0.md @@ -48,6 +48,7 @@ Form * Added argument `callable|null $filter` to `ChoiceListFactoryInterface::createListFromChoices()` and `createListFromLoader()`. * The `Symfony\Component\Form\Extension\Validator\Util\ServerParams` class has been removed, use its parent `Symfony\Component\Form\Util\ServerParams` instead. * The `NumberToLocalizedStringTransformer::ROUND_*` constants have been removed, use `\NumberFormatter::ROUND_*` instead. + * Removed `PropertyPathMapper` in favor of `DataMapper` and `PropertyPathAccessor`. FrameworkBundle --------------- diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 7b30716787b7..afd40ef62799 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -5,6 +5,8 @@ CHANGELOG ----- * Added support for using the `{{ label }}` placeholder in constraint messages, which is replaced in the `ViolationMapper` by the corresponding field form label. + * Added `DataMapper`, `ChainAccessor`, `PropertyPathAccessor` and `CallbackAccessor` with new callable `getter` and `setter` options for each form type + * Deprecated `PropertyPathMapper` in favor of `DataMapper` and `PropertyPathAccessor` 5.1.0 ----- diff --git a/src/Symfony/Component/Form/DataAccessorInterface.php b/src/Symfony/Component/Form/DataAccessorInterface.php new file mode 100644 index 000000000000..d128dde074f8 --- /dev/null +++ b/src/Symfony/Component/Form/DataAccessorInterface.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form; + +/** + * Writes and reads values to/from an object or array bound to a form. + * + * @author Yonel Ceruto + */ +interface DataAccessorInterface +{ + /** + * Returns the value at the end of the property of the object graph. + * + * @param object|array $viewData The view data of the compound form + * @param FormInterface $form The {@link FormInterface()} instance to check + * + * @return mixed The value at the end of the property + * + * @throws Exception\AccessException If unable to read from the given form data + */ + public function getValue($viewData, FormInterface $form); + + /** + * Sets the value at the end of the property of the object graph. + * + * @param object|array $viewData The view data of the compound form + * @param mixed $value The value to set at the end of the object graph + * @param FormInterface $form The {@link FormInterface()} instance to check + * + * @throws Exception\AccessException If unable to write the given value + */ + public function setValue(&$viewData, $value, FormInterface $form): void; + + /** + * Returns whether a value can be read from an object graph. + * + * Whenever this method returns true, {@link getValue()} is guaranteed not + * to throw an exception when called with the same arguments. + * + * @param object|array $viewData The view data of the compound form + * @param FormInterface $form The {@link FormInterface()} instance to check + * + * @return bool Whether the value can be read + */ + public function isReadable($viewData, FormInterface $form): bool; + + /** + * Returns whether a value can be written at a given object graph. + * + * Whenever this method returns true, {@link setValue()} is guaranteed not + * to throw an exception when called with the same arguments. + * + * @param object|array $viewData The view data of the compound form + * @param FormInterface $form The {@link FormInterface()} instance to check + * + * @return bool Whether the value can be set + */ + public function isWritable($viewData, FormInterface $form): bool; +} diff --git a/src/Symfony/Component/Form/Exception/AccessException.php b/src/Symfony/Component/Form/Exception/AccessException.php new file mode 100644 index 000000000000..ac712cc3d40c --- /dev/null +++ b/src/Symfony/Component/Form/Exception/AccessException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class AccessException extends RuntimeException +{ +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataAccessor/CallbackAccessor.php b/src/Symfony/Component/Form/Extension/Core/DataAccessor/CallbackAccessor.php new file mode 100644 index 000000000000..fb121450a47d --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataAccessor/CallbackAccessor.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataAccessor; + +use Symfony\Component\Form\DataAccessorInterface; +use Symfony\Component\Form\Exception\AccessException; +use Symfony\Component\Form\FormInterface; + +/** + * Writes and reads values to/from an object or array using callback functions. + * + * @author Yonel Ceruto + */ +class CallbackAccessor implements DataAccessorInterface +{ + /** + * {@inheritdoc} + */ + public function getValue($data, FormInterface $form) + { + if (null === $getter = $form->getConfig()->getOption('getter')) { + throw new AccessException('Unable to read from the given form data as no getter is defined.'); + } + + return ($getter)($data, $form); + } + + /** + * {@inheritdoc} + */ + public function setValue(&$data, $value, FormInterface $form): void + { + if (null === $setter = $form->getConfig()->getOption('setter')) { + throw new AccessException('Unable to write the given value as no setter is defined.'); + } + + ($setter)($data, $form->getData(), $form); + } + + /** + * {@inheritdoc} + */ + public function isReadable($data, FormInterface $form): bool + { + return null !== $form->getConfig()->getOption('getter'); + } + + /** + * {@inheritdoc} + */ + public function isWritable($data, FormInterface $form): bool + { + return null !== $form->getConfig()->getOption('setter'); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataAccessor/ChainAccessor.php b/src/Symfony/Component/Form/Extension/Core/DataAccessor/ChainAccessor.php new file mode 100644 index 000000000000..39e444bb7b0b --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataAccessor/ChainAccessor.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataAccessor; + +use Symfony\Component\Form\DataAccessorInterface; +use Symfony\Component\Form\Exception\AccessException; +use Symfony\Component\Form\FormInterface; + +/** + * @author Yonel Ceruto + */ +class ChainAccessor implements DataAccessorInterface +{ + private $accessors; + + /** + * @param DataAccessorInterface[]|iterable $accessors + */ + public function __construct(iterable $accessors) + { + $this->accessors = $accessors; + } + + /** + * {@inheritdoc} + */ + public function getValue($data, FormInterface $form) + { + foreach ($this->accessors as $accessor) { + if ($accessor->isReadable($data, $form)) { + return $accessor->getValue($data, $form); + } + } + + throw new AccessException('Unable to read from the given form data as no accessor in the chain is able to read the data.'); + } + + /** + * {@inheritdoc} + */ + public function setValue(&$data, $value, FormInterface $form): void + { + foreach ($this->accessors as $accessor) { + if ($accessor->isWritable($data, $form)) { + $accessor->setValue($data, $value, $form); + + return; + } + } + + throw new AccessException('Unable to write the given value as no accessor in the chain is able to set the data.'); + } + + /** + * {@inheritdoc} + */ + public function isReadable($data, FormInterface $form): bool + { + foreach ($this->accessors as $accessor) { + if ($accessor->isReadable($data, $form)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function isWritable($data, FormInterface $form): bool + { + foreach ($this->accessors as $accessor) { + if ($accessor->isWritable($data, $form)) { + return true; + } + } + + return false; + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataAccessor/PropertyPathAccessor.php b/src/Symfony/Component/Form/Extension/Core/DataAccessor/PropertyPathAccessor.php new file mode 100644 index 000000000000..bd1c38215132 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataAccessor/PropertyPathAccessor.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataAccessor; + +use Symfony\Component\Form\DataAccessorInterface; +use Symfony\Component\Form\Exception\AccessException; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\PropertyAccess\Exception\AccessException as PropertyAccessException; +use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; + +/** + * Writes and reads values to/from an object or array using property path. + * + * @author Yonel Ceruto + * @author Bernhard Schussek + */ +class PropertyPathAccessor implements DataAccessorInterface +{ + private $propertyAccessor; + + public function __construct(PropertyAccessorInterface $propertyAccessor = null) + { + $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function getValue($data, FormInterface $form) + { + if (null === $propertyPath = $form->getPropertyPath()) { + throw new AccessException('Unable to read from the given form data as no property path is defined.'); + } + + return $this->getPropertyValue($data, $propertyPath); + } + + /** + * {@inheritdoc} + */ + public function setValue(&$data, $propertyValue, FormInterface $form): void + { + if (null === $propertyPath = $form->getPropertyPath()) { + throw new AccessException('Unable to write the given value as no property path is defined.'); + } + + // If the field is of type DateTimeInterface and the data is the same skip the update to + // keep the original object hash + if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $this->getPropertyValue($data, $propertyPath)) { + return; + } + + // If the data is identical to the value in $data, we are + // dealing with a reference + if (!\is_object($data) || !$form->getConfig()->getByReference() || $propertyValue !== $this->getPropertyValue($data, $propertyPath)) { + $this->propertyAccessor->setValue($data, $propertyPath, $propertyValue); + } + } + + /** + * {@inheritdoc} + */ + public function isReadable($data, FormInterface $form): bool + { + return null !== $form->getPropertyPath(); + } + + /** + * {@inheritdoc} + */ + public function isWritable($data, FormInterface $form): bool + { + return null !== $form->getPropertyPath(); + } + + private function getPropertyValue($data, $propertyPath) + { + try { + return $this->propertyAccessor->getValue($data, $propertyPath); + } catch (PropertyAccessException $e) { + if (!$e instanceof UninitializedPropertyException + // For versions without UninitializedPropertyException check the exception message + && (class_exists(UninitializedPropertyException::class) || false === strpos($e->getMessage(), 'You should initialize it')) + ) { + throw $e; + } + + return null; + } + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/DataMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/DataMapper.php new file mode 100644 index 000000000000..f7cb81f34a49 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/DataMapper.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Core\DataMapper; + +use Symfony\Component\Form\DataAccessorInterface; +use Symfony\Component\Form\DataMapperInterface; +use Symfony\Component\Form\Exception\UnexpectedTypeException; +use Symfony\Component\Form\Extension\Core\DataAccessor\CallbackAccessor; +use Symfony\Component\Form\Extension\Core\DataAccessor\ChainAccessor; +use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor; + +/** + * Maps arrays/objects to/from forms using data accessors. + * + * @author Bernhard Schussek + */ +class DataMapper implements DataMapperInterface +{ + private $dataAccessor; + + public function __construct(DataAccessorInterface $dataAccessor = null) + { + $this->dataAccessor = $dataAccessor ?? new ChainAccessor([ + new CallbackAccessor(), + new PropertyPathAccessor(), + ]); + } + + /** + * {@inheritdoc} + */ + public function mapDataToForms($data, iterable $forms): void + { + $empty = null === $data || [] === $data; + + if (!$empty && !\is_array($data) && !\is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $config = $form->getConfig(); + + if (!$empty && $config->getMapped() && $this->dataAccessor->isReadable($data, $form)) { + $form->setData($this->dataAccessor->getValue($data, $form)); + } else { + $form->setData($config->getData()); + } + } + } + + /** + * {@inheritdoc} + */ + public function mapFormsToData(iterable $forms, &$data): void + { + if (null === $data) { + return; + } + + if (!\is_array($data) && !\is_object($data)) { + throw new UnexpectedTypeException($data, 'object, array or empty'); + } + + foreach ($forms as $form) { + $config = $form->getConfig(); + + // Write-back is disabled if the form is not synchronized (transformation failed), + // if the form was not submitted and if the form is disabled (modification not allowed) + if ($config->getMapped() && $form->isSubmitted() && $form->isSynchronized() && !$form->isDisabled() && $this->dataAccessor->isWritable($data, $form)) { + $this->dataAccessor->setValue($data, $form->getData(), $form); + } + } + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php index c03b1a323910..7dbc214ca677 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php +++ b/src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php @@ -18,10 +18,14 @@ use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +trigger_deprecation('symfony/form', '5.2', 'The "%s" class is deprecated. Use "%s" instead.', PropertyPathMapper::class, DataMapper::class); + /** * Maps arrays/objects to/from forms using property paths. * * @author Bernhard Schussek + * + * @deprecated since symfony/form 5.2. Use {@see DataMapper} instead. */ class PropertyPathMapper implements DataMapperInterface { diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index d8e219ed2621..79c61ff505f1 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -12,7 +12,10 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\Exception\LogicException; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataAccessor\CallbackAccessor; +use Symfony\Component\Form\Extension\Core\DataAccessor\ChainAccessor; +use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormConfigBuilderInterface; @@ -25,11 +28,14 @@ class FormType extends BaseType { - private $propertyAccessor; + private $dataMapper; public function __construct(PropertyAccessorInterface $propertyAccessor = null) { - $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); + $this->dataMapper = new DataMapper(new ChainAccessor([ + new CallbackAccessor(), + new PropertyPathAccessor($propertyAccessor ?? PropertyAccess::createPropertyAccessor()), + ])); } /** @@ -52,7 +58,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->setCompound($options['compound']) ->setData($isDataOptionSet ? $options['data'] : null) ->setDataLocked($isDataOptionSet) - ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null) + ->setDataMapper($options['compound'] ? $this->dataMapper : null) ->setMethod($options['method']) ->setAction($options['action']); @@ -202,6 +208,8 @@ public function configureOptions(OptionsResolver $resolver) 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => [], 'is_empty_callback' => null, + 'getter' => null, + 'setter' => null, ]); $resolver->setAllowedTypes('label_attr', 'array'); @@ -211,6 +219,11 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedTypes('help_attr', 'array'); $resolver->setAllowedTypes('help_html', 'bool'); $resolver->setAllowedTypes('is_empty_callback', ['null', 'callable']); + $resolver->setAllowedTypes('getter', ['null', 'callable']); + $resolver->setAllowedTypes('setter', ['null', 'callable']); + + $resolver->setInfo('getter', 'A callable that accepts two arguments (the view data and the current form field) and must return a value.'); + $resolver->setInfo('setter', 'A callable that accepts three arguments (a reference to the view data, the submitted value and the current form field).'); } /** diff --git a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php index 5e8facd4cf95..49d7f1887b04 100644 --- a/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractRequestHandlerTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormError; @@ -409,7 +409,7 @@ protected function createBuilder($name, $compound = false, array $options = []) $builder->setCompound($compound); if ($compound) { - $builder->setDataMapper(new PropertyPathMapper()); + $builder->setDataMapper(new DataMapper()); } return $builder; diff --git a/src/Symfony/Component/Form/Tests/CompoundFormTest.php b/src/Symfony/Component/Form/Tests/CompoundFormTest.php index 45786c85f35e..e44eaf5ae418 100644 --- a/src/Symfony/Component/Form/Tests/CompoundFormTest.php +++ b/src/Symfony/Component/Form/Tests/CompoundFormTest.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Form\Tests; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler; use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormEvent; @@ -394,17 +394,17 @@ public function testSetDataSupportsDynamicAdditionAndRemovalOfChildren() { $form = $this->getBuilder() ->setCompound(true) - // We test using PropertyPathMapper on purpose. The traversal logic + // We test using DataMapper on purpose. The traversal logic // is currently contained in InheritDataAwareIterator, but even // if that changes, this test should still function. - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $childToBeRemoved = $this->createForm('removed', false); $childToBeAdded = $this->createForm('added', false); $child = $this->getBuilder('child', new EventDispatcher()) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($form, $childToBeAdded) { $form->remove('removed'); $form->add($childToBeAdded); @@ -449,7 +449,7 @@ public function testSetDataMapsViewDataToChildren() public function testSetDataDoesNotMapViewDataToChildrenWithLockedSetData() { - $mapper = new PropertyPathMapper(); + $mapper = new DataMapper(); $viewData = [ 'firstName' => 'Fabien', 'lastName' => 'Pot', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/DataMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/DataMapperTest.php new file mode 100644 index 000000000000..b20b827fdbb5 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/DataMapperTest.php @@ -0,0 +1,427 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormConfigBuilder; +use Symfony\Component\Form\Tests\Fixtures\TypehintedPropertiesCar; +use Symfony\Component\PropertyAccess\PropertyPath; + +class DataMapperTest extends TestCase +{ + /** + * @var DataMapper + */ + private $mapper; + + /** + * @var EventDispatcherInterface + */ + private $dispatcher; + + protected function setUp(): void + { + $this->mapper = new DataMapper(); + $this->dispatcher = new EventDispatcher(); + } + + public function testMapDataToFormsPassesObjectRefIfByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $car->engine = $engine; + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $form = new Form($config); + + $this->mapper->mapDataToForms($car, [$form]); + + self::assertSame($engine, $form->getData()); + } + + public function testMapDataToFormsPassesObjectCloneIfNotByReference() + { + $car = new \stdClass(); + $engine = new \stdClass(); + $engine->brand = 'Rolls-Royce'; + $car->engine = $engine; + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $form = new Form($config); + + $this->mapper->mapDataToForms($car, [$form]); + + self::assertNotSame($engine, $form->getData()); + self::assertEquals($engine, $form->getData()); + } + + public function testMapDataToFormsIgnoresEmptyPropertyPath() + { + $car = new \stdClass(); + + $config = new FormConfigBuilder(null, \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $form = new Form($config); + + self::assertNull($form->getPropertyPath()); + + $this->mapper->mapDataToForms($car, [$form]); + + self::assertNull($form->getData()); + } + + public function testMapDataToFormsIgnoresUnmapped() + { + $car = new \stdClass(); + $car->engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setMapped(false); + $config->setPropertyPath($propertyPath); + $form = new Form($config); + + $this->mapper->mapDataToForms($car, [$form]); + + self::assertNull($form->getData()); + } + + /** + * @requires PHP 7.4 + */ + public function testMapDataToFormsIgnoresUninitializedProperties() + { + $engineForm = new Form(new FormConfigBuilder('engine', null, $this->dispatcher)); + $colorForm = new Form(new FormConfigBuilder('color', null, $this->dispatcher)); + + $car = new TypehintedPropertiesCar(); + $car->engine = 'BMW'; + + $this->mapper->mapDataToForms($car, [$engineForm, $colorForm]); + + self::assertSame($car->engine, $engineForm->getData()); + self::assertNull($colorForm->getData()); + } + + public function testMapDataToFormsSetsDefaultDataIfPassedDataIsNull() + { + $default = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($default); + + $form = new Form($config); + + $this->mapper->mapDataToForms(null, [$form]); + + self::assertSame($default, $form->getData()); + } + + public function testMapDataToFormsSetsDefaultDataIfPassedDataIsEmptyArray() + { + $default = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($default); + + $form = new Form($config); + + $this->mapper->mapDataToForms([], [$form]); + + self::assertSame($default, $form->getData()); + } + + public function testMapFormsToDataWritesBackIfNotByReference() + { + $car = new \stdClass(); + $car->engine = new \stdClass(); + $engine = new \stdClass(); + $engine->brand = 'Rolls-Royce'; + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertEquals($engine, $car->engine); + self::assertNotSame($engine, $car->engine); + } + + public function testMapFormsToDataWritesBackIfByReferenceButNoReference() + { + $car = new \stdClass(); + $car->engine = new \stdClass(); + $engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($engine, $car->engine); + } + + public function testMapFormsToDataWritesBackIfByReferenceAndReference() + { + $car = new \stdClass(); + $car->engine = 'BMW'; + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('engine', null, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData('Rolls-Royce'); + $form = new SubmittedForm($config); + + $car->engine = 'Rolls-Royce'; + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame('Rolls-Royce', $car->engine); + } + + public function testMapFormsToDataIgnoresUnmapped() + { + $initialEngine = new \stdClass(); + $car = new \stdClass(); + $car->engine = $initialEngine; + $engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setMapped(false); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($initialEngine, $car->engine); + } + + public function testMapFormsToDataIgnoresUnsubmittedForms() + { + $initialEngine = new \stdClass(); + $car = new \stdClass(); + $car->engine = $initialEngine; + $engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = new Form($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($initialEngine, $car->engine); + } + + public function testMapFormsToDataIgnoresEmptyData() + { + $initialEngine = new \stdClass(); + $car = new \stdClass(); + $car->engine = $initialEngine; + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData(null); + $form = new Form($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($initialEngine, $car->engine); + } + + public function testMapFormsToDataIgnoresUnsynchronized() + { + $initialEngine = new \stdClass(); + $car = new \stdClass(); + $car->engine = $initialEngine; + $engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $form = new NotSynchronizedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($initialEngine, $car->engine); + } + + public function testMapFormsToDataIgnoresDisabled() + { + $initialEngine = new \stdClass(); + $car = new \stdClass(); + $car->engine = $initialEngine; + $engine = new \stdClass(); + $propertyPath = new PropertyPath('engine'); + + $config = new FormConfigBuilder('name', \stdClass::class, $this->dispatcher); + $config->setByReference(true); + $config->setPropertyPath($propertyPath); + $config->setData($engine); + $config->setDisabled(true); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame($initialEngine, $car->engine); + } + + /** + * @requires PHP 7.4 + */ + public function testMapFormsToUninitializedProperties() + { + $car = new TypehintedPropertiesCar(); + $config = new FormConfigBuilder('engine', null, $this->dispatcher); + $config->setData('BMW'); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $car); + + self::assertSame('BMW', $car->engine); + } + + /** + * @dataProvider provideDate + */ + public function testMapFormsToDataDoesNotChangeEqualDateTimeInstance($date) + { + $article = []; + $publishedAt = $date; + $publishedAtValue = clone $publishedAt; + $article['publishedAt'] = $publishedAtValue; + $propertyPath = new PropertyPath('[publishedAt]'); + + $config = new FormConfigBuilder('publishedAt', \get_class($publishedAt), $this->dispatcher); + $config->setByReference(false); + $config->setPropertyPath($propertyPath); + $config->setData($publishedAt); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $article); + + self::assertSame($publishedAtValue, $article['publishedAt']); + } + + public function provideDate(): array + { + return [ + [new \DateTime()], + [new \DateTimeImmutable()], + ]; + } + + public function testMapDataToFormsUsingGetCallbackOption() + { + $initialName = 'John Doe'; + $person = new DummyPerson($initialName); + + $config = new FormConfigBuilder('name', null, $this->dispatcher, [ + 'getter' => static function (DummyPerson $person) { + return $person->myName(); + }, + ]); + $form = new Form($config); + + $this->mapper->mapDataToForms($person, [$form]); + + self::assertSame($initialName, $form->getData()); + } + + public function testMapFormsToDataUsingSetCallbackOption() + { + $person = new DummyPerson('John Doe'); + + $config = new FormConfigBuilder('name', null, $this->dispatcher, [ + 'setter' => static function (DummyPerson $person, $name) { + $person->rename($name); + }, + ]); + $config->setData('Jane Doe'); + $form = new SubmittedForm($config); + + $this->mapper->mapFormsToData([$form], $person); + + self::assertSame('Jane Doe', $person->myName()); + } +} + +class SubmittedForm extends Form +{ + public function isSubmitted(): bool + { + return true; + } +} + +class NotSynchronizedForm extends SubmittedForm +{ + public function isSynchronized(): bool + { + return false; + } +} + +class DummyPerson +{ + private $name; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function myName(): string + { + return $this->name; + } + + public function rename($name): void + { + $this->name = $name; + } +} 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 76d936d9789a..780d9988fddc 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php @@ -22,6 +22,9 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyAccess\PropertyPath; +/** + * @group legacy + */ class PropertyPathMapperTest extends TestCase { /** @@ -363,19 +366,3 @@ public function provideDate() ]; } } - -class SubmittedForm extends Form -{ - public function isSubmitted(): bool - { - return true; - } -} - -class NotSynchronizedForm extends SubmittedForm -{ - public function isSynchronized(): bool - { - return false; - } -} 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 3411fdb7d5b3..d5d3a407a1b2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/EventListener/ResizeFormListenerTest.php @@ -14,7 +14,7 @@ use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilder; @@ -31,7 +31,7 @@ protected function setUp(): void $this->factory = (new FormFactoryBuilder())->getFormFactory(); $this->form = $this->getBuilder() ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); } @@ -268,12 +268,12 @@ public function testOnSubmitDeleteEmptyCompoundEntriesIfAllowDelete() $this->form->setData(['0' => ['name' => 'John'], '1' => ['name' => 'Jane']]); $form1 = $this->getBuilder('0') ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form1->add($this->getForm('name')); $form2 = $this->getBuilder('1') ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form2->add($this->getForm('name')); $this->form->add($form1); diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php index 37d7594bef66..9a40e49d3c11 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/CsrfValidationListenerTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormEvent; @@ -33,7 +33,7 @@ protected function setUp(): void $this->factory = (new FormFactoryBuilder())->getFormFactory(); $this->tokenManager = new CsrfTokenManager(); $this->form = $this->getBuilder() - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php index 4cf38c4132dc..b32e217377bb 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Form\Extension\Core\CoreExtension; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -81,7 +81,7 @@ protected function setUp(): void $this->dataCollector = new FormDataCollector($this->dataExtractor); $this->dispatcher = new EventDispatcher(); $this->factory = new FormFactory(new FormRegistry([new CoreExtension()], new ResolvedFormTypeFactory())); - $this->dataMapper = new PropertyPathMapper(); + $this->dataMapper = new DataMapper(); $this->form = $this->createForm('name'); $this->childForm = $this->createForm('child'); $this->view = new FormView(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index 6d0fa9cc541b..78ae4c8baeab 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -15,7 +15,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Validator\Constraints\Form; use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator; use Symfony\Component\Form\Extension\Validator\ValidatorExtension; @@ -107,7 +107,7 @@ public function testValidateChildIfValidConstraint() $parent = $this->getBuilder('parent') ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $options = [ 'validation_groups' => ['group1', 'group2'], @@ -130,7 +130,7 @@ public function testDontValidateIfParentWithoutValidConstraint() $parent = $this->getBuilder('parent', null) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $options = ['validation_groups' => ['group1', 'group2']]; $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); @@ -169,7 +169,7 @@ public function testValidateConstraintsOptionEvenIfNoValidConstraint() $parent = $this->getBuilder('parent', null) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $options = [ 'validation_groups' => ['group1', 'group2'], @@ -196,7 +196,7 @@ public function testDontValidateIfNoValidationGroups() ]) ->setData($object) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form->setData($object); @@ -243,7 +243,7 @@ public function testDontValidateChildConstraintsIfCallableNoValidationGroups() ]; $form = $this->getBuilder('name', null, $formOptions) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $childOptions = ['constraints' => [new NotBlank()]]; $child = $this->getCompoundForm(new \stdClass(), $childOptions); @@ -470,7 +470,7 @@ public function testUseValidationGroupOfClickedButton() $parent = $this->getBuilder('parent') ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form = $this->getForm('name', '\stdClass', [ 'validation_groups' => 'form_group', @@ -497,7 +497,7 @@ public function testDontUseValidationGroupOfUnclickedButton() $parent = $this->getBuilder('parent') ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form = $this->getCompoundForm($object, [ 'validation_groups' => 'form_group', @@ -525,7 +525,7 @@ public function testUseInheritedValidationGroup() $parentOptions = ['validation_groups' => 'group']; $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $formOptions = ['constraints' => [new Valid()]]; $form = $this->getCompoundForm($object, $formOptions); @@ -546,7 +546,7 @@ public function testUseInheritedCallbackValidationGroup() $parentOptions = ['validation_groups' => [$this, 'getValidationGroups']]; $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $formOptions = ['constraints' => [new Valid()]]; $form = $this->getCompoundForm($object, $formOptions); @@ -571,7 +571,7 @@ public function testUseInheritedClosureValidationGroup() ]; $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $formOptions = ['constraints' => [new Valid()]]; $form = $this->getCompoundForm($object, $formOptions); @@ -618,7 +618,7 @@ public function testViolationIfExtraData() { $form = $this->getBuilder('parent', null, ['extra_fields_message' => 'Extra!|Extras!']) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->add($this->getBuilder('child')) ->getForm(); @@ -643,7 +643,7 @@ public function testViolationFormatIfMultipleExtraFields() { $form = $this->getBuilder('parent', null, ['extra_fields_message' => 'Extra!|Extras!!']) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->add($this->getBuilder('child')) ->getForm(); @@ -669,7 +669,7 @@ public function testNoViolationIfAllowExtraData() $form = $this ->getBuilder('parent', null, ['allow_extra_fields' => true]) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->add($this->getBuilder('child')) ->getForm(); @@ -698,7 +698,7 @@ public function testCauseForNotAllowedExtraFieldsIsTheFormConstraint() $form = $this ->getBuilder('form', null, ['constraints' => [new NotBlank(['groups' => ['foo']])]]) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); $form->submit([ 'extra_data' => 'foo', @@ -739,7 +739,7 @@ private function getCompoundForm($data, array $options = []) return $this->getBuilder('name', \is_object($data) ? \get_class($data) : null, $options) ->setData($data) ->setCompound(true) - ->setDataMapper(new PropertyPathMapper()) + ->setDataMapper(new DataMapper()) ->getForm(); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php index f8fbabd92a01..ba0118391533 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Validator\Constraints\Form as FormConstraint; use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener; use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; @@ -79,7 +79,7 @@ private function createForm($name = '', $compound = false) $config->setCompound($compound); if ($compound) { - $config->setDataMapper(new PropertyPathMapper()); + $config->setDataMapper(new DataMapper()); } return new Form($config); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php index 547be2ff37a5..b25a3426ae57 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -16,7 +16,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Exception\TransformationFailedException; -use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormConfigBuilder; @@ -82,7 +82,7 @@ protected function getForm($name = 'name', $propertyPath = null, $dataClass = nu $config->setInheritData($inheritData); $config->setPropertyPath($propertyPath); $config->setCompound(true); - $config->setDataMapper(new PropertyPathMapper()); + $config->setDataMapper(new DataMapper()); if (!$synchronized) { $config->addViewTransformer(new CallbackTransformer( @@ -1643,7 +1643,7 @@ public function testMessageWithLabel2() $config->setInheritData(false); $config->setPropertyPath('name'); $config->setCompound(true); - $config->setDataMapper(new PropertyPathMapper()); + $config->setDataMapper(new DataMapper()); $child = new Form($config); $parent->add($child); @@ -1681,7 +1681,7 @@ public function testMessageWithLabelFormat1() $config->setInheritData(false); $config->setPropertyPath('custom'); $config->setCompound(true); - $config->setDataMapper(new PropertyPathMapper()); + $config->setDataMapper(new DataMapper()); $child = new Form($config); $parent->add($child); @@ -1719,7 +1719,7 @@ public function testMessageWithLabelFormat2() $config->setInheritData(false); $config->setPropertyPath('custom-id'); $config->setCompound(true); - $config->setDataMapper(new PropertyPathMapper()); + $config->setDataMapper(new DataMapper()); $child = new Form($config); $parent->add($child); diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json index 8e08211a348a..cc7d5544a95e 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json @@ -39,6 +39,7 @@ "by_reference", "data", "disabled", + "getter", "help", "help_attr", "help_html", @@ -57,6 +58,7 @@ "property_path", "required", "row_attr", + "setter", "translation_domain", "upload_max_size_message" ] diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt index 33ba1995eb76..74603552e0d1 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt @@ -17,7 +17,8 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") group_by by_reference multiple data placeholder disabled - preferred_choices help + preferred_choices getter + help help_attr help_html help_translation_parameters @@ -35,6 +36,7 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") property_path required row_attr + setter translation_domain upload_max_size_message --------------------------- -------------------- ------------------------------ ----------------------- diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json index d9f8ee75b70c..9ac279de872f 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json @@ -17,6 +17,7 @@ "disabled", "empty_data", "error_bubbling", + "getter", "help", "help_attr", "help_html", @@ -36,6 +37,7 @@ "property_path", "required", "row_attr", + "setter", "translation_domain", "trim", "upload_max_size_message" diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt index 2366ec5112d1..c5ec9f775805 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt @@ -19,6 +19,7 @@ Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form") disabled empty_data error_bubbling + getter help help_attr help_html @@ -38,6 +39,7 @@ Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form") property_path required row_attr + setter translation_domain trim upload_max_size_message