表单规范数据与视图数据——有什么区别?

Form Norm Data vs. View Data - what's the difference?

The documentation 说:

In any form, the three different types of data are:

    Model data - This is the data in the format used in your application (e.g. an Issue object). If you call Form::getData() or Form::setData(), you're dealing with the "model" data.
    Norm Data - This is a normalized version of your data and is commonly the same as your "model" data (though not in our example). It's not commonly used directly.
    View Data - This is the format that's used to fill in the form fields themselves. It's also the format in which the user will submit the data. When you call Form::submit($data), the $data is in the "view" data format.

但是我不明白常模数据和视图数据的区别。常模数据的用例是什么?文档的下一段 "So why Use the Model Transformer?" 对我来说没有任何意义。

让我们以 DateTimeType 小部件为例。

模型数据是Datetime

标准数据是array (in the documentation)

视图数据是 string(例如输入文本)

我找到的最好的解释是在 Symfony 的表格 class 描述中:

<?php

...

namespace Symfony\Component\Form;

use ...

/**
 * Form represents a form.
 *
 * To implement your own form fields, you need to have a thorough understanding
 * of the data flow within a form. A form stores its data in three different
 * representations:
 *
 *   (1) the "model" format required by the form's object
 *   (2) the "normalized" format for internal processing
 *   (3) the "view" format used for display
 *
 * A date field, for example, may store a date as "Y-m-d" string (1) in the
 * object. To facilitate processing in the field, this value is normalized
 * to a DateTime object (2). In the HTML representation of your form, a
 * localized string (3) is presented to and modified by the user.
 *
 * In most cases, format (1) and format (2) will be the same. For example,
 * a checkbox field uses a Boolean value for both internal processing and
 * storage in the object. In these cases you simply need to set a value
 * transformer to convert between formats (2) and (3). You can do this by
 * calling addViewTransformer().
 *
 * In some cases though it makes sense to make format (1) configurable. To
 * demonstrate this, let's extend our above date field to store the value
 * either as "Y-m-d" string or as timestamp. Internally we still want to
 * use a DateTime object for processing. To convert the data from string/integer
 * to DateTime you can set a normalization transformer by calling
 * addNormTransformer(). The normalized data is then converted to the displayed
 * data as described before.
 *
 * The conversions (1) -> (2) -> (3) use the transform methods of the transformers.
 * The conversions (3) -> (2) -> (1) use the reverseTransform methods of the transformers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Form implements \IteratorAggregate, FormInterface
{
    ...
}

编辑:这是来自 Sf 表单组件(合)作者的视频,他在其中解释了数据格式并给出了规范化格式使用的示例 -> https://youtu.be/Q80b9XeLUEA?t=7m6s


所以,基本上,每个表单都有三种不同格式的数据。前两个是:模型数据 - 您在域模型中使用的数据,以及视图数据 - 您通常在 HTML 中输出为字符串或在提交前输入表单字段的数据。

两个简单的示例是 TextType 和 NumberType。 TextType中,模型数据为字符串,视图数据为字符串。

在 NumberType 中,模型数据是浮点数 - 在您的应用程序中,您处理的是浮点数类型,而视图数据是本地化的字符串。

但是,另一方面,如果我们看一下 DateType,事情会有点复杂,因为模型数据可能是字符串 ('YYYY-MM-DD')、整数(unix 时间戳)、数组或 DateTime 对象。视图数据可以是本地化字符串或数组。

例如,如果你想写一个事件监听器,你不能确定你会得到什么数据格式:

class MyDateType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add...
            ->addEventListener(
                FormEvents::FormEvents::SUBMIT,
                array($this, 'onSubmit')
            )
        ;
    }

    public function onSubmit(FormEvent $event)
    {
        // BUT WHAT FORMAT DO I GET???
        $data = $event->getForm()->getData();
    }
}

幸运的是,Sf表格还有第三种数据格式——归一化格式。该格式是静态的 - 您始终知道您将获得什么,它始终是相同的数据类型!

 public function onSubmit(FormEvent $event)
 {
     // $data will be DateTime object!
     $data = $event->getForm()->getNormData();
 }

简短的解释/简化,当我开始学习 symfony 形式时,我曾经用来记住这些事情:

  • 模型数据:Model/Entity/Whatever形式的数据。就是你的数据源数据格式
  • Norm数据:Form字段表单中的数据。如果你有CollectionType,例如,你有一个数组,如果你有一个DateTimeType,你有一个DateTime等等
  • 查看数据:查看表单中的数据。

基本上,如果您的格式 "consistent" 介于这些 "layers" 之间,您永远(*)不需要创建转换器。

只是为每种类型做一个例子:

型号 --> 标准

如果您有一个实体(模型数据)并且您的表单中有一个EntityType字段(规范数据 ), 你不需要使用任何变压器

如果您有一个实体(模型数据)并且您有一个 TextType 字段来表示您的实体,您需要使用 ModelTransformer 作为你的格式不一致

规范 --> 视图

(*) 任何 DateTypeDateTimeType 都有一个直接插入数据类型的转换器:用于从视图(用户)日期格式(时区)和规范(或模型)格式转换(时区)

正如您在此处看到的,转换不仅在非一致数据类型之间发生,而且在同一类型内发生。

你也可以检查this presentation

提醒一句,ViewData 可以变成 ModelData,反之亦然,这完全违背了此处提供的解释,您可能会感到困惑。它发生在几乎所有真实的单词案例中。

这取决于形式是否复合。复合表单就是有多个字段的表单,所以基本上都是。

参见:

line 634 send to ViewTransformer mix of view data (from main form) and model data (from children forms). Is it really expected?

是的 :) 原因我在上面已经解释过了。

继续:

简单表单=>查看数据==请求数据

复合形式=>查看数据==(data_class|数组)映射子模型数据,使用DataMapper。

来源:https://github.com/symfony/symfony/issues/22371#issuecomment-293494324

并且:

Internally the compound form uses its view data to map the data of the children.

我做了更多的研究:

As you say the compound option determines if PropertyPathMapper will be used. That's OK. ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)

Also as you say the Form class is using the mapper to map the view data to the forms. That's OK too. $this->config->getDataMapper()->mapDataToForms($viewData, $iterator);

Then PropertyPathMapper mapper is setting the data on the forms. Hmm... $form->setData($this->propertyAccessor->getValue($data, $propertyPath));

Why PropertyPathMapper trough Form is setting ViewData on forms that expect ModelData? public function setData($modelData);

mapper中的ViewData是怎么变成ModelData的?为什么 ModelData 没有映射到表单上?为什么 data_class 适用于 ViewData 而不是 ModelData 更有意义?

来源:https://github.com/symfony/symfony/issues/20880#issuecomment-267129575