I am using Symfony 6.4, and I'm not sure how to factorize the code in my forms.
Here's a specific example:
In UserRegistrationType, I do:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', TextType::class, [
'label' => 'Email',
'attr' => [
'placeholder' => 'Email'
],
])
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Les mots de passe ne correspondent pas.',
'options' => [
'attr' => [
'autocomplete' => 'new-password',
],
],
'required' => true,
'first_options' =>
[
'label' => 'Mot de passe',
'attr' =>
[
'placeholder' => 'Mot de passe',
'class' => 'verify-password'
]
],
'second_options' => ['label' => 'Répéter le mot de passe', 'attr' => ['placeholder' => 'Répéter le mot de passe']],
])
->add('nom', TextType::class, [
'label' => 'Nom',
'attr' => [
'placeholder' => 'Nom',
],
])
->add('prenom', TextType::class, [
'label' => 'Prénom',
'attr' => [
'placeholder' => 'Prénom',
],
])
}
In one part of the site, I only need to be able to modify the user's password. Not the email, not the name, not the first name, just the password.
So, I use a UserPasswordType:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Les mots de passe ne correspondent pas.',
'options' => [
'attr' => [
'autocomplete' => 'new-password',
],
],
'required' => true,
'first_options' =>
[
'label' => 'Mot de passe',
'attr' =>
[
'placeholder' => 'Mot de passe',
'class' => 'verify-password'
]
],
'second_options' => ['label' => 'Répéter le mot de passe', 'attr' => ['placeholder' => 'Répéter le mot de passe']],
]);
}
But I can see that I'm copying and pasting the same logic twice.
Is there a solution to write the logic only once and use it in each FormType where I'll need it?
Yes you can. There are a few methods to do this. Pick what works for you.
Method 1. Create your own form type and configure the defaults.
https://symfony.com/doc/current/form/create_custom_field_type.html
This is the preferred solution because you can still override the defaults the way symfony meant to. For example if you want to change the invalid_message
in different forms.
class MyRepeatedPasswordType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'type' => PasswordType::class,
'invalid_message' => 'Les mots de passe ne correspondent pas.',
'options' => [
'attr' => [
'autocomplete' => 'new-password',
],
],
'required' => true,
'first_options' =>
[
'label' => 'Mot de passe',
'attr' =>
[
'placeholder' => 'Mot de passe',
'class' => 'verify-password'
]
],
'second_options' => ['label' => 'Répéter le mot de passe', 'attr' => ['placeholder' => 'Répéter le mot de passe']],
]);
}
public function getParent(): string
{
return RepeatedType::class;
}
}
You can add it as follows to your Forms:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('password', MyRepeatedPasswordType::class);
}
Method 2: Factory pattern
This might be the simplest method, but maybe not preferable.
You can create a factory class that you can call to deduplicate code.
class MyFormBuilderFactory
{
public static function passwordField(FormBuilderInterface $builder): void
{
$builder
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Les mots de passe ne correspondent pas.',
'options' => [
'attr' => [
'autocomplete' => 'new-password',
],
],
'required' => true,
'first_options' =>
[
'label' => 'Mot de passe',
'attr' =>
[
'placeholder' => 'Mot de passe',
'class' => 'verify-password'
]
],
'second_options' => ['label' => 'Répéter le mot de passe', 'attr' => ['placeholder' => 'Répéter le mot de passe']],
]);
}
}
You can then use the factory in your Forms.
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('email', TextType::class);
MyFormBuilderFactory::passwordField($builder);
$builder
->add('other_field', ...);
}
There are probably more methods but these I have come across.