Description
Symfony version(s) affected
5.2.0 upwards
Description
Symfony 5.2 introduced the getter
and setter
options for form types, but the separation is not complete causing exceptions during form submission.
How to reproduce
Consider this use case on an object tree inheriting settings:
private ?int $value;
public function setValue(?int $newValue): void
{
$this->value = $newValue;
}
public function getConfiguredValue(): ?int
{
return $this->value;
}
public function getActualValue(): int
{
return $this->value ?? $this->parent->getActualValue();
}
In this case, the getter is not called getValue
on purpose, to avoid confusion with programmers calling the wrong one. Since Symfony 5.2 I can now map this in a form:
$builder
->add('value', IntegerType::class, [
'getter' => fn($object) => $object->getConfiguredValue(),
])
This works fine to render the form. Submitting it however crashes with a NoSuchPropertyException
, stating Can't get a way to read the property "value" in class XXX
. Note that it doesn't know how to read the property: this is because there is no setter
defined, as the default works fine, and as such the PropertyPathAccessor
is used to set the value, which reads the property to detect changes first, is blissfully unaware of the getter
option, and therefore tries to read the propery via the accessor.
Possible Solution
Right now the only working solution without modifying the original object is to add a setter
as well, which is pure boilerplate as it does nothing special:
'setter' => function($object, ?int $newValue) {
$object->setValue($newValue);
},
So solutions we could implement are:
- Make
getter
andsetter
mandatory in pairs - setting one requires the other. This is currently in practice enforced by the implementation, yet it does not throw an error if you don't. Seems weak as in the given example the setter is completely obsolete. - Make the DataAccessor properly aware of the existence of a
getter
when defaulting the set behavior, bypassing default PropertyAccess behavior that tries to read the property for changes.
Obviously the second solution is far more correct and better, it is also more complex and may introduce new edge cases.
Additional Context
No response