Приложение Symfony 4 имеет сущность User, использующую Joined InheritanceType с типами Admin
, Staff
и Volunteer
. Сущность Staff
имеет отношение OneToOne с сущностью Organization
. Таким образом, тип формы организации включает дочерний элемент staff
, который имеет пользовательский тип формы NewUserType
. (Соответствующие выдержки приведены ниже.) Если отправлена пустая форма OrganizationType
, все ожидаемые ошибки формы отображаются, КРОМЕ имени и ошибок электронной почты. Поле password
формы ошибки NewUserType
отображается. Профилировщик Symfony не показывает ошибки для имени или электронной почты. error_bubbling
в NewUserType
не имеет значения. @Assert\Valid
в свойстве staff
объекта Organization не имеет значения.
registerOrganization:
public function registerOrganiztion(Request $request) {
$form = $this->createForm(OrganizationType::class);
$templates = [
'Registration/organization.html.twig',
'Registration/new_user.html.twig',
'Registration/focuses.html.twig',
];
...
return $this->render('Default/formTemplates.html.twig', [
'form' => $form->createView(),
'headerText' => 'Add an organization',
'userHeader' => 'Staff Member',
'orgHeader' => 'Organization',
'focusHeader' => "Organization's Focus",
'templates' => $templates,
]);
}
new_user.html.twig:
{% if form.staff is defined %}{% set user = form.staff %}{% else %}{% set user = form %}{% endif %}
<div class="text-center text-bold">
{{ userHeader }}
</div>
{{ form_row(user.fname) }}
{{ form_row(user.sname) }}
{{ form_row(user.email) }}
{{ form_row(user.plainPassword.first) }}
{{ form_row(user.plainPassword.second) }}
OrganizationType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
...
->add('staff', NewUserType::class)
}
NewUserType:
class NewUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('sname', null, [
'attr' => [
'class' => 'mb-2',
'size' => '15',
],
'label' => 'Last name: ',
'label_attr' => ['class' => 'mr-2'],
])
->add('fname', null, [
'attr' => [
'class' => 'mb-2',
'size' => '15',
],
'label' => 'First name: ',
'label_attr' => ['class' => 'mr-2'],
])
->add('email', null, [
'attr' => [
'class' => 'mb-2',
'size' => '15',
],
'label' => 'Email: ',
'label_attr' => ['class' => 'mr-2'],
])
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'mapped' => false,
'constraints' => [new NotBlank(['message' => "Password may not empty"])],
'invalid_message' => 'Passwords do not match',
'first_options' => [
'attr' => [
'class' => 'mb-2',
'size' => '15',
],
'label' => 'Password:',
'label_attr' => ['class' => 'mr-2'],
'required' => true,
],
'second_options' => [
'attr' => [
'class' => 'mb-2',
'size' => '15',
],
'label' => 'Confirm:',
'label_attr' => ['class' => 'mr-2'],
'required' => true,
],
))
;
if (Volunteer::class === $options['data_class']) {
$builder
->add('focuses', FocusFieldType::class)
->add('skills', SkillFieldType::class)
;
}
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class' => Staff::class,
'required' => false,
'error_bubbling' => true,
]);
}
}
Субъект организации
/**
* Organization
*
* @ORM\Table(name="organization")
* @ORM\Entity
*/
class Organization
{
...
/**
* @ORM\OneToOne(targetEntity="Staff", inversedBy="organization")
* @ORM\JoinColumn(name="staff_id", referencedColumnName="id")
* @Assert\Valid
*/
protected $staff;
...
}
Служебный объект
/**
* Staff
*
* @ORM\Table(name="staff")
* @ORM\Entity
*/
class Staff extends User
{
...
/**
* @ORM\OneToOne(targetEntity="Organization", mappedBy="staff")
*/
protected $organization;
public function getOrganization()
{
return $this->organization;
}
public function setOrganization(Organization $organization = null)
{
$this->organization = $organization;
return $this;
}
}