skip to Main Content

I’m completely new to PHP. Working with ajax, php and WordPress I’m sending an object with ajax:

let import_data = {
 action: 'import_data',
 data: {
  first_array: [], // this one is empty
  second_array: [], // this one too
  third_array: [1,2,3] // this one is full
 }
};

I’ve checked the import_data object many times right before it was sent. The php, however, always receives:

import_data = {
 action: 'import_data',
 data: {
  third_array: [1,2,3]
 }
}

The question is why is that happening and how can I achieve receiving all arrays, whether they are empty or not, i.e.

import_data = {
 action: 'import_data',
 data: {
  first_array: [],
  second_array: [], 
  third_array: [1,2,3]
 }
}

I need to refactor a lot of code now due to this issue so I’m trying to solve it as easy as possible, but if there is a common known right way to deal with it I’ll use it. Thanks in advance!

P.S. In case you wondering, yes, if all arrays being sent are full, php will receive all of them.

UPD In the comments I got I might’ve wanted to add contentType or json.strngify my data. It didn’t help, but I might do it wrong, so I’ll try to partly show my code below:

var import_data = {
     action: 'start_import',
     sliced_batch: {
        rows_to_add: [],
        rows_to_delete: [],
        rows_to_update: [1,2,3,4,5,...]
     }
};
    

function ajax_call(import_data) {
// ... processes

 jQuery.ajax({
            url: start_import_ajax.url, // url from php file
            type: 'POST',
            contentType: "text; charset=utf-8", // sending string with cyrillic (ukrainian lng)
            dataType: 'application/json', // want  to recieve json
            data: JSON.stringify(import_data), 
            success: function(response) { 
               // ... processes import_data ...
               if(it is the end of data) return;
               else ajax_call(import_data);
            },
            error: function(e) {
              // here is where I end up
            }

}

PHP side is now pretty shy, as I just made a pause and wanted to see my data in console:

function start_import_callback() {
   
    echo json_decode($_POST);
    echo $_POST;
    echo json_decode($_POST['sliced_batch']);
    echo $_POST['sliced_batch'];

 
    wp_die();
}

I’ve tried all echo‘s one by one, but always saw:

{
    "readyState": 4,
    "responseText": "0",
    "status": 400,
    "statusText": "error"
}

When NOT stringifying and NOT specifying contentType/dataType it returns:

{
             action: 'import_data',
     sliced_batch: {
    rows_to_update:
            {
                "ID": "00000006125",
                "CatalogueNumber": "bla, bla",
                "Category": "bla, bla",
                "Manufacturer": "bla",
                "Nomenclature": "blablablablabla",
                "NomenclatureUkrainian": "bla",
                "StockStatus": "instock",
                "Price": "2 315",
                "Parent": "blabla",
                "Sorting": "99"
            },
            {},...
        ]
    }
}

So, rows_to_delete: [] and rows_to_add: [] are missing…

2

Answers


  1. Chosen as BEST ANSWER

    So, thanks once again to @vee for his explanation, but here's one more thing I'd like to share as it was crucial to get everything to work.
    First, for json_decode method the JS object keys should be double-quoted, i.e. NOT

    $bad_json = '{ bar: "baz" }';
    json_decode($bad_json); // null
    

    or

    $bad_json = '{ 'bar': "baz" }';
    json_decode($bad_json); // null
    

    BUT

    $bad_json = '{ "bar": "baz" }';
    json_decode($bad_json); // array("bar" => "baz")
    

    Second and most important! When dealing with WordPress it sets its own rules and shows focuses! Depending on what answer you'd like to get, you may want to use function wp_unslash(). Looking at the stringified data in console I saw somth like this:

    "u0421u0435u0440u0432u0435u0440: u0424u0430u0439u043bu0456u0432 av_imp_p_WEB.csv u0456 av_imp_p_WEB_previous.csv u043du0435 u0431u0443u043bu043e u0432u0438u044fu0432u043bu0435u043du043e. u041fu043eu0447u0438u043du0430u044e u0456u043cu043fu043eu0440u0442 u0432u0441u044cu043eu0433u043e 
    // it is more common for contentType: "application/x-www-form-urlencoded"
    

    It is the dirty work of WooCommerce (as I read from another's people opinion) and it hinders parsing it the right way, so my full code is: JS

     var import_data = {
              "action": "start_import",
               "sliced_batch": {
               "rows_to_add": my_data1,
               "rows_to_delete": my_data2,
               "rows_to_update": my_data3
                }
    };
    
    function ajax_call(import_data) {
     // ... processes
    jQuery.ajax({ // ajax to php to save the file to uploads and move it to the plugin's folder
                url: start_import_ajax.url, // url from php file
                type: 'POST',
                //contentType: "application/json;charset=utf-8", //  what you send
                dataType: 'JSON', // what you would like as a response
                data: {
                    "action": import_data.action,
                    "sliced_batch": JSON.stringify(import_data.sliced_batch)
                },
                    success: function(response) {
                        //response = JSON.parse(response); // if you'd like to console.log  what you've sent
                         console.log(response);
                     }
    ....
    
    

    PHP

     $sliced_batch = wp_unslash($_POST['sliced_batch']);
        $sliced_batch = json_decode($sliced_batch, true);
    
        $result = start_import($sliced_batch);
    
        if($result == 0) {
            echo json_encode(["status" => 0]);
        } else echo json_encode(["status" => 1]);
    

  2. You are using jQuery dataType options wrong!
    The dataType: value should be 'json' not 'application/json' because your value will request with HTTP accept: */* but if you use 'json' it will be accept: application/json.

    Option 1

    Use content type application/json.

    The contentType: should be 'application/json' or 'application/json;charset=utf-8'.

    By this content type you will be now able to receive POST data in JSON but you cannot access them with $_POST because the data is not valid application/x-www-form-urlencoded.

    Full code for client side:

    var import_data = {
         action: 'start_import',
         sliced_batch: {
            rows_to_add: [],
            rows_to_delete: [],
            rows_to_update: [1,2,3,4,5]
         }
    };
    
    function ajax_call(import_data) {
        // ... processes
    
        jQuery.ajax({
            url: 'test.php', // url from php file
            type: 'POST',
            contentType: "application/json;charset=utf-8", // sending string with cyrillic (ukrainian lng)
            dataType: 'json', // want  to recieve json
            data: JSON.stringify(import_data), 
            success: function(response) { 
               // ... processes import_data ...
               
            },
            error: function(e) {
              // here is where I end up
            }
    
        });
    }
    

    Code for PHP:

    $data = json_decode(file_get_contents('php://input'), true);
    // use $data['sliced_batch'] to access `rows_to_add`, `rows_to_delete` etc.
    

    Option 2

    Use content type application/x-www-form-urlencoded.

    With this content type, you will be able to access $_POST properly.

    However, to use this request content type in header, the jQuery itself will be modify the value if it is empty jQuery will be just delete it!! So, you need to JSON string only sliced_batch property.

    Here is the JS code:

    var import_data = {
         action: 'start_import',
         sliced_batch: {
            rows_to_add: [],
            rows_to_delete: [],
            rows_to_update: [1,2,3,4,5]
         }
    };
    
    function ajax_call(import_data) {
        // ... processes
    
        // modify `sliced_batch` property to be JSON string to prevent jQuery remove empty properties.
        import_data.sliced_batch = JSON.stringify(import_data.sliced_batch);
    
        jQuery.ajax({
            url: 'test.php', // url from php file
            type: 'POST',
            // just remove contentType option. It is no need.
            //contentType: "application/json;charset=utf-8", // sending string with cyrillic (ukrainian lng)
            dataType: 'json', // want  to recieve json
            data: import_data, 
            success: function(response) { 
               // ... processes import_data ...
               
            },
            error: function(e) {
              // here is where I end up
            }
    
        });
    }
    

    PHP:

    $sliced_batch = ($_POST['sliced_batch'] ?? '');
    $sliced_batch = json_decode($sliced_batch, true);
    // you can now access $sliced_batch['rows_to_add'], etc...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search