Recuperare i dati POST da una chiamata AJAX

28 apr 2016, 21:08:41
Visualizzazioni: 34.6K
Voti: 5

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"]$_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 funzione formFieldsToObject che viene chiamata sui dati durante la chiamata AJAX.

  • Soluzione di @czerspalace - Utilizzare il metodo jQuery serialize() e fare una deserializzazione manuale lato server, utilizzando parse_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.

7
Commenti

E se provi $_POST["newFormRecherche"]["field1"] e $_POST["newFormRecherche"]["field2"]

czerspalace czerspalace
28 apr 2016 21:17:57

Ottengo un warning sulle righe interessate: Warning: Illegal string offset 'field1'...

Fafanellu Fafanellu
28 apr 2016 21:37:26

per favore fai almeno un minimo di debug prima di fare una domanda. Questo significa controllare ogni variabile (come $_POST["newFormRecherche"]) per verificare se contengono i valori attesi e scrivere questi valori nella domanda. Guarda anche questa pagina: http://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php

mmm mmm
28 apr 2016 21:57:26

Probabilmente devi analizzare $_POST['newFormRecherche'] come parse_str($_POST["newFormRecherche"], $output); e poi provare $output["field1"]

czerspalace czerspalace
28 apr 2016 22:00:15

grazie mille, funziona! anche se non capisco perché non potevo accedere alle variabili "normalmente". In una versione precedente di questa funzione, "action" era fuori dal parametro data, e riuscivo a recuperare $_POST['field1'].

Fafanellu Fafanellu
28 apr 2016 22:55:36

Un ottimo riepilogo in EDIT #2, @Fafanellu! Come nota finale, ho aggiunto una piccola funzione formFieldsToObject() alla soluzione jQuery che dovrebbe risolvere il brutto problema di $_POST['newFormRecherche'][0]['name'] / $_POST['newFormRecherche'][0]['value'] =]

bosco bosco
30 apr 2016 12:33:34

ben fatto, una soluzione pulita e semplice! Perché una funzione del genere non esiste nativamente?!

Fafanellu Fafanellu
30 apr 2016 12:43:32
Mostra i restanti 2 commenti
Tutte le risposte alla domanda 1
9

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.

29 apr 2016 01:51:31
Commenti

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 Fafanellu
29 apr 2016 11:34:54

@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.

bosco bosco
29 apr 2016 23:14:44

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

Fafanellu Fafanellu
30 apr 2016 11:25:28

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

Fafanellu Fafanellu
30 apr 2016 11:55:29

È 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.

bosco bosco
30 apr 2016 12:02:18

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.

Fafanellu Fafanellu
30 apr 2016 12:05:27

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.

bosco bosco
30 apr 2016 12:21:52

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!

bosco bosco
30 apr 2016 12:26:01

ancora una volta, grazie per le tue spiegazioni chiare! Ho aggiornato il mio post originale per includere i tuoi suggerimenti :)

Fafanellu Fafanellu
30 apr 2016 12:35:09
Mostra i restanti 4 commenti