Symfony 6继承映射:如何提交表单依赖于点击单选按钮?



我有一个表单,我想提交它取决于一个单选按钮,这是一个选择类型的点击:我有三个实体(User是particer和professional的父实体):

#[ORMEntity(repositoryClass: UserRepository::class)]
#[ORMInheritanceType("JOINED")]
#[ORMDiscriminatorColumn(name:"compte", type: "string")]
#[ORMDiscriminatorMap(["professionnel"=>Professionel::class, "particulier"=> Particulier::class])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn(type: 'integer')]
private $id;
#[ORMColumn(type: 'string', length: 50)]
private $typeCompte;
#[ORMColumn(type: 'string', length: 180, unique: true)]
#[AssertNotBlank()]
#[AssertLength(min:2, max:80)]
#[AssertEmail(message:"Choisir un autre email")]
private $email;

#[ORMColumn(type: 'boolean', nullable: true)]
private $approve;
// getters and setters
}
#[ORMEntity(repositoryClass: ProfessionelRepository::class)]
class Professionel extends User
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn(type: 'integer')]
private $id;
#[ORMColumn(type: 'string', length: 255, nullable: true)]
private $logo;
// getters and setters
}
class Particulier extends User
{
#[ORMId]
#[ORMGeneratedValue]
#[ORMColumn(type: 'integer')]
private $id;

#[ORMColumn(type: 'string', length: 50)]
#[AssertNotBlank()]
#[AssertLength(min:2, max:50)]
private $prenom;
// getters and setters 
}

对应的格式类型:

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,
]);
}
}

对应的控制器:

#[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,
]);
}

我试图通过添加事件监听器来构建表单,如formType所示,但当我试图使用dd($data)时,我遇到了一个null错误。我所期望的是提交表单,无论用户是Particulier还是professional通过点击typeCompte字段。

我的回答可能看起来很武断,但我想强调正确答案的技术视角,而不是直接解决由更深层次问题引起的问题。

你试图预测一个多功能的方法,却没有一个明确的目标,你最终要做什么。

你的问题找到它的起源,你试图提交一个表单类型的潜在不同的实体。这是一个不好的做法,并将导致你在未来的其他问题。在您的示例中,还有一个主要问题,由于logo与用户

相关联,因此一个且只有一个用户可以与相同的company(在您的示例中为Professionel

)相关联。这是我的愿景

一个user不能是一个人以外的东西(电子邮件,名字,姓氏等)。
因此,user不能是company(在您的情况下是Professionel)或particular(与用户相同)。
但是user可以与company相关联

在这种情况下,您可以完全放弃使用继承。您将根据用户的选择删除许多您的条件。它将大大简化你的代码库。

类用户:

class User implements UserInterface
{

//...
#[ORMManyToMany(targetEntity: Company::class, mappedBy: 'associatedUsers')]
private Collection $companies;
#[ORMColumn(type: 'json')]
private array $roles = [];

}

班级公司:

class Company
{
#[ORMColumn()]
private string $name;

#[ORMColumn()]
private string $logo;
#[ORMManyToMany(targetEntity: User::class, inversedBy: 'companies')]
private Collection $associatedUsers;
}

因此,您可以创建一个表单类型,而不是更多。无论如何,此表单都会提交user。例外情况是,如果用户选择"专业";在表单中,您可以显示额外的字段来直接从用户订阅表单中添加company。检查https://symfony.com/doc/current/form/embedded.html

基本上它是一个CompanyType:

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

在user type里面你可以加上这一行:

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

也可用于:

查看映射的false以生成字段,但在持久化实体时忽略它,因为您在数据库中并不真正需要它,因为与company的关联为您提供了信息。

->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'
]

])

现在您有一个userType来处理添加公司。然后,如果用户填写了公司字段,将他的角色设置为ROLE_COMPANY作为他的用户角色,如果不是ROLE_USER或根据您的需要设置任何角色。

这是一个通用的方法,可以帮助你改进你的应用程序,避免复杂的自制使用symfony和他的表单系统。

请随便问任何问题,我会更新我的答案。:)

我认为你对symfony中的eventlistener有错误的理解。PRE_SUBMIT不能替换/添加字段,只能编辑将要提交的模型中包含的数据。

如果我理解你想有一个选择类型字段的表单。根据用户的选择来加载与用户的选择相对应的字段。

  1. 一种方法是使用2种不同的表单创建2个端点,基于选择类型重定向到professional/Particulier然后提交表单。

  2. 第二种是将两个未映射字段的表单组合成一个大表单。然后使用ajax隐藏/显示不需要的字段。但是你必须处理数据的提交。

最新更新