Symfony2表单未选中复选框未考虑,为什么



>当我发送带有未选中复选框的表单时,如果相关实体属性等于 true ,则它不会更改为 false

相反的方式(在使用选中复选框发送表单时将属性设置为 true)工作正常,以及保存其他字段的所有表单。

以下是我构建窗体并声明相关属性的方法:

// --- Form creation function EntityType::buildForm() ---
$builder->add('secret', 'checkbox', array( 'required' => false ));
// --- Entity related property, Entity.php file ---
/** @ORMColumn(name="secret", type="boolean") */
protected $secret;

编辑:发生此问题是因为表单是使用PATCH请求提交的。

在Symfony中,Form::submit方法由请求处理程序调用,行如下:

$form->submit($data, 'PATCH' !== $method);

因此,在 PATCH 请求的情况下,Form::submit $clearMissing 参数设置为 false,从而将未发送的字段保留为其旧值。

但我不知道如何解决问题。如果我在未选中复选框时显式地将 JSON {secret: false}传递给 Symfony 框架,它会将其解释为"false"字符串并认为这是一个真正的值,从而考虑选中的复选框......


铌。对于使用链接到 Doctrine Simple Array 属性的choice字段类型(带有 multipleextended to true)的复选框数组,我遇到了完全相同的问题:一旦给定的复选框被发送一次选中,就不可能将相关属性设置回后续unchecked提交false

上述非对我没有帮助。所以,我正在使用这个...

解释

使用"PATCH"方法时,此问题的解决方案是在表单类型中添加其他隐藏的"时间戳"字段,并将其放在twig文件中的问题复选框旁边。这需要与复选框一起传递一些东西,这肯定会改变 - 时间戳会改变。

接下来是使用PRE_SUBMIT事件并等待表单字段到达,如果未设置,我将手动设置它......工作正常,我不介意额外的代码...

表单类型

$builder
...
->add('some_checkbox')
->add('time_stamp', 'hidden', ['mapped' => false, 'data' => time()])
...

树枝

{{ form_widget(form.time_stamp) }}
{{ form_widget(form.some_checkbox) }}

生成器中的PRE_SUBMIT事件

$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use($options) {
    $data = $event->getData();
    $form = $event->getForm();
    if (!$data) {
        return;
    }
    /* Note that PATCH method is added as an option for "createForm" 
     * method, inside of your controller 
    */       
    if ($options["method"]=="PATCH" && !isset($data['some_checkbox'])) {
        $form->getData()->setSomeCheckbox(false);//adding missing checkbox, since it didn't arrive through submit.. grrr
    }            
});

发生此问题的原因是表单是使用 PATCH 请求提交的。

这导致了这个Symfony问题。

如前所述,一种解决方法是在取消选中复选框时显式发送特定的保留值(例如字符串"__false")(而不是不发送任何内容),并使用表单类型的自定义数据转换器将此值替换为"null":

// MyEntityFormType.php -- buildForm method
$builder->add('mycheckbox', ...);
$builder->get('mycheckbox')
    ->addViewTransformer(new CallbackTransformer(
        function ($normalizedFormat) {
            return $normalizedFormat;
        },
        function ($submittedFormat) {
            return ( $submittedFormat === '__false' ) ? null : $submittedFormat;
        }
    ));

"选择"字段的情况无法以相同的方式解决。它实际上是Symfony的一个错误,在本期中处理。

你使用的是哪个版本的Symfony?

应该有一些代码专门用于您正在编写的情况,vendor/symfony/symfony/src/Symfony/Component/Form/Form.phpForm::submit()

// Treat false as NULL to support binding false to checkboxes.
// Don't convert NULL to a string here in order to determine later
// whether an empty value has been submitted or whether no value has
// been submitted at all. This is important for processing checkboxes
// and radio buttons with empty values.
if (false === $submittedData) {
    $submittedData = null;
} elseif (is_scalar($submittedData)) {
    $submittedData = (string) $submittedData;
}

位于我的525-534号线。你能检查一下这对你来说是否有效吗?

另一个线索是自定义表单订阅者,它不完全按预期工作 - 通过覆盖提供的值。

这可能是因为架构上不需要该字段。 您可以使用以下内容为复选框提供默认值:

$builder->add('secret', 'checkbox', array(
  'required' => false,
  'empty_data' => false
));

看这里或这里

这个解决方案对我有用。

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('isActive', CheckboxType::class, array(
            'required' => false
        ))
    $builder->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $e {
        $entity = $e->getData();
        $form = $e->getForm();
        $isActive = empty($_POST[$form->getName()]['isActive']) ? false : true;
        $entity->setIsActive($isActive);
    });
}

另一种可能性是添加一个隐藏元素并使用Javascript进行。它将在0.1%的人中使用没有javascript的浏览器中失败。这是一个多个复选框 FormType 元素的简单示例:

            ->add('ranges', ChoiceType::class, array(
            'label'   => 'Range',
            'multiple' => true,
            'expanded' => true,
            'choices' => array(
                null => 'None',
                'B1' => 'My range',
            )
        ))
<script>
$(document).ready(function() {
       function updateDynRanges(object) {
            if (object.prop("checked")) {
                document.getElementById('ranges_0').checked = 0;
            } else {
                document.getElementById('ranges_0').checked = 1;
            }
        }
 // On page onload
        $('#ranges_1').change(function() {
           updateDynRanges($(this));
        });
        updateDynRanges($('ranges_1'));
}
</script>

如果测试工作正常,您只需在第二个复选框中添加 visibility:false。

树枝模板:

 {{ form_label(form.dynamicRanges) }}<br>
 {{ form_widget(form.dynamicRanges[1]) }}
 <div class="hidden">{{ form_widget(form.ranges[0]) }}</div>

看起来像一个丑陋的解决方法,但我只是想与其他丑陋的建议解决方法竞争,在这种情况下主要是更新树枝模板。

最新更新