Forms 无法将实体转换为表单中使用的不可变值对象字段

Forms 无法将实体转换为表单中使用的不可变值对象字段,forms,symfony,doctrine-orm,silex,Forms,Symfony,Doctrine Orm,Silex,我对一个相当复杂的表格有困难。这种形式的子对象是一个值对象,与条令文档中的内容非常相似(即地址值对象,带有文本字段和国家实体字段)。我可以毫不费力地用Address-Value对象的每个字段显示表单,但是当我提交表单时,会抛出一个属性访问异常,表示Value对象上既没有属性国家,也没有任何setter方法。由于Value对象是不可变的,因此应出现此异常 $addressFormBuilder = $app['form.factory']->createNamedBuilder('ad

我对一个相当复杂的表格有困难。这种形式的子对象是一个值对象,与条令文档中的内容非常相似(即地址值对象,带有文本字段和国家实体字段)。我可以毫不费力地用Address-Value对象的每个字段显示表单,但是当我提交表单时,会抛出一个属性访问异常,表示Value对象上既没有属性国家,也没有任何setter方法。由于Value对象是不可变的,因此应出现此异常

   $addressFormBuilder = $app['form.factory']->createNamedBuilder('address', 'form', new Address('street','postalCode','city','region',$country_1), [
        'data_class' => 'Address'
    ]);
   $addressFormBuilder
        ->add('street', 'text', ['constraints' => [new Assert\NotBlank ,new Assert\Length(['max' => 50])]])
        ->add('postal_code', 'text', ['constraints' => [new Assert\NotBlank ,new Assert\Length(['max' => 50])]])
        ->add('city', 'text', ['constraints' => [new Assert\NotBlank ,new Assert\Length(['max' => 50])]])
        ->add('region', 'text', ['constraints' => [new Assert\NotBlank ,new Assert\Length(['max' => 50])]])
        ->add('country', 'entity', [
            'class' => 'Countries',
            'choice_label' => 'label',
            'em' => $this->em,
            'placeholder' => ''
        ]);

   $addressFormBuilder->addModelTransformer(new CallbackTransformer(function($originalDescription) {
        return $originalDescription;
    }, function($submittedDescription) {
        // Exception thrown before this part
        var_dump($submittedDescription);exit;
        return new Addresses(null, null, null, null, null);
    }));
问题是我无法将提交的地址转换为新地址(处理不可变值对象的唯一方法)。由于转换器在转换之前处理提交的实体,因此在尝试将修改后的国家/地区实体设置为原始地址之后,我添加到地址字段的转换器可能会被调用到late


所以我想知道是否有办法处理这个用例?

Symfony的表单并不是设计用来将不可变的对象作为实体来处理的,所以当您尝试这样做时,会遇到这个问题

无论如何,表单的创建者自己发布了解决此问题的方法:

归结起来就是:使用数据转换器

public function mapDataToForms($data, $forms)
{
    $forms = iterator_to_array($forms);
    $forms['amount']->setData($data ? $data->getAmount() : 0);
    $forms['currency']->setData($data ? $data->getCurrency() : 'EUR');
}


另一种方法是插入您自己的属性访问对象,并让它直接访问值对象的私有/受保护变量。和第二条原则一样。或者干脆咬紧牙关,添加getter/setter方法。
public function mapFormsToData($forms, &$data)
{
    $forms = iterator_to_array($forms);
    $data = new Money(
        $forms['amount']->getData(), 
        $forms['currency']->getData()
    );
}