Bonjour,

Voila je rencontre un petit problème avec mon code.

J'essaye de créer un formulaire de création de compte qui se baserait sur deux entités.
La première "User" contient l'adresse mail, le mot de passe, etc...
La deuxième "UserInfo" contient le prénom, nom, date de naissance et d'autres propriétés.
Cette partie, j'arrive très bien à la faire. J'obtiens un beau formulaire qui contient les champs de ces deux entités.

Par contre, j'aimerais que ce formulaire n'embarque pas certains champs de UserInfo et ne garde que ceux cités (prénom, nom, DdN).

J'ai beau lire la documentation, je ne trouve nul part une manière de le faire via FormType. Je n'y arrive que via le controller en ajoutant les champs désirés au formulaire à la main.

Certes, mon formulaire n'est pas réutilisé, mais j'aimerais garder mes formulaires ensembles et ne pas commencer à en avoir certains dans les controllers. Je serai sûrement amené à devoir le faire sur un formulaire réutilisable, donc j'aimerais bien savoir quelle est l'astuce à réaliser. :) Et je n'ai pas envie non plus de faire une seule entité qui contiendrait toutes ces propriétés.

Merci par avance pour vos réponses éclairées !

4 réponses


Trsiel
Auteur
Réponse acceptée

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

Quand tu affiches tes form tu dois faire ceci toi

{{form(form)}}

Ici, ça a affiche l'ensemble des champs qui sont présent dans tes EntityType en brut.

alors que tu dois faire

{{form(NonDeLattrubutVoulu)}}

Ici, tu peux afficher les champs présent dans tes EntityType un par un.
Ceci se fait bien évidemment dans un page *.html.twig càd, dans une vue.

Salut Trsiel,

As tu essayé de diviser ton formulaire final en deux formType (un pour User et l'ature pour UserInfo) ?

Je suppose que tes entités sont de ce genre ?

class User
{
  private $id;

  private $email;

  private $password;

  private $userInfo;

  // ...
}

class UserInfo
{
  private $firstName;

  private $lastName;

  private $birthDate;

  //...
}

Dans ce cas là, la solution la plus simple c'est d'avoir 2 formTypes de ce genre:

class UserType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('email', EmailType::class)
            ->add('userInfo', UserInfoType::class)
            ->add('password', RepeatedType::class, [
                'type'           => PasswordType::class,
                'first_options'  => [
                    'label' => 'Password'
                ],
                'second_options' => [
                    'label' => 'Repeat password'
                ]
            ]);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => User::class
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_user';
    }
}

class UserInfoType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
          ->add('firstName')
          ->add('lastName')
          ->add('birthDate', DateType::class);
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => UserInfo::class
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_user';
    }
}

Pour le rendu,
tu peut soit le faire via le form theme avec un simple

{{ form(form) }}

ou en spécifiant les champs un par un:

{{ form(form.userInfo.firstName) }}
Trsiel
Auteur

Bonjour,

Désolé du temps de réaction, un peu trop de boulot pour me permettre de regarder dans ce projet... Chaque fois c'était "bon, ce week-end je regarde à ça" et... :D
Cette fois, j'ai du temps !

@Mehdikacim: Je sais comment afficher un formulaire. C'est juste l'omission de certains champs qui me posait un soucis. ;)

@kipik: C'est la solution que j'employais pour le moment, mais j'essayais de ne pas devoir créer un formType par action (register, edit, ...) pour cette partie UserInfo. Je crois que je vais être obligé. :/