Symfony2 form unchecked checkbox not taken into account, why?

旧时模样 提交于 2021-01-28 04:23:43

问题


When I send a form with an unchecked checkbox, if the related entity property equals true, then it does not change to false.

The other way round (setting property to true when the form is sent with a checked checkbox) works fine, as well as all the forms other fields saving.

Here is how I build the form and declare the related property:

// --- Form creation function EntityType::buildForm() ---
$builder->add('secret', 'checkbox', array( 'required' => false ));

// --- Entity related property, Entity.php file ---
/** @ORM\Column(name="secret", type="boolean") */
protected $secret;

EDIT: The issue happens because the form is submitted using a PATCH request.

In Symfony, the Form::submit method is called by a Request Handler with this line:

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

As a result the Form::submit $clearMissing parameter is set to false in the case of a PATCH request, thus leaving the non-sent fields to their old value.

But I do not know how to solve the problem. If I explicitely pass a JSON {secret: false} to the Symfony framework when the checkbox is not checked, it will interpret it as the "false" string and consider that a true value, thus considering the checkbox checked...


NB. I have exactly the same issue with an array of checkboxes using a choice field type (with multiple and extended to true) linked to a Doctrine Simple Array property: as soon as a given checkbox has been sent once as checked, it is impossible to set back the related property to false with subsequent unchecked submissions.


回答1:


The issue happens because the form is submitted using a PATCH request.

This has lead to open this Symfony issue.

As explained, one workaround is to explicitely send a specific reserved value (for instance the string '__false') when the checkbox is unchecked (instead of sending nothing), and replace this value by 'null' using a custom data transformer in the form type:

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

The case with the 'choice' field can't be solved the same way. It is actually a bug of Symfony, dealt with in this issue.




回答2:


Non of above-mentioned didn't help me. So, I am using this...

Explanation

Resolution for this issue when "PATCH" method was used, was to add additional hidden "timestamp" field inside of a form type and to have it next to the checkbox of issue in twig file. This is needed to pass something along with the checkbox, that would definitely change - time-stamp will change.

Next thing was to use PRE_SUBMIT event and to wait for form field to arrive and if it not set, I would set it manually... Works fine, and I don't mind extra code...

FormType

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

Twig

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

PRE_SUBMIT event in builder

$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
    }            
});



回答3:


What version of Symfony are you using?

There should exist some code dedicated to the situation you're writing about, in vendor/symfony/symfony/src/Symfony/Component/Form/Form.php, in Form::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;
}

Located at lines 525-534 for me. Could you check this works properly for you?

Another lead would be a custom form subscriber that do not work exactly as intended - by overwriting the provided value.




回答4:


It's probably because the field isn't required on you schema. you can provide a default value to the checkbox with the following:

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

See here or here




回答5:


This solution works for me.

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);
    });
}



回答6:


Another possibility is to add a hidden element and make this with Javascript. It will fail in 0.1 % of the people that use a browser without javascript. This is a simple example for a multiple checkboxes FormType element:

            ->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>

If after testing works you can just add a visibility:false to the second checkbox.

Twig template:

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

Looks like an ugly workaround, but I just wanted to compete with the other ugly suggested workarounds, in this case mostly updating the twig template.



来源:https://stackoverflow.com/questions/35502979/symfony2-form-unchecked-checkbox-not-taken-into-account-why

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!