Recuperare i dati POST da una chiamata AJAX
Ho il seguente 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;
}
Sul lato 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"];
}
}
Come avrete intuito, riesco ad accedere a $_POST["newFormRecherche"]
mentre non riesco a recuperare né $_POST["field1"]
né $_POST["field2"]
.
jQuery serialize()
funziona correttamente: ho testato la variabile newFormRecherche
con un alert e viene visualizzata nella forma corretta: $field1=whatever&$field2=anything
.
Normalmente, non dovrei dover analizzare i risultati per accedere alle variabili $_POST[], secondo quanto ho letto qui, ma ovviamente non funziona. Da dove viene questo problema? Dovrei usare qualcosa di diverso da data
per passare i miei argomenti?
MODIFICA: $_POST["newFormRecherche"]
esiste sul lato PHP e contiene la stringa prevista $field1=whatever&$field2=anything
.
MODIFICA #2: Ecco un aggiornamento, secondo l'interessante osservazione di @czerspalace e il post molto dettagliato di @bosco. Provo qui a riassumere quello che hanno detto e fornire alcune soluzioni.
Il problema qui era una doppia serializzazione, una fatta "a mano", una fatta da jQuery durante la chiamata AJAX. Il fatto che le variabili $_POST[]
non possano essere recuperate correttamente lato server deriva da data
, che deve corrispondere al formalismo WordPress, ovvero: una action
(che è una funzione PHP) e i dati inviati (solitamente, da un form).
Soluzione di @Bosco - Utilizzare il metodo jQuery
serializeArray()
. In questo caso, i dati inviati sono composti da 2 oggetti. Per recuperare correttamente i campi lato server, devo gestire un array associativo così:$_POST['newFormRecherche'][0]['name']
e$_POST['newFormRecherche'][0]['value']
. Stessa cosa per gli altri campi (sostituendo [0] con altri numeri). Per risolvere questo, @Bosco propone di seguito la funzioneformFieldsToObject
che viene chiamata sui dati durante la chiamata AJAX.Soluzione di @czerspalace - Utilizzare il metodo jQuery
serialize()
e fare una deserializzazione manuale lato server, utilizzandoparse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche );
per poter recuperare i campi desiderati: $newFormRecherche['field1'],...e così via.
In entrambi i casi, i dati lato server devono essere correttamente sanitizzati, come devono sempre essere i dati inviati da un utente tramite form. Questo implica: controllo dei tipi di campo, controllo (e anche troncamento) della lunghezza dei campi..., in altre parole: non fidarsi mai dell'utente.
MODIFICA #3: Nel caso si utilizzi FormData
, assicurarsi di aggiungere questa riga all'interno della chiamata AJAX: processData: false,
. Tuttavia, non ho implementato una soluzione completa con questa tecnica.
Problema
"Serializzazione" è l'atto di trasformare un oggetto-dati in una rappresentazione di stringa. jQuery serializza automaticamente la proprietà data
prima di inviare una richiesta AJAX. Il server poi deserializza la stringa di query GET
dall'URL e il corpo della richiesta per le richieste POST
prima di popolare le variabili di richiesta di PHP.
Il tuo codice funzionava come previsto quando data
consisteva unicamente nei dati del form serializzati (es. data: newFormRecherche,
) poiché i dati erano già in formato stringa e quindi non potevano essere ulteriormente serializzati. La deserializzazione sul server analizzava correttamente i dati del form nelle variabili di richiesta.
Tuttavia, quando l'argomento data
è un oggetto, jQuery deve serializzarlo per passarlo al server. Come proprietà di quell'oggetto, i tuoi dati del form pre-serializzati vengono trattati come qualsiasi altra stringa - specificamente, vengono escapati in modo da evitare che vengano "scambiati" per un oggetto serializzato. Quindi quando il server deserializza data
, newFormRecherche
viene deserializzato nel valore originale ricevuto da jQuery - cioè una stringa - e richiede quindi un secondo passaggio di deserializzazione come accennato da @czerspalace nei commenti per ottenere un array associativo dei dati del form.
Soluzioni
- jQuery
Per evitare la doppia serializzazione dei dati del form, ottienili come array di coppie chiave/valore invece che come stringa serializzata usando il metodo .serializeArray()
di jQuery sull'elemento form invece di .serialize()
.
Sebbene jQuery sia in grado di serializzare correttamente questo formato a array chiave/valore di data
, sembra avere problemi quando tale array è annidato come proprietà di un oggetto data
(inviando invece la stringa '[object Object]'
al posto di ogni coppia chiave/valore), così come quando si tenta di annidare un array chiave/valore all'interno di un altro. Pertanto, penso che il modo migliore per inviare dati del form come parte di dati multidimensionali sia convertire l'array chiave/valore in un oggetto.
Tutto ciò può essere fatto come segue:
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
In alternativa, se devi supportare solo browser moderni o non ti dispiace caricare un polyfill per supportare quelli vecchi, puoi usare l'oggetto FormData
per passare i dati del form come un vero oggetto dati:
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;
}
- Doppia Deserializzazione Lato Server
Come suggerito da @czerspalace nei commenti, gestire la doppia serializzazione deserializzando manualmente i dati del form sul server è anch'essa una soluzione valida:
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 ) );
}
}
Sono propenso a pensare che gli altri approcci siano più "professionali" poiché tutti i dati inviati al server sono impacchettati in un formato consistente e atteso - nessun ulteriore "decodifica" è necessaria da parte del server, migliorando la modularità del codice.
Ma la modularità del codice e il "professionalismo" soggettivo non sono sempre i fattori più importanti - a volte la soluzione più semplice è la più appropriata.
Ricorda di validare e sanificare i dati della richiesta sul server prima di usarli per mitigare vulnerabilità di sicurezza.

grazie mille per questa spiegazione chiara e pulita, ora capisco dove fosse il problema! Tuttavia, hai scritto che "jQuery serializzerà correttamente queste coppie chiave/valore in variabili di richiesta", ma ancora non riesco ad accedervi e non posso più analizzarli poiché non sono più stringhe. Qual è la soluzione più pulita per recuperare i loro valori?

@Fafanellu sembra che abbia dato a jQuery più credito di quanto meriti - non gli piace quando usi quell'array chiave/valore in strutture dati annidate, e in effetti non riesce a serializzarlo correttamente in tali situazioni. Ho aggiunto una piccola funzione helper che dovrebbe sistemare tutto - i dati dovrebbero essere accessibili in PHP tramite un array $_POST
correttamente strutturato, ad esempio $_POST["newFormRecherche"]["field1"]
. Puoi usare echo json_encode( $_POST );
nel tuo gestore AJAX, oppure var_dump( $_POST );
per vedere la struttura esatta dei dati del form.

grazie per questo argomento molto interessante! Ho aggiornato il mio post iniziale per tenere conto dei commenti costruttivi fatti qui

A proposito, qual è lo scopo di die( json_encode( $newFormRecherche ) ?

È solo un segnaposto temporaneo per quello che sarebbe il tuo codice =) . die()
termina immediatamente l'esecuzione di uno script PHP, opzionalmente echo()
ando un argomento, se fornito - quindi terminerà immediatamente la richiesta AJAX, rispondendo con i dati del form che il server ha ricevuto come oggetto JSON (anche probabilmente con l'header Content-Type
sbagliato, ma jQuery non dovrebbe farci caso). In combinazione con la callback success
che hai specificato nel tuo JavaScript che fa console.log()
della risposta del server, dovresti ottenere una bella visualizzazione navigabile dei dati del form nella console JavaScript del tuo browser.

ok grazie! in realtà non ho scritto tutto il codice PHP, ma c'era un die() vuoto alla fine. Se ho capito, lo scopo è terminare la richiesta AJAX il prima possibile per poter inviare i dati indietro.

Praticamente! Questa pagina del Codex ne parla, brevemente. L'idea generale è che dopo aver gestito la richiesta AJAX e aver echo()
ato una risposta, non c'è molto motivo di far continuare l'esecuzione di WordPress. Se a WordPress è permesso continuare a funzionare come farebbe normalmente, c'è il rischio che il codice eseguito dopo il tuo gestore AJAX possa echo()
are qualcos'altro nella risposta, il che probabilmente romperebbe il tuo JavaScript quando la tua callback success
proverà a processarla.

wp_die()
è considerata un'alternativa migliore a die()
. È raro usare uno dei due al di fuori dei gestori AJAX, eccetto per risolvere temporaneamente problemi - c'è quasi sempre un modo migliore per gestire un problema o segnalare un errore piuttosto che semplicemente terminare l'esecuzione dello script!
