Je remets le code exact pour une solution qui fonctionne comme il faut car:
Lors de la validation, les erreurs pour l'entité User sont renvoyées dans les erreurs générales du formulaire, et pas dans celles de chaque champ. Je sais pas si je suis clair...
Comme je passe User à mon createForm, il va chercher ses validations, mais ne les appliques pas à php ->add('user', UserType::class). Ce qui applique les erreurs générées par User au formulaire global.
Voici donc le code remanié pour que tout fonctionne comme il faut:
Bon...
Après quelques heures de trifouillages, d'essais et de sueur, voici la solution "propre" pour créer un formType, incluant deux autres formType basés sur deux entités, qui omet certains champs.
Exemple typique:
- Deux entités (User et UserProfile)
- Un formulaire de création de compte demandant certaines informations obligatoires à l'inscription
- Ces informations obligatoires sont situées dans les deux entités, mais ces entités comportent d'autres propriétés qu'on ne veut pas afficher à la création de compte
- On veut un formulaire type pour la création de compte (histoire d'être propre) et utiliser les Type créés pour User et UserProfile
<?php
namespace UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\User;
use UserBundle\Entity\UserProfile;
use UserBundle\Form\RegisterForm;
class RegistrationController extends Controller
{
public function registerAction(Request $request)
{
$user = new User();
$profile = new UserProfile();
$form = $this->createForm(RegisterForm::class, ['user' => $user, 'profile' => $profile]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$encoder = $this->get('security.password_encoder');
$encodedPassword = $encoder->encodePassword($user, $user->getPlainPassword());
$user->setPassword($encodedPassword);
$user->setProfile($profile);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('user_registration_register');
}
return $this->render('UserBundle:Registration:register.html.twig', [
'registerForm' => $form->createView(),
]);
}
}
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Form\Type\UserType;
use UserBundle\Form\Type\UserProfileType;
class RegisterForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user', UserType::class, [
'label' => false
])
->add('profile', UserProfileType::class, [
'label' => false
])
// Ceci est un exemple de comment enlever des fields à profile
->get('profile')
->remove('firstName')
->remove('lastName')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'validation_groups' => 'register'
]);
}
}
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use UserBundle\Entity\User;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'label' => 'user.form.register.email.label',
])
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'user.form.password.not_match',
'first_options' => ['label' => 'user.form.register.password.label_first'],
'second_options' => ['label' => 'user.form.register.password.label_second'],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
<?php
namespace UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use UserBundle\Entity\UserProfile;
class UserProfileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName', TextType::class, [
'label' => 'user.form.register.first_name.label'
])
->add('lastName', TextType::class, [
'label' => 'user.form.register.last_name.label'
])
->add('birthday', BirthdayType::class, [
'label' => 'user.form.register.birthday.label',
'placeholder' => [
'year' => 'core.year',
'month' => 'core.month',
'day' => 'core.day'
]
])
->add('gender', ChoiceType::class, [
'label' => 'user.form.register.gender.label',
'choices' => [
'core.male' => 1,
'core.female' => 2,
]
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => UserProfile::class,
]);
}
}
Je ne mets pas les Entity, ça n'a pas grand intérêt. :)
En espérant que cela aide d'autres personnes !
Et si cela n'a pas été clair, n'hésitez pas à m'écrire :D