(表单)如何使用数据转换器(3.0)

数据转换适用于将一个字段数据格式转换成表单里显示的数据格式(并且可以重复提交)。在symfony内部已经有了很多这样的字段类型。举例,DateType类型在input文本框中被渲染成yyyy-MM-dd格式。在内部,一个数据转换器将开始的DateTime字段的值转换成yyyy-MM-dd字符串渲染到form,并在提交时返回DateTime对象。

注意:当一个表单字段设置了inherit_data配置时,数据转换器将不会应用到这一字段。

简单例子:清除用户输入的HTML

假设你有一个Task表单,有一个textarea类型的description字段:

但,这里有两个复杂的地方:

1.你的用户可能会输入一些html标签,你可能不都需要它:在表单提交之后,你需要调用strip_tags方法。

2.为了友好,在渲染表单时你可能想要把<br />标签转换成换行符\n,使得文本编辑起来更加人性化。

 

这是一个将自定义数据转换到description 字段的好机会。使用 CallbackTransformer这个方法很容易去做到:

CallbackTransformer类用两个回调函数作为参数。第一个函数将原始的值转化为一个能在表单中渲染的格式。第二个函数做了相反的事情:他将提交后获取的值转化为你代码中需要的格式。

提示:这个addModelTransformer()方法接受任何实现DataTransformerInterface接口的对象- 这样你能够创建属于我们自己的类,而不是在表单中放入所有的逻辑(看下一章)。

当添加字段略微改变格式,你也可以添加转换器(transformer):

 

复杂的例子:将Issue编号转化成Isuse实体

比如说你有一个Task实体和一个Issue实体他们是 many-to-one(多对一)的映射关系(好像每一个任务都有一些关联的问题)。添加所有问题到一个listbox,他会变得很长,而且加载时间也变长了。你可以添加一个textbox让用户输入一些问题的编号来解决。

开始我们设置一个文本字段就和平时一样:

一个好的开始!如果你停止在这里,并提交表单,你的Task的issue属性就会是一个字符串(例如 55 )。你怎么把他变成一个实体提交呢?

 

创建转换器

你应该像之前一样使用CallbackTransformer。但是由于这个逻辑有些复杂,创建一个转换器将会使得TaskType表单类更加简单。

创建一个IssueToNumberTransformer类:他将会负责相互转化Issue编号和Issue实体:

就像第一个例子,转换器有两个方法。这个transform()负责将你代码中的数据转换为一个form表单渲染的数据格式(如:一个Issue对象转换为一个id字符串)。这个reverseTransform()方法正好相反:他将提交的数据转换成你代码想要的数据(如:把一个id转换为Issue对象)。

如果验证发生错误,你可以抛出TransformationFailedException。但是这个异常信息就不要给你的用户看了。你使用invalid_message来设置消息(详见下面)。

当null被传递到transform()方法时,你的转换器应该返回一个和它类型相等的值(例如:一个空字符串,整型的0,或者是浮点数0.0)。

 

使用这个转换器

下一步,你将在TaskType中实例化你的IssueToNumberTransformer类并添加他到issue字段。要做到这一点,你将需要一个实体管理(entity manager)(因为IssueToNumberTransformer需要他)。

没有问题!仅仅给TaskType添加一个__construct()函数并把它注册为一个服务传入entity管理即可:

在你的配置文件中定义一个表单类型作为一个服务:

 提示:更多表单类型注册为服务的信息,请阅读 register your form type as a service.

现在,你能够很容易的使用你的TaskType:

酷,你完成了!你的用户将能够在text字段输入一个issue编号来把他转换成一个Issue对象。这意味着,在成功的提交之后,表单组件将会向 Task::setIssue() 传递一个真正的 Issue 对象而不是问题数字。

如果issue没有被找到的话,一个表单字段错误将会产生,并且invalid_message这个字段能够控制错误信息。

注意:当你添加一个转换器时你要小心。举例,下面代码是错误的,由于转换器将会被用于整个表单而不是仅仅这个字段:

 

 

创建一个可以重复使用的 issue_selector 字段:

在上面的例子中,你转换了一个普通的text字段。但如果你要做很多这样的转换,最好是创建一个自定义的表单类型,他就可以自动完成。

首先,创建一个自定义的字段类型类:

好!他将像一个text字段一样渲染(getParent()做了指定),但他自动有一个数据转换器并默认配置invalid_message。

接下来,将你的类型注册为一个服务并标注form.type标签,这样他就被认定为是一个自定义的字段类型了:

现在,无论什么使用你需要使用你的特殊issue_selector字段类型,他都非常的容易:

 

 

关于Model(模型)和View Transformers(视图转换器)

上面的例子的转换器是一个“Model”转换器。实时上,共有两种类型的转换器,又有三种不同类型的基础数据。

DataTransformersTypes

在任何表单中,都有三种不同类型的数据:

1. Model data (模型数据)- 这个数据在你的应用程序内部使用(例如一个Issue对象)。如果你调用Form::getData()或者Form::setData(),你就可以处理模型数据。

2.Norm Data (普通数据) – 这是一个你的普通版本数据,并且这个数据和你的modle数据一样常见(尽管我们的例子中没有)。她通常不会被直接应用。

3.View Data (视图数据) –  这是表单字段自动填充的数据格式。用户也很有可能提交这种格式的数据。当你调用 Form::submit($data)时,$data 就是“视图”格式的数据。

这两种不同类型的转换器可以帮助我们相互转换这些类型数据:

Model transformers:

  • transform: “model data” => “norm data”
  • reverseTransform: “norm data” => “model data”

View transformers:

  • transform: “norm data” => “view data”
  • reverseTransform: “view data” => “norm data”

你需要使用那种转换器取决于你的实际情况。

如果你想使用视图转换器(view transformer)就调用addViewTransformer。

 

为什么在这里要使用模型转换器?

在这个例子中,字段类型是一个text,同时一个text字段总是比较简单,这个格式在“norm”和“view”中。因为在这里model转换器做适合转换(转换表单格式—-字符串issue编号—-模型格式—-Issue对象)。

转换器的区别是微妙的,你应该考虑‘norm’数据字段是什么样子。举例来说,text字段的普通数据就是一个字符串,但是一个date字段就是一个DataTime对象。

提示:一个普遍的规律,规范化的数据应当包含尽可能多的信息。

 

发表评论