I need to send some data using ajax and FormData, because I want to send a file and some other parameters. The way I usually send data is this:
$.ajax({
To add on to Quentin's answer, I had some PHP Laravel code like this:
$tags = [];
foreach ($request->input('tags') as $tag) {
if (!is_array($tag)) {
$new_tag = Tag::generate($tag);
array_push($tags, $new_tag->id);
} else {
array_push($tags, $tag['id']);
}
}
You can see I start with an empty array, and then I fill it with values from $request->input('tags'). That request input is a multi-dimensional array, so it suffered the issue described in this question. It only manifested when using FormData and multipart/form-data form encoding type.
I was able to fix it with Quentin's answer plus this client-side JavaScript code here:
this.example.tags.forEach((tag, i) => {
if (Object.prototype.toString.call(tag) === '[object String]') {
payload.append(`tags[${i}]`, tag);
} else {
Object.keys(tag).forEach(field => payload.append(`tags[${i}][${field}]`, tag[field]));
}
});
This code is first checking if the tag it is about to add to FormData is a string. If so, it is a new, non-existant tag. Then it adds it by its index number. If the tag already existed, I just send all the values the client had. In my case, the server only cares about the name and ID (for SQL relation to that row ID), so that's fine, but a good exercise in using syntax such as arr[index][field].
If you study Quentin's and my answer, you should be able to see the pattern. My example is also somewhat non trivial in nature, so it will be good to examine in my opinion.
To fully understand, here is what the payload looked like:
'tags' =>
array (
0 =>
array (
'id' => '2',
'name' => 'React JS',
),
1 =>
array (
'id' => '5',
'name' => 'JSON Web Tokens',
),
2 => 'Sandwiches',
),
You can see that there are 3 tags. The first two already exist. The third one doesn't exist in my database, so it comes in as a string value not an object. Laravel/PHP doesn't like the nested object "existing tags", so I had to modify the FormData (which had images, thus multipart encoding type).
For context, here is my entire submitForm function:
const payload = new FormData();
Object.keys(this.example).forEach(field => payload.append(field, this.example[field]));
this.example.tags.forEach((tag, i) => {
if (Object.prototype.toString.call(tag) === '[object String]') {
payload.append(`tags[${i}]`, tag);
} else {
Object.keys(tag).forEach(field => payload.append(`tags[${i}][${field}]`, tag[field]));
}
});
this.example.images.forEach(image => payload.append('images[]', image));
const response = await axios.post(route('admin.examples.create'), payload);
References: Check if a variable is a string in JavaScript