Symfony3中嵌入式形式的条件配置和验证



我在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) }}

(如所注释的),我会显示父级错误。

最新更新