我在Symfony3的表单和验证组件上苦苦挣扎。我尝试使用嵌入式子表单(地址),子表单集合(银行帐户)和条件语句来设置一个相当复杂的应用程序表单(为了简单起见,我将其删除)。
我不使用Doctrine实体。所有表单实体都是普通的旧PHP对象。
1)表单实体
我使用的应用程序具有与Address实体相关的homeAddress属性,以及bankAccounts属性,该属性是BankAccount实体的数组。
// AppBundle/Form/Application
namespace AppBundle\Form;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;
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 AppBundle\Form;
class Address
{
protected $street;
protected $houseNumber;
protected $zip;
protected $city;
... getters and setters ...
}
// AppBundle/Form/BankAccount
namespace AppBundle\Form;
class BankAccount
{
protected $iban;
protected $bic;
protected $bankName;
protected $holder;
... getters and setters ...
}
2)FormType类如下:
// AppBundle/Form/ApplicationType
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
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 AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
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 AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
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)验证 我在validation.yml中定义了验证:
AppBundle\Form\Application:
constraints:
- AppBundle\Validator\Constraints\Application: ~
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.
AppBundle\Form\Address:
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."
AppBundle\Form\BankAccount:
properties:
iban:
- NotBlank:
message: "Bitte eine IBAN an."
holder:
- NotBlank:
message: "Bitte Inhaber angeben."
但是有一些条件验证:
因此我添加了一个自定义的ApplicationValidator(作为服务)来处理这个问题。
// AppBundle/Validator/Constraints
namespace AppBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Constraints as Assert;
use AppBundle\Form\Application 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 Assert\Valid());
}
if ($object->hasBankAccounts()) {
$this->context
->getValidator()
->inContext($this->context)
->validate($object->getBankAccounts(), new Assert\Valid())
;
}
}
}
此代码有效但有一些问题。如果我执行上述条件验证,则错误例如空地址字段是"绑定"尽管我将error_bubbling设置为false。
如果我评论它,而是使用validation.yml中的Valid约束:
AppBundle\Form\Application:
properties:
homeAddress:
- Valid: ~
bankAccounts:
- Valid: ~
表单错误正确绑定到子表单(但它们不再是条件表单)。
我怎样才能"绑定"错误是否正确?我想我需要在我的自定义ApplicationValidator中使用ExecutionContext,但我不知道如何。
除此之外,我需要知道如何获取嵌套的错误数组,将其用作我前端的json数据,以便在通过ajax完成表单提交时标记错误。
提前致谢, 延
答案 0 :(得分:0)
模板代码如下:
{% 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) }}
(如注释掉的那样)我显示了父表单错误。