211

I'm using ajax to submit a multipart form with array, text fields and files.

I append each VAR to the main data as so

var attachments = document.getElementById('files'); 
var data= new FormData();

for (i=0; i< attachments.files.length; i++){
    data.append('file', attachments.files[i]);
    console.log(attachments.files[i]);

    data.append ('headline', headline);
    data.append ('article', article);
    data.append ('arr', arr);
    data.append ('tag', tag);

then I use the ajax function to send it to a PHP file to store inside sql DB.

$.ajax({    
    type: "post",
    url: 'php/submittionform.php',
    cache: false,
    processData: false,
    contentType: false,
    data: data,
    success: function(request) {$('#box').html(request); }
})

But on the PHP side, the arr variable, which is an array appears as a string.

When I don't send it with ajax as Form data but use the simple $.POST option I do get it as an array on the PHP side, but then I can't send the files as well.

any solutions?

16 Answers 16

415

You can also send an array via FormData this way:

var formData = new FormData;
var arr = ['this', 'is', 'an', 'array'];

for (var i = 0; i < arr.length; i++) {
  formData.append('arr[]', arr[i]);
}

console.log(...formData);

So you can write arr[] the same way as you do it with a simple HTML form. In case of PHP it should work.

You may find this article useful: How to pass an array within a query string?

9
  • 2
    @Oleg What is the need to write arr[] in formData.append('arr[]', arr[i]);? why isn't arr correct? I tried both but only arr[] worked.
    – Totoro
    Commented Apr 28, 2018 at 14:59
  • 1
    @Totoro because in case of arr your just redefine this value on each loop iteration, and in the end, the final value would equal to last array element, but not the whole array
    – Oleg
    Commented Apr 28, 2018 at 15:03
  • @Oleg If redefining is the case then what's different in arr[] , why isn't arr[] redefined? arr[] is also a string . And while testing both neither arr nor arr[] was redefined in my case. I got multiple array in FormData with same key but different value. So i got arr with value 1 and another arr with value 2.
    – Totoro
    Commented Apr 28, 2018 at 15:21
  • @Totoro yes, you're right, my mistake. I believe this is more serverside-specific question. Different languages may parse query string differently. For example, PHP behaves like you described, but I saw examples (if memory serves, in Java), where arr also worked for arrays. In this topic there is a more detailed answer to this question
    – Oleg
    Commented Apr 28, 2018 at 15:30
  • 9
    If anyone is looking to post an array of objects, you can extend this answer as follows for (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }
    – edqwerty
    Commented Sep 17, 2018 at 12:39
156

You have several options:

Convert it to a JSON string, then parse it in PHP (recommended)

JS

var json_arr = JSON.stringify(arr);

PHP

$arr = json_decode($_POST['arr']);

Or use @Curios's method

Sending an array via FormData.


Not recommended: Serialize the data with, then deserialize in PHP

JS

// Use <#> or any other delimiter you want
var serial_arr = arr.join("<#>"); 

PHP

$arr = explode("<#>", $_POST['arr']);
8
  • 1
    the problem is that the array contains lines of REAL text, with spaces and punctuation marks. I don't want to mess it up.
    – shultz
    Commented Apr 19, 2013 at 12:01
  • 4
    When you encode and parse it with JSON, data isn't lost. Give it a try ;) Commented Apr 19, 2013 at 12:03
  • 1
    @Richard de Wit If you have data such File or FormData you will lose them in json.stringfy
    – Mohsen
    Commented Apr 24, 2018 at 6:56
  • 1
    I like stringified better, simpler. As you need to do some sort of recursion to pass array of arrays using [] but good to know that can be done that way.
    – Chopnut
    Commented May 20, 2019 at 22:35
  • 1
    Why is this the accepted answer when the keywords from the OP not only NOT list PHP, but start with JavaScript? Commented Jan 2, 2023 at 11:49
16

Typescript version:

export class Utility {      
    public static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
        let formData = form || new FormData();
        let formKey;

        for (let propertyName in model) {
            if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
            let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
            if (model[propertyName] instanceof Date)
                formData.append(formKey, model[propertyName].toISOString());
            else if (model[propertyName] instanceof Array) {
                model[propertyName].forEach((element, index) => {
                    const tempFormKey = `${formKey}[${index}]`;
                    this.convertModelToFormData(element, formData, tempFormKey);
                });
            }
            else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File))
                this.convertModelToFormData(model[propertyName], formData, formKey);
            else
                formData.append(formKey, model[propertyName].toString());
        }
        return formData;
    }
}

Using:

let formData = Utility.convertModelToFormData(model);
2
  • 1
    Thank you so much, it works fine! For those not using Typescript, you just need to remove the first and last line + replace the method declaration public static with function + remove the types declarations : any, : FormData and : FormData. Commented Oct 12, 2021 at 14:42
  • As of now, the function skips zeros and empty strings. To include them, replace the if block at line 7 with: if (!model.hasOwnProperty(propertyName) || (!model[propertyName] && model[propertyName]!==0 && model[propertyName]!=='')) continue; Commented Oct 12, 2021 at 14:53
8

This is an old question but I ran into this problem with posting objects along with files recently. I needed to be able to post an object, with child properties that were objects and arrays as well.

The function below will walk through an object and create the correct formData object.

// formData - instance of FormData object
// data - object to post
function getFormData(formData, data, previousKey) {
  if (data instanceof Object) {
    Object.keys(data).forEach(key => {
      const value = data[key];
      if (value instanceof Object && !Array.isArray(value)) {
        return this.getFormData(formData, value, key);
      }
      if (previousKey) {
        key = `${previousKey}[${key}]`;
      }
      if (Array.isArray(value)) {
        value.forEach(val => {
          formData.append(`${key}[]`, val);
        });
      } else {
        formData.append(key, value);
      }
    });
  }
}

This will convert the following json -

{
  name: 'starwars',
  year: 1977,
  characters: {
    good: ['luke', 'leia'],
    bad: ['vader'],
  },
}

into the following FormData

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader
1
  • It was usefull for me, just had to apply String(value) on value inside append (otherwise it fails for true/false). Also it should be (value !== null) && formData.append(key, value) instead of just formData.append(key, value) otherwise it fails on null values
    – Alexander
    Commented Jun 13, 2017 at 15:03
6

Based on @YackY answer shorter recursion version:

function createFormData(formData, key, data) {
    if (data === Object(data) || Array.isArray(data)) {
        for (var i in data) {
            createFormData(formData, key + '[' + i + ']', data[i]);
        }
    } else {
        formData.append(key, data);
    }
}

Usage example:

var data = {a: '1', b: 2, c: {d: '3'}};
var formData = new FormData();
createFormData(formData, 'data', data);

Sent data:

data[a]=1&
data[b]=2&
data[c][d]=3
1
  • Thank you sir, we were not able to resolve that issue from last 3 days but your answer fixed our issue. Commented Jan 15 at 7:50
5

add all type inputs to FormData

const formData = new FormData();
for (let key in form) {
    Array.isArray(form[key])
        ? form[key].forEach(value => formData.append(key + '[]', value))
        : formData.append(key, form[key]) ;
}
5

here's another version of the convertModelToFormData since I needed it to also be able to send Files.

utility.js

const Utility = {
  convertModelToFormData(val, formData = new FormData, namespace = '') {
    if ((typeof val !== 'undefined') && val !== null) {
      if (val instanceof Date) {
        formData.append(namespace, val.toISOString());
      } else if (val instanceof Array) {
        for (let i = 0; i < val.length; i++) {
          this.convertModelToFormData(val[i], formData, namespace + '[' + i + ']');
        }
      } else if (typeof val === 'object' && !(val instanceof File)) {
        for (let propertyName in val) {
          if (val.hasOwnProperty(propertyName)) {
            this.convertModelToFormData(val[propertyName], formData, namespace ? `${namespace}[${propertyName}]` : propertyName);
          }
        }
      } else if (val instanceof File) {
        formData.append(namespace, val);
      } else {
        formData.append(namespace, val.toString());
      }
    }
    return formData;
  }
}
export default Utility;

my-client-code.js

import Utility from './utility'
...
someFunction(form_object) {
  ...
  let formData = Utility.convertModelToFormData(form_object);
  ...
}
0
3

TransForm three formats of Data to FormData :

1. Single value like string, Number or Boolean

 let sampleData = {
  activityName: "Hunting3",
  activityTypeID: 2,
  seasonAssociated: true, 
};

2. Array to be Array of Objects

let sampleData = {
   activitySeason: [
    { clientId: 2000, seasonId: 57 },
    { clientId: 2000, seasonId: 57 },
  ],
};

3. Object holding key value pair

let sampleData = {
    preview: { title: "Amazing World", description: "Here is description" },
};

The that make our life easy:

function transformInToFormObject(data) {
  let formData = new FormData();
  for (let key in data) {
    if (Array.isArray(data[key])) {
      data[key].forEach((obj, index) => {
        let keyList = Object.keys(obj);
        keyList.forEach((keyItem) => {
          let keyName = [key, "[", index, "]", ".", keyItem].join("");
          formData.append(keyName, obj[keyItem]);
        });
      });
    } else if (typeof data[key] === "object") { 
      for (let innerKey in data[key]) {
        formData.append(`${key}.${innerKey}`, data[key][innerKey]);
      }
    } else {
      formData.append(key, data[key]);
    }
  }
  return formData;
}

Example : Input Data

let sampleData = {
  activityName: "Hunting3",
  activityTypeID: 2,
  seasonAssociated: true,
  activitySeason: [
    { clientId: 2000, seasonId: 57 },
    { clientId: 2000, seasonId: 57 },
  ],
  preview: { title: "Amazing World", description: "Here is description" },
};

Output FormData :

activityName: Hunting3
activityTypeID: 2
seasonAssociated: true
activitySeason[0].clientId: 2000
activitySeason[0].seasonId: 57
activitySeason[1].clientId: 2000
activitySeason[1].seasonId: 57
preview.title: Amazing World
preview.description: Here is description
2

Next version valid for model containing arays of simple values:

function convertModelToFormData(val, formData = new FormData(), namespace = '') {
    if((typeof val !== 'undefined') && (val !== null)) {
        if(val instanceof Date) {
            formData.append(namespace, val.toISOString());
        } else if(val instanceof Array) {
            for(let element of val) {
                convertModelToFormData(element, formData, namespace + '[]');
            }
        } else if(typeof val === 'object' && !(val instanceof File)) {
            for (let propertyName in val) {
                if(val.hasOwnProperty(propertyName)) {
                    convertModelToFormData(val[propertyName], formData, namespace ? namespace + '[' + propertyName + ']' : propertyName);
                }
            }
        } else {
            formData.append(namespace, val.toString());
        }
    }
    return formData;
}
2

If you have nested objects and arrays, best way to populate FormData object is using recursion.

function createFormData(formData, data, key) {
    if ( ( typeof data === 'object' && data !== null ) || Array.isArray(data) ) {
        for ( let i in data ) {
            if ( ( typeof data[i] === 'object' && data[i] !== null ) || Array.isArray(data[i]) ) {
                createFormData(formData, data[i], key + '[' + i + ']');
            } else {
                formData.append(key + '[' + i + ']', data[i]);
            }
        }
    } else {
        formData.append(key, data);
    }
}
1

I've fixed the typescript version. For javascript, just remove type definitions.

  _getFormDataKey(key0: any, key1: any): string {
    return !key0 ? key1 : `${key0}[${key1}]`;
  }
  _convertModelToFormData(model: any, key: string, frmData?: FormData): FormData {
    let formData = frmData || new FormData();

    if (!model) return formData;

    if (model instanceof Date) {
      formData.append(key, model.toISOString());
    } else if (model instanceof Array) {
      model.forEach((element: any, i: number) => {
        this._convertModelToFormData(element, this._getFormDataKey(key, i), formData);
      });
    } else if (typeof model === 'object' && !(model instanceof File)) {
      for (let propertyName in model) {
        if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
        this._convertModelToFormData(model[propertyName], this._getFormDataKey(key, propertyName), formData);
      }
    } else {
      formData.append(key, model);
    }

    return formData;
  }
1

If you are using django ArrayField so you need send data as example below

const form = new FormData();
const value = ["Hello", "World"];
const key = "arr";
let i;
for (i = 0; i < value.length; i++) {
    form.append(key, value[i]);
}
axios.post(url, form).then(res=> console.log(res));
0

JavaScript code:

var formData = new FormData();
let arr = [1,2,3,4];
formData.append('arr', arr);

Output on php:

$arr = $_POST['arr']; ===>  '1,2,3,4'

Solution php code:

$arr = explode(",", $_POST['arr']); ===> [1,2,3,4]
2
  • This could use some explanation that says why and how it works rather than just showing the code that you think works.
    – Newbyte
    Commented Nov 24, 2021 at 16:53
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Nov 24, 2021 at 16:53
0

In a case where you have an array of elements or maybe an object. You can simply use the set method together with JSON.stringify to convert the array to a string and set the value to that string. An tstringified json data back to a proper array JSON, set the form value, and on the backend convert back to a valid array with very little problems.

const bookItems = [
    {
      name: "John Doe",
      age: 21
    }
 ...
];
formData.set("books", JSON.stringify(bookItems));
0

Javascript with typescript. Create a function for FormData() data handling

function createFormData(formData: FormData, key: string, 
 data: any): void {
  if (typeof data === 'object' && !Array.isArray(data)) {
    for (const prop in data) {
      if (data.hasOwnProperty(prop)) {
        createFormData(formData, `${key}[${prop}]`, data[prop]);
      }
    }
  } else if (Array.isArray(data)) {
    data.forEach((item, index) => {
      createFormData(formData, `${key}[${index}]`, item);
    });
  } else {
    formData.append(key, data);
  }
}

const data = {name:["Shariar","Sheikh"],age:24}
const formData = new FormData()

createFormData(formData,"name",data.name)
createFormData(formData,"age",data.age)
-3

simple way :

data.map(dt=>formdata.append("name",dt))

i've tried it, it works perfectly

2
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Cristik
    Commented Apr 21, 2022 at 4:30
  • Your answer is of much lower quality than others
    – Zach Jensz
    Commented Apr 25, 2022 at 11:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.