Cover of the book Symfony 5: The Fast Track

Symfony 5: The Fast Track is the best book to learn modern Symfony development, from zero to production. +300 pages showcasing Symfony with Docker, APIs, queues & async tasks, Webpack, SPAs, etc.

New in Symfony 5.2: Form mapping callbacks

Contributed by
Yonel Ceruto
in #37968.

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.)

Help the Symfony project!

As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.

Comments

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 into `firstName` and `lastName`?
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!
Login with SymfonyConnect to post a comment