Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony3: How to update boolean to false using PATCH method

Tags:

symfony

I have a big entity and a big form. When updating my entity, I only render parts of my form, through ajax calls. On client side, I'm using Jquery and html5 FormData, so I can also send files within my form. To make sure the fields that are not rendered won't be set to null in the process, I'm using PATCH method.

So when a field is not present in the request, it's left as is by Symfony.

But when the field I update is a boolean (rendered a a checkbox) that was set to true and I want to set it to false, it's not passed in the request, so my update is ignored.

Is there an easy way to force unchecked checkboxes to appear in the request?

EDIT
I found a way to force unchecked checkboxes to appear in the request, thanks to Felix Kling's comment on this question :

$("input:checkbox:not(:checked)").each(function() {
    formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0');
});  

Unfortunately, this didn't solve my problem, because of Symfony's behaviour:
- When using PUT, if the boolean field appears in the request, it's set to true, regardless of its value (even if it's "0" or "false").
- When using PATCH method, the fields not appearing in the request are ignored.

Could that be solved with DataTransformer? (I've never used it)

like image 235
Roubi Avatar asked Feb 03 '16 21:02

Roubi


2 Answers

You are absolutely right, Symfony will ignore it if method is PATCH because of this line in Request Handler:

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

Now, I would generally suggest that you use a PUT request if that is an option, but if it isn't then second argument to FormInterface::submit($submittedData, $clearMissing = true) is what you're after.

The "proper" way would probably be to make your own implementation of Symfony\Component\Form\RequestHandlerInterface which would force $clearMissing to be true.

Other, way is a lot easier but might not work for all use-cases: use $form->submit() directly.

If you have the following code:

$form->handleRequest($request);

You can do:

$form->submit($request->get($form->getName()), true);

You can also omit second parameter since true is the default value

like image 140
Igor Pantović Avatar answered Sep 29 '22 01:09

Igor Pantović


Here goes a working solution, that could be improved.

To force unchecked checkboxes to appear in the request, thanks to Felix Kling's comment on this question, I've added this js before my ajax request :

$("input:checkbox:not(:checked)").each(function() {
    formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0');
});  

Then, on the Symfony side, I had to override the BooleanToStringTransformer behaviour, that returns true for whatever string and false only for null value. Making a change in the last line, we now return false if the value doesn't match the value defined for true ("1" by default). So if the value returned by the form is "0", we get false, as expected.

public function reverseTransform($value)
{
    if (null === $value) {
        return false;
    }

    if (!is_string($value)) {
        throw new TransformationFailedException('Expected a string.');
    }

    return ($this->trueValue === $value); // initially: "return true;" 
}

Following the docs, I made my own DataTransformer, as well as a custom AjaxCheckboxType

Unfortunately, it seems that Symfony uses both DataTransformers (mine and the original one), one after the other, so it didn't work. In the docs they extend TextType not CheckboxType, that must explain the problems I encountered.

I ended up copying and pasting the whole CheckboxType class in my own AjaxCheckboxType, only changing the DataTransformer's call in order to use mine.

A much nicer solution would be to totally override the DataTransformer, but I don't know how.

like image 30
Roubi Avatar answered Sep 29 '22 00:09

Roubi