Symfony3

时间:2017-09-19 14:35:45

标签: php forms symfony validation

我在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."

但是有一些条件验证:

  • 仅在持续时间为" 24"时验证homeAddress。 (这没有 感觉,但我想实现条件验证)
  • 如果hasBankAccounts为"是"
  • ,则验证bankAccounts

因此我添加了一个自定义的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完成表单提交时标记错误。

提前致谢, 延

1 个答案:

答案 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) }}

(如注释掉的那样)我显示了父表单错误。