我在symfony3的形式和验证组件上挣扎。我尝试使用嵌入式儿童表格(地址),子女表格(银行帐户)和有条件的阀门设置相当复杂的申请表(为简单起见,我将其剥离)。
我不使用学说实体。所有形式实体都是普通的旧php对象。
1)表单实体
我使用的一个应用程序具有HomeadDress属性Whoch与地址实体和BankAccounts属性相关的应用程序,该属性是一系列BankAccount实体。
// AppBundle/Form/Application
namespace AppBundleForm;
use SymfonyComponentValidatorContextExecutionContextInterface;
use SymfonyComponentValidatorMappingClassMetadata;
class Application
{
protected $amount;
protected $duration;
protected $email;
protected $hasBankAccounts;
protected $bankAccounts;
protected $homeAddress;
public function __construct()
{
$this->bankAccounts = [];
}
... some getters and setters ...
public function setHomeAddress(Address $homeAddress = null)
{
$this->homeAddress = $homeAddress;
}
public function addBankAccount(BankAccount $bankAccount)
{
$this->bankAccounts[] = $bankAccount;
}
}
// AppBundle/Form/Address
namespace AppBundleForm;
class Address
{
protected $street;
protected $houseNumber;
protected $zip;
protected $city;
... getters and setters ...
}
// AppBundle/Form/BankAccount
namespace AppBundleForm;
class BankAccount
{
protected $iban;
protected $bic;
protected $bankName;
protected $holder;
... getters and setters ...
}
2)formType类是:
// AppBundle/Form/ApplicationType
namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentFormFormEvents;
use SymfonyComponentFormFormEvent;
use SymfonyComponentFormExtensionCoreTypeIntegerType;
use SymfonyComponentFormExtensionCoreTypeEmailType;
use SymfonyComponentFormExtensionCoreTypeChoiceType;
use SymfonyComponentFormExtensionCoreTypeCollectionType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
use SymfonyComponentOptionsResolverOptionsResolver;
class ApplicationType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Application::class,
'attr' => ['novalidate' => 'novalidate'],
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('homeAddress', AddressType::class, [
'error_bubbling' => false,
])
->add('bankAccounts', CollectionType::class, [
'entry_type' => BankAccountType::class,
'entry_options' => ['label' => false],
'error_bubbling' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
])
->add('amount', IntegerType::class, ['label' => 'Kreditsumme'])
->add('duration', ChoiceType::class, ['choices' => $duration])
->add('email', EmailType::class)
->add('hasBankAccounts', ChoiceType::class, ['choices' => ['nein' => 'n', 'ja' => 'y'], 'expanded' => true, 'multiple' => false])
->add('save', SubmitType::class, array('label' => 'Weiter'))
->getForm()
;
}
}
// AppBundle/Form/AddressType
namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
class AddressType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Address::class,
'attr' => ['novalidate' => 'novalidate'],
'error_bubbling' => false,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('street', TextType::class)
->add('houseNumber', TextType::class)
->add('zip', TextType::class)
->add('city', TextType::class)
;
}
}
// AppBundle/Form/BankAccountType
namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
class BankAccountType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => BankAccount::class,
'attr' => ['novalidate' => 'novalidate'],
'error_bubbling' => false,
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('iban', TextType::class)
->add('bic', TextType::class, ['disabled' => true])
->add('bankName', TextType::class, ['disabled' => true])
->add('holder', TextType::class)
;
}
}
3)验证我在验证中定义了验证。
AppBundleFormApplication:
constraints:
- AppBundleValidatorConstraintsApplication: ~
properties:
# homeAddress:
# - Valid: ~
# bankAccounts:
# - Valid: ~
amount:
- NotBlank:
message: "Bitte eine Kreditsumme angeben."
- Length:
min: 5000
max: 50000
minMessage: "Kreditsumme zu niedrig."
maxMessage: "Kreditsumme zu hoch."
email:
- NotBlank:
message: Bitte geben Sie eine E-Mail-Adresse an.
- Email:
message: Bitte geben Sie eine korrekte E-Mail an.
hasBankAccounts:
- NotBlank:
message: Bitte geben Sie an, ob Sie Bankkonten haben.
AppBundleFormAddress:
properties:
street:
- NotBlank:
message: "Bitte eine Straße an."
houseNumber:
- NotBlank:
message: "Bitte eine Hausnummer an."
zip:
- NotBlank:
message: "Bitte eine PLZ an."
city:
- NotBlank:
message: "Bitte einen Ort an."
AppBundleFormBankAccount:
properties:
iban:
- NotBlank:
message: "Bitte eine IBAN an."
holder:
- NotBlank:
message: "Bitte Inhaber angeben."
但是有一些有条件的验证:
- 仅在持续时间为" 24"时验证Homeaddress(这没有有意义,但我想实施有条件验证)
- 如果HasbankAccounts为"是"
因此,我添加了一个自定义应用程序效果(作为服务)来处理此操作。
// AppBundle/Validator/Constraints
namespace AppBundleValidatorConstraints;
use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintValidator;
use SymfonyComponentValidatorConstraints as Assert;
use AppBundleFormApplication as ApplicationFormEntity;
class ApplicationValidator extends ConstraintValidator
{
public function validate($object, Constraint $constraint)
{
if ($object->getDuration() == 24) {
$this->context
->getValidator()
->inContext($this->context)
->validate($object->getHomeAddress(), new AssertValid());
}
if ($object->hasBankAccounts()) {
$this->context
->getValidator()
->inContext($this->context)
->validate($object->getBankAccounts(), new AssertValid())
;
}
}
}
此代码有效,但存在一些问题。如果我执行上述条件验证,则错误,例如尽管我将error_bubbling设置为false,但空地址字段的"绑定"为"绑定"。
如果我对其进行评论,而是使用验证中的有效约束。
AppBundleFormApplication:
properties:
homeAddress:
- Valid: ~
bankAccounts:
- Valid: ~
形式错误正确地绑定到儿童形式(但不再有条件)。
如何正确"绑定"错误?我认为我需要在自定义应用程序validator中使用executionContext,但我不知道如何。
除此之外
预先感谢Jens
模板代码如下:
{% block content %}
{#{{ form_start(form) }}#}
{#{{ form_widget(form) }}#}
{#{{ form_end(form) }}#}
{{ form_start(form) }}
{{ form_row(form.amount) }}
{{ form_row(form.duration) }}
{{ form_row(form.email) }}
{{ form_row(form.occupation) }}
{{ form_row(form.homeAddress) }}
{{ form_row(form.employer) }}
{{ form_row(form.hasBankAccounts) }}
{{ form_row(form.bankAccounts) }}
{% endblock %}
如果我使用
{{ form_widget(form) }}
(如所注释的),我会显示父级错误。