symfony2表单中可重用的表单选项

时间:2014-06-06 21:30:28

标签: forms symfony

我有以下代码正在运行......但我认为可以做得更好。

(下面的说明)。

Class Address
{
    protected province;

    public function getProvince()...
}

class AddressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        ...
        $build->add(Province, new ProvinceType());
        ...
    }
}

class ProvinceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $provinceList = array(... //very long list
        ...
        $build->add(Province, 'choice', array(
        'choices' => $provinceList;
        ));

   public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\Bundle\Entity\Address'
        ));
    }
}

class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $Province = array(... //very long list
        ...
        $build->add('Address', new AddressType());
        $build->add('FromProvince', new AddressType());
    }
}

上面的代码有两个问题:

  1. 在twig PersonType表单中使用它我必须form_widget(person.Address.ProvinceType.ProvinceType)才能使用它。这看起来很错误。
  2. 要验证省份,我必须采用一种比我应该更深入的方法。
  3. 最后,我希望能够验证有意义的字段,例如:

    Acme\Bundle\Entity\Person:
        property:
            provinceBorn:
                - NotBlank: ~ //Wish to reuse province list here for straight-forward validation.
            Address:
                Valid: ~
    
    Acme\Bundle\Entity\Address:
        property:
            province:
                - NotBlank: ~ //As well as here.
    

3 个答案:

答案 0 :(得分:0)

要缩短ProvinceType的路径,您应该将其定义为扩展Symfony的choice类型的基本小部件(请参阅the doc)。你在这里得到的最好的就是{{ form_widget(person.address.province) }}

为了使选择可重复使用,将ProvinceType提取到服务中是明智的(请参阅Symfony's doc了解如何执行此操作)并将省份列表作为参数传递到{{1我的ProvinceType方法(将在您的包的__construct中定义)。这样,您就可以将省份提取到外部存储中。

在验证时,请记住,您在此处提供的YAML与表单组件无关,而是与您的实体有关。因此,重复services.yml约束实际上是有道理的,因为您没有将NotBlank的{​​{1}}属性与Person实体相关联,而是保存了一个单独的字段。< / p>

但是,如果您定义自定义字段类型,则可以通过将约束移动到您提取到服务的provinceBorn来默认使用它。这样的约束可以这样定义:

Address

答案 1 :(得分:0)

我已经用这种方式做了类似的事情(我将使用你的例子):

    Class Address
{
    protected province;

    public function getProvince()...
}

class AddressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
            $provinceList = array(... //very long list

            ...
        $build->add('province', 'choice', array(
            'choices' => $provinceList, 'empty_value' => null, 'required' => true,
        ));
        ...
    }
}

class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $Province = array(... //very long list
        ...
        $build->add('address', new AddressType());
        $build->add('fromProvince', new AddressType());
    }
}

您发送数据:

    ...
    $form   = $this->createForm(new PersonType(), $entity);

    return array(
        'form'     => $editForm->createView(),
    );

如下所示在Twig中使用它:

{{ form_widget(form.address.province) }}

最后,我认为您的验证是正确的,但如果您需要更具体的内容,则可以在验证部分http://symfony.com/doc/current/book/validation.html#getters中指定Symfony文档中使用getters方法

答案 2 :(得分:0)

之前的扩展答案和解释。

继承表单 http://symfony.com/doc/current/cookbook/form/inherit_data_option.html

代码

使用您的代码,它应该看起来像这样

class AddressType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        ...
        $build->add(Province, new ProvinceType(), [
            'inherit_data' => true 
             // this is an alterrnative for having it bellow inside configure options
             // use one or another, what suit you better
        ]);
        ...
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
         $resolver->setDefaults([
             'inherit_data' => true
         ]);
    }
    // in sf > 2.7 use public function configureOptions(OptionsResolver $resolver)
}

class ProvinceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $provinceList = array(... //very long list
        ...
        $build->add(Province, 'choice', array(
            'choices' => $provinceList;
        ));

   public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\Bundle\Entity\Address',
            'inherit_data' => true
        ));
    }
}

class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $Province = array(... //very long list
        ...
        $build->add('Address', new AddressType());
        $build->add('FromProvince', new AddressType());
    }
}

这是你的addressType从provinceType继承字段,PersonType继承自addressType(包括其从provinceType继承的字段)。

模板

所以应该可以在模板

中做到这一点
 {{ form_row(form.Province)}}

验证

最好的方法是验证约束与Valid约束的关系。 这也会强制对孩子进行验证 http://symfony.com/doc/current/reference/constraints/Valid.html

另一个选项是在表单上设置cascade_validation,但如果有的话,这不会转发您的验证组。

无论哪种方式,您只需要在每个实体上定义一次验证。