Sometimes, the objects handled with Symfony forms don't define the expected
getter/setter methods (e.g. getName()
and setName()
) but other methods
better aligned with the application needs (e.g. getName()
and rename()
).
In those cases, you can use a form data mapper to move the object data into
the form fields and the other way around. In Symfony 5.2 we've improved this to
allow using callback functions to get/set form fields. You only need to
define the new getter
or setter
options (or both) and Symfony will run
that callback to get/set the value from/into the object/array:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
// src/Form/Type/ProductType.php
namespace App\Form\Type;
use App\Entity\Person;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
class PersonType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'getter' => function (Person $person, FormInterface $form): string {
return $person->getUserData()->getFirstName();
},
'setter' => function (Person &$person, ?string $name, FormInterface $form): void {
$person->rename($name);
},
])
// ...
;
}
// ...
}
This new feature means that you no longer need to create a data mapper to solve this problem. However, you still need to use data mappers in certain situations (when several form fields are mapped to a single method, when the mapping of the model depends on the submitted form data, etc.)
Looks like a usable feature. :-)
But I think the code example isn't a good example... Why would you use a singular name input field if the Person interface has a split firstName and lastName? How is the
rename()
method supposed to split the singular$name
variable intofirstName
andlastName
? Please don't say "look for a space character and split it there". ;-).Don't get me wrong; I think these kind of examples are way better than the abstract foo-bar-baz ones, but I think this example adds confusion.
Hi how the variable $ person is initialized ?
It seems to me a few features in Symfony components serve the only purpose to directly map entities to forms, whereas mapping dedicated classes feels a lot straightforward (cf. https://verraes.net/2013/04/decoupling-symfony2-forms-from-entities/).
@Bart you are right! We've updated the "getter" example. Thanks!
Why is the
&
needed for$person
in the setter callback? Aren't objects always references?The initial RPC and I believe PR too had 'get' and 'set' as keys for callback accessors, however it has been changed now to 'getter' and 'setter', can we please go back to 'get' and 'set' ?
It's concise and also is similar to property accessors of other programming languages
@Thomas no. Objects are not always references. Using a reference for the argument allows to replace the object (useful in case of immutable objects). And form data could also be an array.
That's a really an awesome feature! FormMappers are quite verbose and write and not very re-usable, thank you very much for this simplification :)
That's a long awaited feature! Thank you very much for it :)
Wow! This helps a lot!