0

I'm trying to remove jQuery from some code. I only use it for POST operations so I want to drop it and use fetch() instead. But I can't get fetch() to work using the same data. The php file is working OK, it is just not receiving the data

This sets up the test data for both test cases below:

var toPostObj = new(Object);
toPostObj.action = "update+file";
toPostObj.arrays = [["2020-12-28", "23:20:56", "Trying from ztest", "9.jpg"]];

This works using jQuery:

$.post('toMariaDB.php', {  // url
    data: toPostObj 
}, function(data2, status, jqXHR) {
    console.log ("data2",data2);
});

This does not work using fetch():

fetch("toMariaDB.php", { 
    method: "POST", 
    body: toPostObj,   // using JSON.stringify(toPostObj) also doesn't work 
    headers: { "Content-type": "application/text; charset=UTF-8" } 
}) 
.then(response => response.text()) 
.then(text => console.log(text))//; 
.catch(error => {
    console.error(error)
})

For debugging purposes, toMariaDB.php writes out a log file of the data it receives and any other messages from toMariaDB.
Running the jQuery code writes this to the log file:

toMariaDB: I've ARRIVED
in toMariaDB 1=>Array
(
    [action] => update+file
    [arrays] => Array
        (
            [0] => Array
                (
                    [0] => 2020-12-28
                    [1] => 23:20:56
                    [2] => Trying from ztest
                    [3] => 9.jpg
                )

        )
)

which is what toMariaDB.php expects.
But the fetch() version writes this:

toMariaDB: I've ARRIVED
in toMariaDB 1=>

The result for fetch() is the same whether I use

body: toPostObj,   

or

body: JSON.stringify(toPostObj),

I've used

headers: { "Content-type": "application/text; charset=UTF-8" } 

since toMariaDB.php returns text and, as I understand it, headers describes what is returned
but just in case I had misunderstood, I tried

headers: { "Content-type": "application/json; charset=UTF-8" } 

as well, but that didn't work either.

How can I format the body so that it arrives at toMariaDB.php in the same form as with jQuery? I.e.

toPostObj.action = "update+file";
toPostObj.arrays = [["2020-12-28", "23:20:56", "Trying from ztest", "9.jpg"]];

Thanks for any help.

EDIT

As suggested by @T.J.Crowder, (thanks for pointing me at that) here's what the Network tab shows as the Request Payload when running with jQuery:

data[action]: update+file
data[arrays][0][]: 2020-12-28
data[arrays][0][]: 23:20:56
data[arrays][0][]: Trying from ztest
data[arrays][0][]: 9.jpg

I don't understand why these don't show as data[arrays][0][0], etc., but it works.
(It's a 2D array because toMariaDB.php has to be able to process multiple arrays.)

With fetch(), the Network tab Request Payload shows:

[object Object]
Roy Grubb
  • 93
  • 2
  • 11
  • 3
    Please open the Network tab in your browser, run your jQuery code, find the body of the POST being sent, and copy and paste that into the question. – T.J. Crowder Dec 28 '20 at 17:47
  • 4
    Side note, just FWIW: `var toPostObj = new(Object);` is a ***very*** unusual way to write `var toPostObj = new Object;`, which in turn is a slightly unusual (but perfectly valid) way to write `var toPostObj = new Object();`, which in turn is generally best written `var toPostObj = {};` – T.J. Crowder Dec 28 '20 at 17:49
  • Thanks, @T.J. Crowder, for the comments on making new objects. I picked up the new(Object) style when copying some other code from stackoverflow, or somewhere. As I make a new array by var arr = []; your observation makes perfect sense. I'll remember it. – Roy Grubb Dec 29 '20 at 07:06

2 Answers2

5

From the documentation we can see that...

When data is an object, jQuery generates the data string from the object's key/value pairs unless the processData option is set to false. For example, { a: "bc", d: "e,f" } is converted to the string "a=bc&d=e%2Cf". If the value is an array, jQuery serializes multiple values with same key based on the value of the traditional setting (described below). For example, { a: [1,2] } becomes the string "a%5B%5D=1&a%5B%5D=2" with the default traditional: false setting.

(It doesn't say so, but it does it recursively.)

Your code is sending an object with a single top-level property called data whose value is your toPostObj, which in turn has properties with string and array values. It ends up sending a POST body that looks like this:

data%5Baction%5D=update%2Bfile&data%5Barrays%5D%5B0%5D%5B%5D=2020-12-28&data%5Barrays%5D%5B0%5D%5B%5D=23%3A20%3A56&data%5Barrays%5D%5B0%5D%5B%5D=Trying+from+ztest&data%5Barrays%5D%5B0%5D%5B%5D=9.jpg

...which is these parameters:

data[action]: update+file
data[arrays][0][]: 2020-12-28
data[arrays][0][]: 23:20:56
data[arrays][0][]: Trying from ztest
data[arrays][0][]: 9.jpg

You can replicate that with a URLSearchParams object like this:

var toPostObj = new URLSearchParams();
toPostObj.append("data[action]", "update+file");
toPostObj.append("data[arrays][0][]", "2020-12-28");
toPostObj.append("data[arrays][0][]", "23:20:56");
toPostObj.append("data[arrays][0][]", "Trying from ztest");
toPostObj.append("data[arrays][0][]", "9.jpg");
fetch("foo", {
    method: "POST",
    body: toPostObj
})
// ...

URLSearchParams will handle the URI-escaping etc. for you.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • FormData will generate multipart/form-data data, you'd want URLSearchParams to replicate what jQuery does. – Quentin Dec 28 '20 at 18:05
  • @Quentin - Gah! You are, of course, correct. Although I doubt the PHP code receiving it would care... – T.J. Crowder Dec 28 '20 at 18:07
  • 1
    True, PHP handles multipart and urlencoded data transparently and identically as far as the developer is concerned. (Worth bearing in mind for people coming across this with a different backend though). – Quentin Dec 28 '20 at 18:10
  • I don't know if a comment is the right place for this, but this was an education and an example of stackoverflow at its best. Thank you. – Roy Grubb Dec 29 '20 at 07:26
  • That's great, the code set out by @T.J.Crowder works. I formed my question as wanting the body to arrive in the same form as with jQuery, so that shaped the answers. I'd like to know, is this accepted best practice for formatting the body to be processed by php? I'm not only trying to remove a need for jQuery, but understand the best way to send data from plain javascript to php. Thanks a lot. – Roy Grubb Dec 29 '20 at 07:41
  • @RoyGrubb - If you can change the PHP script, I'd consider sending JSON instead. The sending side of that looks like `fetch("/your/url", {method: "POST", body: JSON.stringify(toPostObj), headers: {"Content-Type": "application/json"}})` and [this answer](https://stackoverflow.com/a/7084677/157247) shows how to read and parse the JSON on the PHP side. – T.J. Crowder Dec 29 '20 at 08:45
  • 1
    Thanks, this gave me the missing bit in my understanding of how data can be transferred, and it's working now. Apparently I can't accept two answers, but I'd like to. – Roy Grubb Dec 30 '20 at 11:14
1

I think you'd have the best experience using this:

fetch("toMariaDB.php", { 
    method: "POST", 
    body: JSON.stringify(toPostObj),
    headers: {
        "Content-type": "application/json",
    },
}) 

And doing json_decode on the PHP side.

If you want to exactly replicate what jQuery does, you'll have to look into the network tab as it can differ... but most likely it's using application/x-www-form-urlencoded as the content type. Best way to replicate that is to populate a FormData object which is still quite a hassle when you have arrays, especially nested arrays.

Džuris
  • 2,115
  • 3
  • 27
  • 55
  • For the record, I couldn't make this work, but thanks. – Roy Grubb Dec 29 '20 at 08:31
  • 1
    Originally my impression was that the OP couldn't change the PHP side, but from subsequent comments, it looks like they can. This is a good option if they can. – T.J. Crowder Dec 29 '20 at 08:46
  • 1
    I have access to all the code and the server. I tried @Džuris's suggestion and toMariaDB.php reported seeing nothing, or being unable to interpret what it received. I would prefer this approach as it is simpler, especially when dealing with embedded arrays. I can't get it to work because it shows "PHP Notice: Undefined index: data in /var/www/html/t3/toMariaDB.php on ..." I'm using T.J.Crowder way for now, but I'll come back to get this working later because it'll be more straightforward once I can find out where I'm going wrong. – Roy Grubb Dec 29 '20 at 13:51
  • @RoyGrubb did you try the approach that TJ linked in the other comment thread? You should be able to find the posted JSON via `file_get_contents('php://input')` instead of `$_POST`. – Džuris Dec 29 '20 at 16:14
  • @Džuris Shoot, no I didn't. Thanks, will look into that tmr. – Roy Grubb Dec 29 '20 at 16:28
  • 1
    That link by T.J. resolved the issue for me thanks for the nudge. – Roy Grubb Dec 30 '20 at 11:12