From 878effaf47cfb23f73c984d806eb8e9d9206cb5c Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 27 Aug 2020 06:09:23 -0400 Subject: [PATCH] add new way of mapping data using callback functions --- UPGRADE-5.2.md | 22 + UPGRADE-6.0.md | 1 + src/Symfony/Component/Form/CHANGELOG.md | 2 + .../Component/Form/DataAccessorInterface.php | 69 +++ .../Form/Exception/AccessException.php | 16 + .../Core/DataAccessor/CallbackAccessor.php | 64 +++ .../Core/DataAccessor/ChainAccessor.php | 90 ++++ .../DataAccessor/PropertyPathAccessor.php | 102 +++++ .../Extension/Core/DataMapper/DataMapper.php | 83 ++++ .../Core/DataMapper/PropertyPathMapper.php | 4 + .../Form/Extension/Core/Type/FormType.php | 21 +- .../Form/Tests/AbstractRequestHandlerTest.php | 4 +- .../Component/Form/Tests/CompoundFormTest.php | 10 +- .../Core/DataMapper/DataMapperTest.php | 427 ++++++++++++++++++ .../DataMapper/PropertyPathMapperTest.php | 19 +- .../EventListener/ResizeFormListenerTest.php | 8 +- .../CsrfValidationListenerTest.php | 4 +- .../DataCollector/FormDataCollectorTest.php | 4 +- .../Constraints/FormValidatorTest.php | 32 +- .../EventListener/ValidationListenerTest.php | 4 +- .../ViolationMapper/ViolationMapperTest.php | 10 +- .../Descriptor/resolved_form_type_1.json | 2 + .../Descriptor/resolved_form_type_1.txt | 4 +- .../Descriptor/resolved_form_type_2.json | 2 + .../Descriptor/resolved_form_type_2.txt | 2 + 25 files changed, 947 insertions(+), 59 deletions(-) create mode 100644 src/Symfony/Component/Form/DataAccessorInterface.php create mode 100644 src/Symfony/Component/Form/Exception/AccessException.php create mode 100644 src/Symfony/Component/Form/Extension/Core/DataAccessor/CallbackAccessor.php create mode 100644 src/Symfony/Component/Form/Extension/Core/DataAccessor/ChainAccessor.php create mode 100644 src/Symfony/Component/Form/Extension/Core/DataAccessor/PropertyPathAccessor.php create mode 100644 src/Symfony/Component/Form/Extension/Core/DataMapper/DataMapper.php create mode 100644 src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/DataMapperTest.php diff --git a/UPGRADE-5.2.md b/UPGRADE-5.2.md index 0f0c4567ad1dc..99747ca93c59d 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 f104b11b52d5a..43c1a910a3268 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 7b30716787b76..afd40ef627994 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 0000000000000..d128dde074f86 --- /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 0000000000000..ac712cc3d40ce --- /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 0000000000000..fb121450a47dd --- /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 0000000000000..39e444bb7b0bb --- /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 0000000000000..bd1c382151324 --- /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 0000000000000..f7cb81f34a493 --- /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 c03b1a323910f..7dbc214ca677c 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 d8e219ed26211..79c61ff505f1f 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 5e8facd4cf959..49d7f1887b046 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 45786c85f35e3..e44eaf5ae4184 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 0000000000000..b20b827fdbb5a --- /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 76d936d9789a6..780d9988fddcd 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 3411fdb7d5b39..d5d3a407a1b2f 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 37d7594bef666..9a40e49d3c110 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 4cf38c4132dc4..b32e217377bb6 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 6d0fa9cc541b9..78ae4c8baeab0 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 f8fbabd92a019..ba0118391533e 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 547be2ff37a52..b25a3426ae578 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 8e08211a348a4..cc7d5544a95eb 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 33ba1995eb769..74603552e0d1d 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 d9f8ee75b70c0..9ac279de872f9 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 2366ec5112d1a..c5ec9f775805d 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy