php Symfony2:如何对自定义复合表单类型使用约束?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21486538/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Symfony2: How to use constraints on custom compound form type?
提问by Maurice
Here is a question I've been breaking my head over for a while now. Please know that I'm not a Symfony2 expert (yet), so I might have made a rookie mistake somewhere.
这是一个我已经困扰了一段时间的问题。请知道我还不是 Symfony2 专家(还),所以我可能在某个地方犯了一个新手错误。
Field1: Standard Symfony2 textfield type
Field1:标准 Symfony2text字段类型
Field2: Custom field type compoundfield with textfield + checkboxfield)
Field2:compound带有text字段+checkbox字段的自定义字段类型字段)


My Goal:Getting constraints added to the autoValuefield to work on the autoValue's text input child
我的目标:将约束添加到该autoValue领域以处理autoValue's text input child
The reason why the constraints don't work is probably because NotBlankis expecting a string value and the internal data of this form field is an array array('input'=>'value', 'checkbox' => true). This array value gets transformed back into a string with a custom DataTransformer. I suspect however that that happens AFTER validating the field against known constraints.
约束不起作用的原因可能是因为NotBlank期望一个字符串值,而这个表单字段的内部数据是一个数组array('input'=>'value', 'checkbox' => true)。这个数组值被转换回一个带有自定义的字符串DataTransformer。然而,我怀疑这是在根据已知约束验证该字段之后发生的。
As you see below in commented code, I have been able to get constraints working on the text input, however only when hardcoded into the autoValue's form type, and I want to validate against the main field's constraints.
正如您在下面的注释代码中看到的那样,我已经能够在文本输入上设置约束,但是只有在硬编码到 autoValue 的表单类型中时,并且我想针对主字段的约束进行验证。
My (simplified) sample code for controller and field:
我的(简化的)控制器和字段示例代码:
.
.
Controller code
控制器代码
Setting up a quick form for testing purposes.
为测试目的设置快速表单。
<?php
//...
// $entityInstance holds an entity that has it's own constraints
// that have been added via annotations
$formBuilder = $this->createFormBuilder( $entityInstance, array(
'attr' => array(
// added to disable html5 validation
'novalidate' => 'novalidate'
)
));
$formBuilder->add('regular_text', 'text', array(
'constraints' => array(
new \Symfony\Component\Validator\Constraints\NotBlank()
)
));
$formBuilder->add('auto_text', 'textWithAutoValue', array(
'constraints' => array(
new \Symfony\Component\Validator\Constraints\NotBlank()
)
));
.
.
TextWithAutoValue source files
TextWithAutoValue 源文件
src/My/Component/Form/Type/TextWithAutoValueType.php
src/My/Component/Form/Type/TextWithAutoValueType.php
<?php
namespace My\Component\Form\Type;
use My\Component\Form\DataTransformer\TextWithAutoValueTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class TextWithAutoValueType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('value', 'text', array(
// when I uncomment this, the NotBlank constraint works. I just
// want to validate against whatever constraints are added to the
// main form field 'auto_text' instead of hardcoding them here
// 'constraints' => array(
// new \Symfony\Component\Validator\Constraints\NotBlank()
// )
));
$builder->add('checkbox', 'checkbox', array(
));
$builder->addModelTransformer(
new TextWithAutoValueTransformer()
);
}
public function getName()
{
return 'textWithAutoValue';
}
}
src/My/Component/Form/DataTransformer/TextWithAutoValueType.php
src/My/Component/Form/DataTransformer/TextWithAutoValueType.php
<?php
namespace My\Component\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
class TextWithAutoValueTransformer
implements DataTransformerInterface
{
/**
* @inheritdoc
*/
public function transform($value)
{
return array(
'value' => (string) $value,
'checkbox' => true
);
}
/**
* @inheritdoc
*/
public function reverseTransform($value)
{
return $value['value'];
}
}
src/My/ComponentBundle/Resources/config/services.yml
src/My/ComponentBundle/Resources/config/services.yml
parameters:
services:
my_component.form.type.textWithAutoValue:
class: My\Component\Form\Type\TextWithAutoValueType
tags:
- { name: form.type, alias: textWithAutoValue }
src/My/ComponentBundle/Resources/views/Form/fields.html.twig
src/My/ComponentBundle/Resources/views/Form/fields.html.twig
{% block textWithAutoValue_widget %}
{% spaceless %}
{{ form_widget(form.value) }}
{{ form_widget(form.checkbox) }}
<label for="{{ form.checkbox.vars.id}}">use default value</label>
{% endspaceless %}
{% endblock %}
.
.
Question
题
I have been reading docs and google for quite some hours now and can't figure out how to copy, bind, or reference the original constraints that have been added while building this form.
我已经阅读文档和谷歌几个小时了,但不知道如何复制、绑定或引用在构建此表单时添加的原始约束。
-> Does anyone know how to accomplish this?
-> 有谁知道如何做到这一点?
-> For bonus points; how to enable the constraints that have been added to the main form's bound entity? (via annotations on the entity class)
-> 奖励积分;如何启用已添加到主窗体绑定实体的约束?(通过实体类上的注释)
PS
聚苯乙烯
Sorry it became such a long question, I hope that I succeeded in making my issue clear. If not, please ask me for more details!
对不起,它变成了一个这么长的问题,我希望我成功地把我的问题说清楚了。如果没有,请向我询问更多详情!
采纳答案by Zephyr
I suggest you read again the documentation about validationfirst.
What we can make out of this is that validation primarily occurs on classes rather than form types. That is what you overlooked. What you need to do is:
我们可以从中得出的是,验证主要发生在类而不是表单类型上。那是你忽略了。你需要做的是:
- To create a data class for your TextWithAutoValueType, called src/My/Bundle/Form/Model/TextWithAutoValuefor instance. It must contain properties called textand checkboxand their setters/getters;
- To associate this data class to your form type. For this, you must create a TextWithAutoValueType::getDefaultOptions()method and populate the data_classoption. Go herefor more info about this method;
- Create validation for your data class. You can either use annotations or a Resources/config/validation.ymlfile for this. Instead of associating your constraints to the fields of your form, you must associate them to the properties of your class:
- 为您的TextWithAutoValueType创建一个数据类,例如名为src/My/Bundle/Form/Model/TextWithAutoValue。它必须包含称为text和checkbox 的属性以及它们的 setter/getter;
- 将此数据类与您的表单类型相关联。为此,您必须创建一个TextWithAutoValueType::getDefaultOptions()方法并填充data_class选项。转到此处了解有关此方法的更多信息;
- 为您的数据类创建验证。为此,您可以使用注释或Resources/config/validation.yml文件。不是将您的约束与表单的字段相关联,而是必须将它们与您的类的属性相关联:
validation.yml:
验证.yml:
src/My/Bundle/Form/Model/TextWithAutoValue:
properties:
text:
- Type:
type: string
- NotBlank: ~
checkbox:
- Type:
type: boolean
Edit:
编辑:
I assume that you already know how to use a form type in another. When defining your validation configuration, you can use a very useful something, called validation groups. Here a basic example (in a validation.ymlfile, since I'm not much proficient with validation annotations):
我假设您已经知道如何在另一个表单类型中使用。在定义验证配置时,您可以使用一个非常有用的东西,称为验证组。这是一个基本示例(在validation.yml文件中,因为我不太精通验证注释):
src/My/Bundle/Form/Model/TextWithAutoValue:
properties:
text:
- Type:
type: string
groups: [ Default, Create, Edit ]
- NotBlank:
groups: [ Edit ]
checkbox:
- Type:
type: boolean
There is a groupsparameter that can be added to every constraint. It is an array containing validation group names. When requesting a validation on an object, you can specify with which set of groups you want to validate. The system will then look in the validation file what constraints should be applied.
有一个组参数可以添加到每个约束中。它是一个包含验证组名称的数组。请求对对象进行验证时,您可以指定要验证的组集。然后系统将在验证文件中查看应该应用哪些约束。
By default, the "Default" group is set on all constraints. This also is the group that is used when performing a regular validation.
默认情况下,所有约束都设置了“默认”组。这也是执行常规验证时使用的组。
- You can specify the default groups of a specific form type in MyFormType::getDefaultOptions(), by setting the validation_groupsparameter (an array of strings - names of validation groups),
- When appending a form type to another, in MyFormType::buildForm(), you can use specific validation groups.
- 您可以在MyFormType::getDefaultOptions() 中指定特定表单类型的默认组,通过设置validation_groups参数(字符串数组 - 验证组的名称),
- 将表单类型附加到另一个表单类型时,在MyFormType::buildForm() 中,您可以使用特定的验证组。
This, of course, is the standard behaviour for all form type options. An example:
当然,这是所有表单类型选项的标准行为。一个例子:
$formBuilder->add('auto_text', 'textWithAutoValue', array(
'label' => 'my_label',
'validation_groups' => array('Default', 'Edit'),
));
As for the use of different entities, you can pile up your data classes following the same architecture than your piled-up forms. In the example above, a form type using textWithAutoValueTypewill have to have a data_class that has a 'auto_text' property and the corresponding getter/setter.
至于不同实体的使用,您可以按照与堆积表格相同的体系结构堆积您的数据类。在上面的例子中,使用textWithAutoValueType的表单类型必须有一个 data_class,它有一个 'auto_text' 属性和相应的 getter/setter。
In the validation file, the Validconstraints will be able to cascade validation. A property with Validwill detect the class of the property and will try to find a corresponding validation configuration for this class, and apply it with the same validation groups:
在验证文件中,Valid约束将能够级联验证。带有Valid的属性将检测该属性的类,并会尝试为此类找到相应的验证配置,并将其应用于相同的验证组:
src/My/Bundle/Form/Model/ContainerDataClass:
properties:
auto_text:
Valid: ~ # Will call the validation conf just below with the same groups
src/My/Bundle/Form/Model/TextWithAutoValue:
properties:
... etc
回答by Asmir Mustafic
As described here https://speakerdeck.com/bschussek/3-steps-to-symfony2-form-mastery#39(slide 39) by Bernhard Schussek (the main contributor of symofny form extension), a transformer should never change the information, but only change its representation.
如此处所述 https://speakerdeck.com/bschussek/3-steps-to-symfony2-form-mastery#39(幻灯片 39)由 Bernhard Schussek(symofny 表单扩展的主要贡献者)撰写,转换器不应更改信息,但只改变它的表示。
Adding the information (checkbox' => true), you are doing something wrong.
添加信息(复选框'=> true),你做错了什么。
In Edit:
在编辑中:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('value', 'text', $options);
$builder->add('checkbox', 'checkbox', array('mapped'=>false));
$builder->addModelTransformer(
new TextWithAutoValueTransformer()
);
}

