简体   繁体   中英

Symfony 6 inheritance Mapping : How to submit a form depends on a clicked radio button?

I have a form and i want submit it depends on the click of a radio button which is a choiceType: I have three entities(User which is the parent of Particuler and Professionnal):

#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\InheritanceType("JOINED")]
#[ORM\DiscriminatorColumn(name:"compte", type: "string")]
#[ORM\DiscriminatorMap(["professionnel"=>Professionel::class, "particulier"=> Particulier::class])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\Column(type: 'string', length: 50)]
    private $typeCompte;

    #[ORM\Column(type: 'string', length: 180, unique: true)]
    #[Assert\NotBlank()]
    #[Assert\Length(min:2, max:80)]
    #[Assert\Email(message:"Choisir un autre email")]
    private $email;
    
    #[ORM\Column(type: 'boolean', nullable: true)]
    private $approve;
    // getters and setters
}
#[ORM\Entity(repositoryClass: ProfessionelRepository::class)]
class Professionel extends User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    private $logo;
   // getters and setters
}
class Particulier extends User
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private $id;

    
    #[ORM\Column(type: 'string', length: 50)]
    #[Assert\NotBlank()]
    #[Assert\Length(min:2, max:50)]
    private $prenom;
   // getters and setters 
}

the corresponding formtype:

class RegistrationFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('typeCompte', ChoiceType::class, [
                'label' => 'Type de compte*',
                'choices'=>[
                    'Particulier' =>'Particulier',
                    'Professionel'=>'Professionel'
                ],
                'data'=>'Particulier',
                'expanded'=> true,
                'multiple'=> false,
                'attr'=>[
                    'class'=>'form-check-input',
                    'name' => 'type_compte'
                ]
              
            ])
            ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
                $data = $event->getData();
                $form = $event->getForm();

                // Set the default value for the "typeCompte" field
                $form->get('typeCompte')->setData('particulier');
            })
            ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
                $data = $event->getData();
                $form = $event->getForm();
                //dd($data);
                // Check the value of the "typeCompte" field
                $user = $event->getForm()->getData();
                dd($user);
                if ($data['typeCompte'] === 'Particulier') {
                    // Add the corresponding fields
                    $form->add('civility', ChoiceType::class, [
                            'label' => 'Civilité',
                            'choices' => [
                                'Madame' => 'Madame',
                                'Madamoiselle' => 'Madamoiselle',
                                'Monsieur' => 'Monsieur'
                            ],
                            'expanded' => true,
                            'multiple' => false,
                            'attr' => [
                                'class' => 'form-check-input',
                            ]

                        ])
                        ->add('prenom', TextType::class, [
                            'label' => 'Prénom*',
                            'attr' => [
                                'placeholder' => 'Votre prénom',
                                'class' => 'form-control'
                            ]
                            ])
                        ->add('nom', TextType::class, [
                            'label' => 'Nom*',
                            'attr' => [
                                'placeholder' => 'Votre nom',
                                'class' => 'form-control'
                            ]
                        ])
                    ;
                } elseif ($data['typeCompte'] === 'professionel') {
                    // Add the corresponding fields
                    $form->add('nomProfessionel', TextType::class, [
                        'label' => 'Concession*',
                        'attr' => [
                            'placeholder' => 'Nom concession',
                            'class' => 'form-control',
                            'id' => 'entreprise',
                        ])
                     
                        ->add('logo', FileType::class, [
                            'label' => 'Logo',
                            'attr' => [
                                'class' => 'form-control',
                                'id' => 'entreprise',
                            ]
                        ])
                    ;
                }
            })
            ->add('email', EmailType::class, [
                'label' => 'Email*',
                'attr' => [
                    'placeholder' => 'Votre email',
                    'class' => 'form-control'
                ]
         
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

the corresponding controller:

#[Route('/Inscription', name: 'app_register')]
    public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher): Response
    {
      $form = $this->createForm(RegistrationFormType::class);
        // dd($form->get('typeCompte')->getData());
        // dd($request);
        $form->handleRequest($request);
       // dd($form->handleRequest($request));
        // dd($form);
        // dd($form->isSubmitted() && $form->isValid());
        if ($form->isSubmitted() && $form->isValid()) {
            dd($form->get('typeCompte')->getData());
            if ($form->get('typeCompte')->getData() === 'particulier'){
                $user = new Particulier();

            }
            if ($form->get('typeCompte')->getData() === 'professionel') {
                $user = new Professionel();
            }
            $form->getData($user);
            // encode the plain password
            $user->setPassword(
            $userPasswordHasher->hashPassword(
                    $user,
                    $form->get('password')->getData()
                )
            );
           

            $this->em->persist($user);
            $this->em->flush();
       return $this->renderForm('registration/register.html.twig', [
            'registrationForm' => $form,
        ]);
}

I have tried to build the form by adding eventlisteners as shown in the formType but i accross a null error when i trying to use dd($data). What i expect is to submit the form whether the user is Particulier or Professionnal by clicking on the typeCompte field.

My answer could look like judgemental but i want to highlight the tech vision of the proper answer instead of the direct solution to a problem that occurred because of a deeper problem.

Your trying to anticipate a multi-function approach without a clear goal about what you're trying to do in the end.

Your issue find it's origin where you try to submit potential different entities with one form type. Which is a bad practice and will lead you to other problem in the futur. In your case, there is also a main problem, one and only one user can be associated with the same company (a Professionel in your case) since the logo is associated with the user

Here is my vision

A user can not be something else than a person (email, firstname, lastname etc...).
So a user can not be a company (a Professionel in your case) or a particular (same as user).
But a user can be associated with a company

With this vision you could completely abandon using inheritance. You will remove a lot of you condition based on the choice of the user. It will simplify a lot your codebase.

Class User:

class User implements UserInterface
{
    
    //...
    #[ORM\ManyToMany(targetEntity: Company::class, mappedBy: 'associatedUsers')]
    private Collection $companies;

    #[ORM\Column(type: 'json')]
    private array $roles = [];
    
}

Class Company:

class Company
{

    #[ORM\Column()]
    private string $name;


    #[ORM\Column()]
    private string $logo;

    #[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'companies')]
    private Collection $associatedUsers;
}

So you can create one form type and no more. This form will submit a user no matter what. The exception is if the user select "professional" in the form, you can display additional field to directly add a company from the user subscription form. Check https://symfony.com/doc/current/form/embedded.html

Basically it is a CompanyType:

class CompanyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', StringType::class)
            ->add('logo', FileType::class)
    }
}

And then inside user type you can add this line:

$builder->add('company', CompanyType::class);

Also for:

Look at mapped false to generate the field but to ignore when persisting entity since you do not really need it inside the database since the association with company gives you the information.

->add('typeCompte', ChoiceType::class, [
                'label' => 'Type de compte*',
                'choices'=>[
                    'Particulier' =>'Particulier',
                    'Professionel'=>'Professionel'
                ],
                'data'=>'Particulier',
                'expanded'=> true,
                'multiple'=> false,
                'mapped' => false, 
                'attr'=>[
                    'class'=>'form-check-input',
                    'name' => 'type_compte'
                ]
              
            ])

Now you have a userType that handle adding a company. And then if the user fill the company fiels, set his role to ROLE_COMPANY as his user role, if not ROLE_USER or any roles according to your need.

Well it is a generic approach that will help you to improve your app and avoid complex homemade use of symfony and his form system.

Feel free to ask anything and i will update my answer. :)

i think you have wrong understanding of the eventlistener in symfony. The PRE_SUBMIT cannot replace / add fields, only can edit the data contained in the model that is going to be submitted.

If i understand you want to have a form with choice type field. Based on the user selection to load fields corresponding to the choice made by the user.

  1. One approach is to make 2 endpoints with 2 different forms, based on the choice type to redirect to Proffesional / Particulier and then to submit the form.

  2. Second is to make a large form combining the two forms with not mapped fields. Then using ajax to hide/show unwanted fields. But you have to handle the submit of the data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM