['model' => new TestModel(123)]
[]
['model' => null]
['model' => $anEmptyInstanceOfTestModel]
我有一个简单的表格。此表单具有我自定义FormType的子表单(键“模型”)。
如果我使用“模型”已经存在的数据初始化表单,则模型将不为空,即使没有数据提交模型。另一方面,如果init数据对于“model”为null,则“model”的值保持为null。
如果没有提交任何内容,如何配置我的表单以将“model”设置为null?
我已尝试设置'required' => false
和/或'empty_data' => null
这两项似乎都没有帮助。
<?php
namespace AppBundle\Tests\Common\Form\Type;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class NullModelTest extends TypeTestCase {
public function testNullOnNotSubmitted() {
$tm = new TestModel(123);
$data = ['model' => $tm];
$form = $this->factory->createBuilder(FormType::class, $data)
->add('model', TestModelType::class, ['required' => false])
->getForm();
$form->submit([]); // submit no data
$this->assertTrue($form->isSynchronized());
$this->assertNull($form->getData()['model']); // ERROR: returns the empty model
}
}
class TestModel {
protected $id;
public function __construct($id = null) {
$this->id = $id;
}
public function getId() { return $this->id; }
public function setId($id) { $this->id = $id; }
}
class TestModelType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('id', TextType::class);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefault('data_class', TestModel::class);
}
}
答案 0 :(得分:0)
我不确定它是不是bug,因为这个文档:http://symfony.com/doc/current/cookbook/form/use_empty_data.html#option-2-provide-a-closure说你可以有闭包(可以返回NULL),但它对我也没用。
非常简单的解决方案是使用DataTransformer:
use Symfony\Component\Form\DataTransformerInterface;
class NullToEmptyTransformer implements DataTransformerInterface {
public function transform($object) {
return $object; //DO NOTHING
}
public function reverseTransform($object) {
if (is_null($object->getId()))
return NULL; //Return NULL if object is empty
return $object;
}
}
并将其附加到您的表单字段:
$fb = $this->factory->createBuilder(FormType::class, $data);
$model = $this->factory->createBuilder()
->create('model', TestModelType::class, ['required' => false])
->addModelTransformer(new NullToEmptyTransformer());
$fb->add($model);
$form = $fb->getForm();
答案 1 :(得分:0)
好的,所以这是一种非常罕见的情况。在&#34;真实&#34; HTML表单没有像#34;它根本没有提交&#34;。 IMO这就是将子表单设置为null
这样的功能不存在的原因。此外:子表单可能只代表真实模型的一部分。想象一下,子表单只处理字段&#34; name&#34;一个客户。客户本身有很多领域。在经典HTML表单中,此子表单将呈现为简单的文本字段。现在想象一下,在没有为此子表单提交任何内容之后,您的整个客户将被设置为null
。似乎非常错误;)选项empty_data
用于在填充数据之前初始化未初始化的对象(例如您的客户) - 而不是替换它(如果它已经存在)。
我达到了这种类型的场景,因为我使用Symfony Forms以安全和结构化的方式使用请求数据填充我的模型。因此,我的请求数据几乎是自定义的(JSON API),整个子表单可以为null或甚至不存在。
目前我使用以下EventSubscriber动态
null
])
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Replaces the data of this form type by the given value if it's not part of the request data.
*/
class ReplaceIfNotSubmittedListener implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
FormEvents::PRE_SUBMIT => 'preSubmit',
FormEvents::SUBMIT => 'submit',
];
}
/**
* @var bool
*/
private $shouldBeReplaced = false;
/**
* @var mixed|callable
*/
private $replaceValue;
public function __construct($replaceValue) {
$this->replaceValue = $replaceValue;
}
public function preSubmit(FormEvent $event) {
if ($event->getData() === null) {
$this->shouldBeReplaced = true;
}
}
function submit(FormEvent $event) {
if ($this->shouldBeReplaced) {
$value = $this->replaceValue;
$event->setData(is_callable($value) ? $value() : $value);
}
}
}
...以及修改后的主要表单中的用法:
$fb = $this->factory->createBuilder(FormType::class, $data);
$model = $this->factory->createBuilder()
->create('model', TestModelType::class, ['required' => false])
->addEventSubscriber(new ReplaceIfNotSubmittedListener(null));
$fb->add($model);
$form = $fb->getForm();