2

I have a jQuery script that adds hidden inputs into a form whenever a certain .class input undergoes a change. Depending on user input, it generates values for other uneditable columns which also get pushed into a form as hidden inputs.

The form output looks like this:

<input type="hidden" name="[1008016BSTL][1][part]" value="1008016BSTL" />
<input type="hidden" name="[1008016BSTL][1][price]" value="123" />
<input type="hidden" name="[1008016BSTL][1][priceExVat]" value="102.50" />
<input type="hidden" name="[1008016BSTL][1][fee]" value="10.53" />
<input type="hidden" name="[1008016BSTL][1][profit]" value="68.41" />

This is just one set of data I'm trying to capture, but it's the same for the others, save the original key and sub-key.

My form wrapper looks like this:

<form method="post" id="submit-form" enctype="multipart/form-data">
    <input type="submit" value="Save" />
</form>

With my AJAX looking like:

$('form#submit-form').submit(function(e)
{
    e.preventDefault();

    let data = $('form#submit-form').serializeArray();

    $.ajax({
       url: '/save-pricing.php',
       data: {data: JSON.stringify(data)},
       type: 'post',
       success: function(res)
       {
           console.log(res)
       },
       error: function(res)
       {
           alert('Error! I won\'t tell you what it is. But, I\'ll give you a clue: 21');
           console.log(res)
       }
   })
})

I've also tried (for setting data):

let data = $('form#submit-form').serialize();
data = JSON.stringify(data);

$.ajax({
    ...
    data: {data: data}
    ...
})

As well as omitting the .stringify() function.

This comes through to PHP like this:

<?php
    echo '<pre>'. print_r($_POST, 1) .'</pre>';

    /**
     * Below is for .serialize() -> output is an empty array
     *
     * parse_str($_POST['data'], $postData)
     * echo '<pre>'. print_r($postData, 1) .'</pre>';
     */

simplified output (just removing the other sets) with .serializeArray():

Array
(
    [data] => [
        {"name":"[1008016BSTL][1][part]","value":"1008016BSTL"},
        {"name":"[1008016BSTL][1][price]","value":"123"},
        {"name":"[1008016BSTL][1][priceExVat]","value":"102.50"},
        {"name":"[1008016BSTL][1][fee]","value":"10.53"},
        {"name":"[1008016BSTL][1][profit]","value":"68.41"}
    ]
)

This is OK I guess, I could probably group by name and merge into an array, but there feels like it should already do this with .serialize() on jQuery-side and then parse_str() on the PHP side.

However, as I've mentioned, parse_str() and .serialize() yield an empty array, which I can't use.

so my question is: How do I successfully send multi-dimensional form data to PHP via jQuery?

Edit

Added:

dataType: 'json'

with .serialize() and then JSON.stringify(data), removed parse_str() and it outputs:

Array
(
    [\"] => Array
    (
        [1008016BSTL] => Array 
        (
            [1] => Array
            (
                [part] => 1008016BSTL
            )
        )
    )
)
0

3 Answers 3

3

Input fields names with brackets are not treated nicely by serializeArray. This below code will create a proper multidimentional array you can send back to the server.

$('form#submit-form').submit(function(event)
{
    event.preventDefault();
    //Prevent the form from submitting

    var fields = {};
    //This is where you're gonna store your form fields

    $.each($('form#submit-form').serializeArray(), function(i, field) {
        //Get values, even from multiple-selects
        if (Array.isArray(fields[field.name])) {
            fields[field.name].push(field.value);
        } else if (typeof fields[field.name] !== 'undefined') {
            var val = fields[field.name];
            fields[field.name] = new Array();
            fields[field.name].push(val);
            fields[field.name].push(field.value);
        } else {
            fields[field.name] = field.value;
        }
    });
    //Now all the fields are in the fields object

    //You're now going to translate "key[subkey]" string to key[subkey] object
    for (var key in fields) {
        var parts = key.split(/[[\]]{1,2}/);
        parts.length--;
        if (parts.length) {
            var val = fields[key];
            delete fields[key];
            addToTree(fields, parts);
            setToValue(fields, val, parts);
        }
        //input field array names (with brackets) are mistakenly treated as strings, this fixes it
    }


    $.ajax({
        url: '/save-pricing.php',
        data: JSON.stringify(fields),
        contentType: 'application/json',
        type: 'post',
        success: function(res) {
            console.log(res)
        },
        error: function(res) {
            alert('Error! I won\'t tell you what it is. But, I\'ll give you a clue: 21');
            console.log(res)
        }
    })
});

/**
 * Adds values to a tree.
 * @link https://stackoverflow.com/questions/3663096/how-to-convert-array-to-tree
 */
function addToTree(tree, array) {
    for (var i = 0, length = array.length; i < length; i++) {
        tree = tree[array[i]] = tree[array[i]] || {}
    }
}

/**
 * Sets object values.
 * @link https://stackoverflow.com/questions/13719593/how-to-set-object-property-of-object-property-of-given-its-string-name-in-ja
 */
function setToValue(obj, value, path) {
    for (i = 0; i < path.length - 1; i++) {
        obj = obj[path[i]];
    }
    obj[path[i]] = value;
}

with the PHP side using json_decode:

$data = json_decode(file_get_contents('php://input'), true);
echo '<pre>'. print_r($data, 1) .'</pre>';
Sign up to request clarification or add additional context in comments.

1 Comment

It has a couple errors - specifically i is not defined in the setToValue function, but it got me close enough to figure out the rest. thanks +1
0

For your particular issue you can the jquery.serializeJSON

Here is the link of their github https://github.com/marioizquierdo/jquery.serializeJSON

This will create the correct json object.

2 Comments

I did find this answer on another SO post but it didn't help :/
It doesn't seem to do multidimensional arrays as I want them
0

This is simplest solution I have for this case.

<?php if(isset($_POST["data"])) {

    $post_data = urldecode($_POST["data"]);

    parse_str($post_data, $form_data);

    // this will give you first element of array by eliminating double quote key ('') in post data array, which is also desired
    $form_data = reset($form_data);

    echo '<pre>'; print_r($form_data); echo '</pre>'; exit;

} else { ?>

<form method="post" id="submit-form">
    <input type="hidden" name="[1008016BSTL][1][part]" value="1008016BSTL" />
    <input type="hidden" name="[1008016BSTL][1][price]" value="123" />
    <input type="hidden" name="[1008016BSTL][1][priceExVat]" value="102.50" />
    <input type="hidden" name="[1008016BSTL][1][fee]" value="10.53" />
    <input type="hidden" name="[1008016BSTL][1][profit]" value="68.41" />
    <input type="submit" value="Save" />
</form>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
    $("#submit-form").on('submit', function(e){
        e.preventDefault();
        var form_data = $("#submit-form").serialize();
        $.ajax({
            type: "POST",
            data: {data: JSON.stringify(form_data)},
            success: function(res){
                console.log(res);
            }
        });
    });
</script>

<?php } ?>

1 Comment

As much as it might work, I really don't like having PHP and HTML in the same file as much as possible. Prefer the concept of MVC - even if it isn't MVC (as in, keeping model, view and controller all separate)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.