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.
CodePudding user response:
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. :)
CodePudding user response:
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.
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.
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.