Recuperar datos POST de una llamada AJAX
Tengo el siguiente script JS
:
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;
}
Del lado de PHP
:
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"];
}
}
Como habrás adivinado, puedo acceder a $_POST["newFormRecherche"]
mientras que no puedo recuperar $_POST["field1"]
ni $_POST["field2"]
.
El método serialize()
de jQuery funciona correctamente: probé la variable newFormRecherche
con una alerta y se muestra en el formato correcto: $field1=whatever&$field2=anything
.
Normalmente, no debería tener que analizar los resultados para acceder a las variables $_POST[], según lo que he leído aquí, pero obviamente no está funcionando. ¿De dónde viene este problema? ¿Debería usar algo diferente a data
para pasar mis argumentos?
EDICIÓN: $_POST["newFormRecherche"]
existe del lado de PHP y contiene la cadena esperada $field1=whatever&$field2=anything
.
EDICIÓN #2: Aquí hay una actualización, según la interesante observación de @czerspalace y la publicación muy detallada de @bosco. Intento aquí resumir lo que dijeron y dar algunas soluciones.
El problema aquí era una doble serialización, una hecha "a mano" y otra hecha por jQuery al realizar la llamada AJAX. El hecho de que las variables $_POST[]
no puedan recuperarse correctamente en el lado del servidor proviene de data
, que debe coincidir con el formalismo de WordPress, es decir: una action
(que es una función PHP) y los datos enviados (generalmente, desde un formulario).
Solución de @Bosco - Usar el método jQuery
serializeArray()
. En este caso, los datos enviados se componen de 2 objetos. Para recuperar correctamente los campos en el lado del servidor, tengo que manejar un array asociativo así:$_POST['newFormRecherche'][0]['name']
y$_POST['newFormRecherche'][0]['value']
. Lo mismo para los otros campos (reemplazando [0] por otros números). Para resolver esto, @Bosco propone a continuación la funciónformFieldsToObject
que se llama en los datos al realizar la llamada AJAX.Solución de @czerspalace - Usar el método
serialize()
de jQuery y hacer una deserialización manual en el lado del servidor, usandoparse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche );
para poder recuperar los campos que quiero: $newFormRecherche['field1'],... y así sucesivamente.
En ambos casos, los datos en el lado del servidor deben ser sanitizados adecuadamente, como siempre deben serlo los datos enviados por un usuario a través de formularios. Esto implica: verificar tipos de campos, verificar (e incluso truncar) la longitud de los campos..., en otras palabras: nunca confiar en el usuario.
EDICIÓN #3: En caso de que uses FormData
, asegúrate de agregar esta línea dentro de tu llamada AJAX: processData: false,
. Sin embargo, no implementé una solución completa con esta técnica.
Problema
"Serialización" es el acto de convertir un objeto de datos en una representación de cadena. jQuery serializa automáticamente la propiedad data
antes de enviar una solicitud AJAX. Luego, el servidor deserializa la cadena de consulta GET
de la URL y el cuerpo de la solicitud para las solicitudes POST
antes de poblar las variables de solicitud de PHP.
Tu código funcionó como se esperaba cuando data
consistía únicamente en los datos de tu formulario serializados (es decir, data: newFormRecherche,
) ya que los datos ya estaban en formato de cadena y, por lo tanto, no podían serializarse más. La pasada de deserialización del servidor luego analizó correctamente los datos del formulario en variables de solicitud.
Sin embargo, cuando el argumento data
es un objeto, jQuery debe serializarlo para pasarlo al servidor. Como propiedad de ese objeto, tus datos de formulario pre-serializados se manejan como cualquier otra cadena; específicamente, se escapan de manera que evita que sean "confundidos" con un objeto serializado. Entonces, cuando el servidor deserializa data
, newFormRecherche
se deserializa en el valor que jQuery originalmente recibió, es decir, una cadena, y por lo tanto requiere una segunda pasada de deserialización, como mencionó @czerspalace en los comentarios, para producir un arreglo asociativo de los datos del formulario.
Soluciones
- jQuery
Para evitar la doble serialización de los datos del formulario, adquiérelos como un arreglo de pares clave/valor en lugar de una cadena serializada invocando el método .serializeArray()
de jQuery en el elemento del formulario en lugar de .serialize()
.
Si bien jQuery es capaz de serializar correctamente este formato de arreglo clave/valor de data
, parece que falla cuando dicho arreglo está anidado como propiedad de un objeto data
(enviando la cadena '[object Object]'
en lugar de cada par clave/valor), así como al intentar anidar dicho arreglo clave/valor dentro de otro. Por lo tanto, creo que la mejor manera de enviar datos de formulario como parte de datos multidimensionales es convertir el arreglo clave/valor en un objeto.
Todo esto se puede hacer de la siguiente manera:
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
Alternativamente, si solo necesitas soportar navegadores modernos o no te importa cargar un polyfill para soportar versiones antiguas, usa el objeto FormData
para pasar los datos del formulario como un objeto de datos adecuado:
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;
}
- Deserialización Doble en el Servidor
Como sugiere @czerspalace en los comentarios, tener en cuenta la doble serialización simplemente deserializando manualmente los datos del formulario en el servidor también es una solución totalmente válida:
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 ) );
}
}
Estoy inclinado a pensar que los otros enfoques son más "profesionales" en el sentido de que todos los datos enviados al servidor están empaquetados en un formato consistente y esperado; no se necesita "decodificación" adicional por parte del servidor, mejorando la modularidad del código.
Pero la modularidad del código y el "profesionalismo" subjetivo no siempre son los factores más importantes; a veces la solución más simple es la más apropiada.
Recuerda validar y sanitizar los datos de la solicitud en el servidor antes de usarlos para mitigar vulnerabilidades de seguridad.

muchas gracias por esta explicación clara y limpia, ¡ahora entiendo dónde estaba el problema! Aunque escribiste que "jQuery serializará correctamente estos pares clave/valor en variables de solicitud", todavía no puedo acceder a ellos y ya no puedo analizarlos ya que ya no son cadenas. ¿Cuál es la solución más limpia para recuperar sus valores?

@Fafanellu parece que le di a jQuery más crédito del que merece - no le gusta cuando usas ese array de clave/valor en estructuras de datos anidadas, y de hecho falla al serializarlo correctamente en tales situaciones. He añadido una pequeña función auxiliar que debería arreglarlo todo - los datos deberían ser accesibles en PHP a través de un array $_POST
correctamente estructurado, es decir, $_POST["newFormRecherche"]["field1"]
. Puedes usar echo json_encode( $_POST );
en tu manejador AJAX, o var_dump( $_POST );
para ver la estructura exacta de los datos del formulario.

¡gracias por este tema tan interesante! He actualizado mi publicación inicial para tener en cuenta los comentarios constructivos hechos aquí

Por cierto, ¿cuál es el propósito de die( json_encode( $newFormRecherche ) )?

Eso es solo un marcador temporal para lo que sería tu código =). die()
termina inmediatamente la ejecución de un script PHP, opcionalmente echo()
ando un argumento si se proporciona, por lo que finalizará inmediatamente la solicitud AJAX, respondiendo con los datos del formulario que el servidor recibió como un objeto JSON (aunque probablemente con la cabecera Content-Type
incorrecta, pero a jQuery no le debería importar). En combinación con la función de retorno success
que especificaste en tu JavaScript, que hace console.log()
de la respuesta del servidor, deberías terminar con una salida clara y navegable de los datos del formulario en la consola JavaScript de tu navegador.

¡Ok, gracias! En realidad no escribí todo el código PHP, pero había un die() vacío al final. Si entiendo correctamente, el propósito es terminar la solicitud AJAX lo antes posible para enviar los datos de vuelta.

¡Básicamente! Esta página del Codex lo menciona brevemente. La idea general es que después de manejar la solicitud AJAX y haber hecho echo()
de una respuesta, no hay mucha razón para continuar con la ejecución de WordPress. Si se permite que WordPress siga ejecutándose normalmente, existe el riesgo de que código ejecutado después de tu manejador AJAX haga echo()
de algo más en la respuesta, lo cual probablemente rompería tu JavaScript cuando el callback success
intente procesarlo.

wp_die()
se considera una mejor alternativa que die()
. Es raro usar cualquiera de los dos fuera de los manejadores AJAX, excepto para solución temporal de problemas - ¡casi siempre hay una mejor manera de manejar un problema o reportar un error que simplemente matar la ejecución del script!
