Retrieve POST data from AJAX call

I have the following JS script :

jQuery('#form-recherche').submit(ajaxSubmit);

        function ajaxSubmit(){

            var newFormRecherche = jQuery(this).serialize();

            jQuery.ajax({
                type:"post",
                data: { 
                    action: "mon_action",
                    newFormRecherche: newFormRecherche,
                },

                url: ajaxurl,
                success: function(response){
                    console.log(response);
                }
            });

        return false;
        }

On PHP side :

    add_action( 'wp_ajax_mon_action', 'mon_action' );
    add_action( 'wp_ajax_nopriv_mon_action', 'mon_action' );

    function mon_action() {

        if (isset($_POST["newFormRecherche"])) {
            $field1= $_POST["field1"];
            $field2= $_POST["field2"];
        }
    }

As you guessed, I’m able to access $_POST["newFormRecherche"] while I can’t retrieve $_POST["field1"] nor $_POST["field2"].

jQuery serialize() works properly : I tested newFormRecherche var with an alert and it is displayed under the right form : $field1=whatever&$field2=anything.

Normally, I shouldn’t have to parse the results in order to access the $_POST[] variables, according to what I’ve read here, but obviously it’s not working. Where does this problem come from ? Should I use something else than data to pass my arguments ?

EDIT : $_POST["newFormRecherche"] exists on PHP side, and contains the expected string $field1=whatever&$field2=anything.

EDIT #2 : Here is an update, according to the interesting remark from @czerspalace and the very detailed post from @bosco. I try here to put what they said in a nutshell, and give some solutions.

The problem here was a double serialization, one made “by hand”, one made by jQuery while making the AJAX call. The fact that the $_POST[] vars can’t be properly retrieved on server side comes from data, that has to match the WordPress formalism, that is to say : an action (which is a PHP function) and the data sent (usually, from a form).

  • Solution by @Bosco –
    Use the jQuery method serializeArray(). In this case, the data sent is composed of 2 objects. In order to properly retrieve the fields on server side, I have to handle an associative array like this : $_POST['newFormRecherche'][0]['name'] and $_POST['newFormRecherche'][0]['value']. Same thing for the other fields (replacing [0] by other numbers). To solve that, @Bosco proposes below the formFieldsToObject function which is called on the data, when performing the AJAX call.

  • Solution by @czerspalace –
    Use the serialize() jQuery method, and make a manual deserialization on server side, using parse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche ); in order to be able to retrieve the fields I want : $newFormRecherche[‘field1’],…and so on.

In both cases, the data on server side has to be properly sanitized, as data sent by an user via forms always must be. It implies : checking field types, checking (and even truncate) fields length…, in other words : never trust the user.

EDIT #3 : In the case you use FormData, make sure you add this line within your AJAX call : processData: false,. However, I didn’t implement a full solution with this technique.

1
1

Problem

“Serialization” is the act of turning a data-object into a string-representation. jQuery automatically serializes the data property before sending an AJAX request. The server then deserializes the GET querystring from the URL, and the request body for POST requests before populating PHP’s request variables.

Your code functioned as expected when data consisted solely of your serialized form data (i.e. data: newFormRecherche,) as the data was already in a string format, and thus could not be further serialized. The server’s deserialization pass then properly parsed your form data into request variables.

However, when the data argument is an object jQuery must serialize it in order to pass it to a server. As a property of that object, your pre-serialized form data is handled as though it were any other string – specifically, it is escaped in such a manner to prevent it from being “mistaken” for a serialized object. So when the server then deserializes data, newFormRecherche is deserialized into the value which jQuery originally received it as – that is, a string – and thus requires the second pass of deserialization that @czerspalace alluded to in the comments in order to produce an associative array of form data.


Solutions

– jQuery

To prevent the double-serialization of your form data, acquire it as an array of key/value pairs instead of a serialized string by invoking jQuery’s .serializeArray() method on the form element instead of .serialize().

While jQuery is capable of properly serializing this key/value-array-format of data, it seems it chokes when such an array is nested as property of a data object (instead sending the string '[object Object]' in place of each key/value pair), as well as when attempting to nest such a key/value-array inside another. As such, I think the best way to send form data as a part of multi-dimensional data is to convert the key/value-array into an object.

All of this can be done as follows:

function ajaxSubmit() {
  var newFormRecherche = jQuery( this ).serializeArray();

  jQuery.ajax({
    type:"POST",
    data: { 
      action: "mon_action",
      newFormRecherche: formFieldsToObject( newFormRecherche )
    },
    url: ajaxurl,
    success: function( response ){
      console.log( response );
    }
  });

  return false;
}

function formFieldsToObject( fields ) {
  var product = {};

  for( var i = 0; i < fields.length; i++ ) {
    var field = fields[ i ];

    if( ! product.hasOwnProperty( field.name ) ) {
      product[ field.name ] = field.value;
    }
    else {
      if( ! product[ field.name ] instanceof Array )
        product[ field.name ] = [ product[ field.name ] ];

      product[ field.name ].push( field.value );
    }
  }

  return product;
}

– HTML5 FormData

Alternately, if you need only support modern browsers or don’t mind loading a polyfill to support older ones, just use the FormData object to pass your form data as a proper data object:

function ajaxSubmit() {
  var newFormRecherche = new FormData( this );

  jQuery.ajax({
    type:"POST",
    data: { 
      action: "mon_action",
      newFormRecherche: newFormRecherche
    },
    url: ajaxurl,
    success: function( response ){
      console.log( response );
    }
  });

  return false;
}

– Server-Side Double-Deserialization

As @czerspalace suggests in the comments, accounting for the double-serialization by simply manually deserializing the form data on the server is also an entirely valid solution:

add_action( 'wp_ajax_mon_action', 'mon_action' );
add_action( 'wp_ajax_nopriv_mon_action', 'mon_action' );

function mon_action() {
  if( isset( $_POST[ 'newFormRecherche' ] ) ) {
    parse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche );
    die( json_encode( $newFormRecherche ) );
  }
}

I’m inclined to think that the other approaches are more “professional” in that all of the data sent to the server is packaged in a consistent and expected format – no additional “decoding” is necessary on the server’s part, improving code modularity.

But code modularity and subjective “professionalism” aren’t always the most important factors – sometimes the most simple solution is the most appropriate.


Remember to validate and sanitize request
data
on the server before you use it in order to mitigate security vulnerabilities.

Leave a Comment