Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

POSTing to the API Endpoint

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Before we keep going, I want to go back and look at what it used to look like when we submitted the form. I have not refreshed yet, and this AJAX call is an example of what the POST request looked like using our old code.

Click that AJAX call and move to the "Headers" tab. When we sent the AJAX call before, what did our request look like? At the bottom, you'll see "Form Data". But more interestingly, if you click "View Source", it shows you the raw request body that we sent. It's this weird-looking, almost query-string format, with & and = between fields.

This is the traditional form submit format for the web, a data format called application/x-www-form-urlencoded, if you want to get dorky about it. When you submit a normal HTML form, the data is sent like this. In PHP, that data is parsed into the familiar $_POST variable. We don't realize that it originally looked like this, because PHP gives us that nice associative array.

I wanted to show this because we are not going to send data in this format. Remember, our endpoint expects pure JSON. So $form.serialize() is not going to work anymore.

Instead, above the AJAX call, create a new formData variable set to an associative array, or an object:

... lines 1 - 2
(function(window, $) {
... lines 4 - 24
$.extend(window.RepLogApp.prototype, {
... lines 26 - 61
handleNewFormSubmit: function(e) {
... lines 63 - 64
var $form = $(e.currentTarget);
var formData = {};
... lines 67 - 82
}
});
... lines 85 - 102
})(window, jQuery);

Next, use $.each($form.serializeArray()):

... lines 1 - 2
(function(window, $) {
... lines 4 - 24
$.extend(window.RepLogApp.prototype, {
... lines 26 - 61
handleNewFormSubmit: function(e) {
... lines 63 - 65
var formData = {};
$.each($form.serializeArray(), function(key, fieldData) {
... line 68
});
... lines 70 - 82
}
});
... lines 85 - 102
})(window, jQuery);

If you Google for that function - jQuery serializeArray() - you'll see that it finds all the fields in a form and returns a big array with keys name and value for each field.

This is not exactly what we want: we want an array where the name is the array key and that field's value is its value. No problem, because we can loop over this and turn it into that format. Add a function with key and fieldData arguments. Then inside, simply say, formData[fieldData.name] = fieldData.value:

... lines 1 - 2
(function(window, $) {
... lines 4 - 24
$.extend(window.RepLogApp.prototype, {
... lines 26 - 61
handleNewFormSubmit: function(e) {
... lines 63 - 65
var formData = {};
$.each($form.serializeArray(), function(key, fieldData) {
formData[fieldData.name] = fieldData.value
});
... lines 70 - 82
}
});
... lines 85 - 102
})(window, jQuery);

Now that formData has the right format, turn it into JSON with JSON.stringify(formData):

... lines 1 - 2
(function(window, $) {
... lines 4 - 24
$.extend(window.RepLogApp.prototype, {
... lines 26 - 61
handleNewFormSubmit: function(e) {
... lines 63 - 65
var formData = {};
$.each($form.serializeArray(), function(key, fieldData) {
formData[fieldData.name] = fieldData.value
});
$.ajax({
... lines 71 - 72
data: JSON.stringify(formData),
... lines 74 - 81
});
}
});
... lines 85 - 102
})(window, jQuery);

Remember, we're doing this because that's what our endpoint expects: it will json_decode() the request body.

Ok, moment of truth. Refresh! Let's lift our laptop 10 times. Submit! Of course, nothing on the page changes, but we do have a successful POST request! Check out the response: id, item, label, reps and totalWeightLifted. Cool!

Also check out the "Headers" section again and find the request body at the bottom. It's now pure JSON: you can see the difference between our old request format and this new one.

Ok! It's time to get to work on our UI: we need to start processing the JSON response to add errors to our form and dynamically add a new row on success.

Leave a comment!

8
Login or Register to join the conversation
Juan-Etxenike Avatar
Juan-Etxenike Avatar Juan-Etxenike | posted 1 year ago

Hi, following the main ideas of this tutorial I am trying to make my own code, but in my case using a file input type, which gives problem using .serialize and .serializeArray functions. What would be the alternative?

Reply

Hey Juan,

I'm not sure about your problem, have you tried to watch our Symfony Uploads course: https://symfonycasts.com/sc... ? I think it might be helpful. Otherwise, if you have some input fields in your form along with the file upload field - probably I'd recommend to split them into 2 different forms: one will hold all the input fields and another one will hold the file upload field. But this way you will need to write an integration code for them so it works as a single form, e.g. upload the file and then return its unique identifier that you will put in the hidden field of the 2nd form.

I hope this helps!

Cheers!

Reply
Juan-Etxenike Avatar
Juan-Etxenike Avatar Juan-Etxenike | victor | posted 1 year ago

Hello Victor thanks for the idea I may try it. What I see is that. What happened is that JSON.stringify() would not work with a file input. Thanks. I leave this for the record https://stackoverflow.com/q...

1 Reply

Hey Juan,

Ah, I see, it's good to know, thanks for sharing that!

Cheers!

Reply
Gst I. Avatar

Anyone?

Reply
Gst I. Avatar

Hi, I have a problem when trying to prepare the form data to serializeArray. My form contains fields that are type select multiple:

$builder->add('vatTypes', EntityType::class, array( 'class' => VatType::class,
'multiple' => true,

...

'attr' => ['class' => 'form-control select2']));

So when I get to:

$.each($form.serializeArray(), function(key, fieldData) {
...

I get "fieldName[]"=3 where 3 is the last selected value, but it is missing all the other options. How can this be done? So the Json can parse the multiple select?

Also, when the form is sent it complains about "The form should not contain extra fields" which I think it is maybe related to this problem, not sure though...

Any guidance will be really appreciated.

Thanks!

Reply

Hey Gst I.

Sorry for the late reply. I found a way to get all selected items via JQuery: https://stackoverflow.com/a...
and there is even a pure JS example if you scroll down a little bit.
Here is an example of how to serialize the select data and send it via AJAX: https://stackoverflow.com/a...

About "The form should not contain extra fields" problem, I believe somehow your form contains extra fields when you serialize it, try dumping it and check which fields you are actually passing in.

Cheers!

Reply
Gst I. Avatar

Thank you Diego, but the problem is that the ajax endpoint is expecting a json encoded string, not serialized data. I think I found a solution, I will write it here in case someone else needs it:


handleNewFormSubmit: function(e) {
e.preventDefault();

var $form = $(e.currentTarget);
var formData = {};

$.each($form.serializeArray(), function(key, fieldData) {
if (fieldData.name.indexOf("[]") > 0) {
formData[fieldData.name.replace('[]', '')] = $form.find('select[name="'+fieldData.name+'"]').val();
} else {
formData[fieldData.name] = encodeURIComponent(fieldData.value);
}
});

var self = this;
this._saveIssuingEntity(formData)
.then(function(data) {
self._clearForm();
self._addRow(data);
}).catch(function(errorData) {
self._mapErrorsToForm(errorData.errors);
});
}

For sure it could be more efficient, but this just works. Thanks!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony... but since it's a JavaScript tutorial, the concepts are still ? valid!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.1.*", // v3.1.10
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.1
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "symfony/swiftmailer-bundle": "^2.3", // v2.4.0
        "symfony/monolog-bundle": "^2.8", // 2.12.0
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.1
        "symfony/phpunit-bridge": "^3.0" // v3.1.6
    }
}