diff --git a/Extension/Core/DataAccessor/PropertyPathAccessor.php b/Extension/Core/DataAccessor/PropertyPathAccessor.php index c845c10f6..f5c25dfc1 100644 --- a/Extension/Core/DataAccessor/PropertyPathAccessor.php +++ b/Extension/Core/DataAccessor/PropertyPathAccessor.php @@ -12,7 +12,9 @@ namespace Symfony\Component\Form\Extension\Core\DataAccessor; use Symfony\Component\Form\DataAccessorInterface; +use Symfony\Component\Form\DataMapperInterface; use Symfony\Component\Form\Exception\AccessException; +use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\FormInterface; use Symfony\Component\PropertyAccess\Exception\AccessException as PropertyAccessException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; @@ -51,15 +53,25 @@ public function setValue(object|array &$data, mixed $value, FormInterface $form) throw new AccessException('Unable to write the given value as no property path is defined.'); } + $getValue = function () use ($data, $form, $propertyPath) { + $dataMapper = $this->getDataMapper($form); + + if ($dataMapper instanceof DataMapper && null !== $dataAccessor = $dataMapper->getDataAccessor()) { + return $dataAccessor->getValue($data, $form); + } + + return $this->getPropertyValue($data, $propertyPath); + }; + // If the field is of type DateTimeInterface and the data is the same skip the update to // keep the original object hash - if ($value instanceof \DateTimeInterface && $value == $this->getPropertyValue($data, $propertyPath)) { + if ($value instanceof \DateTimeInterface && $value == $getValue()) { return; } // If the data is identical to the value in $data, we are // dealing with a reference - if (!\is_object($data) || !$form->getConfig()->getByReference() || $value !== $this->getPropertyValue($data, $propertyPath)) { + if (!\is_object($data) || !$form->getConfig()->getByReference() || $value !== $getValue()) { $this->propertyAccessor->setValue($data, $propertyPath, $value); } } @@ -93,4 +105,13 @@ private function getPropertyValue(object|array $data, PropertyPathInterface $pro return null; } } + + private function getDataMapper(FormInterface $form): ?DataMapperInterface + { + do { + $dataMapper = $form->getConfig()->getDataMapper(); + } while (null === $dataMapper && null !== $form = $form->getParent()); + + return $dataMapper; + } } diff --git a/Extension/Core/DataMapper/DataMapper.php b/Extension/Core/DataMapper/DataMapper.php index f27f9d14b..a7bf98032 100644 --- a/Extension/Core/DataMapper/DataMapper.php +++ b/Extension/Core/DataMapper/DataMapper.php @@ -74,4 +74,12 @@ public function mapFormsToData(\Traversable $forms, mixed &$data): void } } } + + /** + * @internal + */ + public function getDataAccessor(): DataAccessorInterface + { + return $this->dataAccessor; + } } diff --git a/Tests/Extension/Core/DataMapper/DataMapperTest.php b/Tests/Extension/Core/DataMapper/DataMapperTest.php index fafd0e9d0..1a8f2678d 100644 --- a/Tests/Extension/Core/DataMapper/DataMapperTest.php +++ b/Tests/Extension/Core/DataMapper/DataMapperTest.php @@ -16,6 +16,8 @@ use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor; use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper; use Symfony\Component\Form\Extension\Core\Type\DateType; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormConfigBuilder; use Symfony\Component\Form\FormFactoryBuilder; @@ -403,6 +405,25 @@ public function testMapFormsToDataMapsDateTimeInstanceToArrayIfNotSetBefore() $this->assertEquals(['date' => new \DateTime('2022-08-04', new \DateTimeZone('UTC'))], $form->getData()); } + + public function testMapFormToDataWithOnlyGetterConfigured() + { + $person = new DummyPerson('foo'); + $form = (new FormFactoryBuilder()) + ->getFormFactory() + ->createBuilder(FormType::class, $person) + ->add('name', TextType::class, [ + 'getter' => function (DummyPerson $person) { + return $person->myName(); + }, + ]) + ->getForm(); + $form->submit([ + 'name' => 'bar', + ]); + + $this->assertSame('bar', $person->myName()); + } } class SubmittedForm extends Form @@ -439,4 +460,9 @@ public function rename($name): void { $this->name = $name; } + + public function setName($name): void + { + $this->name = $name; + } }