Cum să preluați date POST dintr-un apel AJAX

28 apr. 2016, 21:08:41
Vizualizări: 34.6K
Voturi: 5

Am următorul 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;
        }

Pe partea 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"];
        }
    }

După cum ați ghicit, pot accesa $_POST["newFormRecherche"] dar nu pot prelua $_POST["field1"] și nici $_POST["field2"].

jQuery serialize() funcționează corect: am testat variabila newFormRecherche cu un alert și este afișată în forma corectă: $field1=whatever&$field2=anything.

În mod normal, nu ar trebui să analizez rezultatele pentru a accesa variabilele $_POST[], conform cu ce am citit aici, dar evident nu funcționează. De unde vine această problemă? Ar trebui să folosesc altceva decât data pentru a transmite argumentele mele?

EDIT: $_POST["newFormRecherche"] există pe partea PHP și conține șirul așteptat $field1=whatever&$field2=anything.

EDIT #2: Iată o actualizare, conform observației interesante de la @czerspalace și postării foarte detaliate de la @bosco. Încerc aici să rezum ce au spus ei și să ofer câteva soluții.

Problema aici era o serializare dublă, una făcută "manual", alta făcută de jQuery în timpul apelului AJAX. Faptul că variabilele $_POST[] nu pot fi preluate corect pe partea de server vine de la data, care trebuie să se potrivească cu formalismul WordPress, adică: o action (care este o funcție PHP) și datele trimise (de obicei, dintr-un formular).

  • Soluția propusă de @Bosco - Folosiți metoda jQuery serializeArray(). În acest caz, datele trimise sunt compuse din 2 obiecte. Pentru a prelua corect câmpurile pe partea de server, trebuie să gestionez un array asociativ astfel: $_POST['newFormRecherche'][0]['name'] și $_POST['newFormRecherche'][0]['value']. Același lucru pentru celelalte câmpuri (înlocuind [0] cu alte numere). Pentru a rezolva acest lucru, @Bosco propune mai jos funcția formFieldsToObject care este apelată pe date, când se efectuează apelul AJAX.

  • Soluția propusă de @czerspalace - Folosiți metoda jQuery serialize() și faceți o deserializare manuală pe partea de server, folosind parse_str( $_POST[ 'newFormRecherche' ], $newFormRecherche ); pentru a putea prelua câmpurile dorite: $newFormRecherche['field1'],... și așa mai departe.

În ambele cazuri, datele pe partea de server trebuie să fie corect sanitizate, așa cum trebuie întotdeauna datele trimise de un utilizator prin formulare. Aceasta implică: verificarea tipurilor de câmpuri, verificarea (și chiar trunchierea) lungimii câmpurilor..., cu alte cuvinte: nu aveți niciodată încredere în utilizator.

EDIT #3: În cazul în care folosiți FormData, asigurați-vă că adăugați această linie în apelul AJAX: processData: false,. Cu toate acestea, nu am implementat o soluție completă cu această tehnică.

7
Comentarii

Ce se întâmplă dacă încerci $_POST["newFormRecherche"]["field1"] și $_POST["newFormRecherche"]["field2"]

czerspalace czerspalace
28 apr. 2016 21:17:57

Am primit un avertisment la liniile implicate: Warning: Illegal string offset 'field1'...

Fafanellu Fafanellu
28 apr. 2016 21:37:26

te rog să faci un minim de depanare înainte de a pune o întrebare. asta înseamnă să verifici fiecare variabilă (cum ar fi $_POST["newFormRecherche"]) pentru a vedea dacă conțin valorile așteptate și să scrii aceste valori în întrebare. verifică și această pagină: http://stackoverflow.com/questions/12769982/reference-what-does-this-error-mean-in-php

mmm mmm
28 apr. 2016 21:57:26

Probabil trebuie să analizezi $_POST['newFormRecherche'] astfel: parse_str($_POST["newFormRecherche"], $output); și apoi să încerci $output["field1"]

czerspalace czerspalace
28 apr. 2016 22:00:15

mulțumesc mult, funcționează! totuși nu înțeleg de ce nu am putut accesa variabilele "normal". Într-o versiune anterioară a acestei funcții, "action" era în afara parametrului data și am putut accesa $_POST['field1'].

Fafanellu Fafanellu
28 apr. 2016 22:55:36

O sinteză solidă în EDIT #2, @Fafanellu! Ca o observație finală, am adăugat o mică funcție formFieldsToObject() la soluția cu jQuery care ar trebui să rezolve problema incomodă cu $_POST['newFormRecherche'][0]['name'] / $_POST['newFormRecherche'][0]['value'] =]

bosco bosco
30 apr. 2016 12:33:34

foarte bine făcut, o soluție curată și simplă! de ce nu există o astfel de funcție în mod nativ?!

Fafanellu Fafanellu
30 apr. 2016 12:43:32
Arată celelalte 2 comentarii
Toate răspunsurile la întrebare 1
9

Problemă

"Serializarea" este actul de a transforma un obiect de date într-o reprezentare sub formă de șir de caractere. jQuery serializează automat proprietatea data înainte de a trimite o cerere AJAX. Serverul apoi deserializează șirul de interogare GET din URL și corpul cererii pentru cererile POST înainte de a popula variabilele de cerere PHP.

Codul dvs. a funcționat conform așteptărilor atunci când data consta doar din datele serializate ale formularului (adică data: newFormRecherche,), deoarece datele erau deja într-un format de șir și, prin urmare, nu puteau fi serializate în continuare. Pasul de deserializare al serverului a analizat corect datele formularului în variabilele de cerere.

Cu toate acestea, când argumentul data este un obiect, jQuery trebuie să îl serializeze pentru a-l trimite către server. Ca proprietate a acelui obiect, datele dvs. pre-serializate ale formularului sunt tratate ca orice alt șir - mai precis, sunt escaponate într-un mod care să împiedice "confundarea" lor cu un obiect serializat. Deci, când serverul deserializează data, newFormRecherche este deserializat în valoarea pe care jQuery a primit-o inițial - adică un șir - și, prin urmare, necesită al doilea pas de deserializare la care @czerspalace a făcut referire în comentarii pentru a produce un tablou asociativ de date ale formularului.


Soluții

- jQuery

Pentru a preveni dubla serializare a datelor formularului, obțineți-le ca un tablou de perechi cheie/valoare în loc de un șir serializat, invocând metoda jQuery .serializeArray() pe elementul formular în loc de .serialize().

Deși jQuery este capabil să serializeze corect acest format de tablou cheie/valoare al data, se pare că întâmpină probleme atunci când un astfel de tablou este imbricat ca proprietate a unui obiect data (trimitând în schimb șirul '[object Object]' în locul fiecărei perechi cheie/valoare), precum și atunci când încearcă să imbriche un astfel de tablou cheie/valoare în altul. Prin urmare, cred că cel mai bun mod de a trimite datele formularului ca parte a unor date multidimensionale este să convertiți tabloul cheie/valoare într-un obiect.

Toate acestea pot fi făcute după cum urmează:

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

Alternativ, dacă trebuie să suportați doar browsere moderne sau nu vă deranjează să încărcați un polifil pentru a suporta cele mai vechi, puteți folosi obiectul FormData pentru a trimite datele formularului ca un obiect de date propriu-zis:

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;
}

- Deserializare Dublă pe Server

Așa cum sugerează @czerspalace în comentarii, contabilizarea dublei serializări prin simpla deserializare manuală a datelor formularului pe server este, de asemenea, o soluție complet validă:

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 ) );
  }
}

Sunt înclinat să cred că celelalte abordări sunt mai "profesionale" prin faptul că toate datele trimise către server sunt ambalate într-un format consistent și așteptat - nu este necesară nicio "decodare" suplimentară din partea serverului, îmbunătățind modularitatea codului.

Dar modularitatea codului și "profesionalismul" subiectiv nu sunt întotdeauna cei mai importanți factori - uneori soluția cea mai simplă este cea mai potrivită.


Nu uitați să validați și să curățați datele cererii pe server înainte de a le folosi pentru a reduce vulnerabilitățile de securitate.

29 apr. 2016 01:51:31
Comentarii

mulțumesc mult pentru această explicație clară și simplă, acum înțeleg unde era problema! Totuși, ai scris că "jQuery va serializa corect aceste perechi cheie/valoare în variabile de cerere", dar încă nu le pot accesa și nu le mai pot parsa deoarece nu mai sunt șiruri de caractere. Care este cea mai elegantă soluție pentru a obține valorile lor?

Fafanellu Fafanellu
29 apr. 2016 11:34:54

@Fafanellu se pare că am dat jQuery mai mult credit decât merită - nu-i place când folosești acea matrice cheie/valoare în structuri de date imbricate și de fapt nu reușește să le serializeze corect în astfel de situații. Am adăugat o mică funcție helper care ar trebui să rezolve problema - datele ar trebui accesibile în PHP printr-un array $_POST structurat corect, adică $_POST["newFormRecherche"]["field1"]. Poți folosi echo json_encode( $_POST ); în handler-ul tău AJAX, sau var_dump( $_POST ); pentru a vedea structura exactă a datelor formularului.

bosco bosco
29 apr. 2016 23:14:44

mulțumesc pentru acest subiect foarte interesant! Am actualizat postarea inițială pentru a ține cont de comentariile constructive făcute aici

Fafanellu Fafanellu
30 apr. 2016 11:25:28

Apropo, care este scopul lui die( json_encode( $newFormRecherche ) ) ?

Fafanellu Fafanellu
30 apr. 2016 11:55:29

Acesta este doar un placeholder temporar pentru orice ar fi codul tău =). die() încheie imediat execuția unui script PHP, opțional afișând (echo()) un argument, dacă este furnizat - așa că va încheia imediat cererea AJAX, răspunzând cu datele formularului pe care serverul le-a primit ca un obiect JSON (deși probabil cu header-ul Content-Type greșit, dar jQuery nu ar trebui să aibă probleme). În combinație cu callback-ul success pe care l-ai specificat în JavaScript-ul tău și care afișează (console.log()) răspunsul serverului, ar trebui să obții o ieșire clară și ușor de navigat a datelor formularului în consola JavaScript a browserului tău.

bosco bosco
30 apr. 2016 12:02:18

ok, mulțumesc! de fapt, nu am scris întregul cod PHP, dar era un die() gol la final. Dacă am înțeles corect, scopul este de a încheia cererea AJAX cât mai repede posibil pentru a trimite datele înapoi.

Fafanellu Fafanellu
30 apr. 2016 12:05:27

Practic! Această pagină Codex o menționează pe scurt. Ideea generală este că după ce ai gestionat cererea AJAX și ai trimis un răspuns cu echo(), nu prea mai ai motive să lași WordPress să continue execuția. Dacă WordPress este lăsat să ruleze în mod normal, există riscul ca codul executat după handler-ul tău AJAX să mai adauge ceva în răspuns cu echo(), ceea ce ar putea strica funcționarea JavaScript-ului când callback-ul tău success încearcă să proceseze răspunsul.

bosco bosco
30 apr. 2016 12:21:52

wp_die() este considerată o alternativă mai bună decât die(). Este rar să folosești oricare dintre ele în afara handler-elor AJAX, cu excepția depanărilor temporare - există aproape întotdeauna o metodă mai bună de a gestiona o problemă sau de a raporta o eroare decât să oprești pur și simplu execuția scriptului!

bosco bosco
30 apr. 2016 12:26:01

Încă o dată, mulțumesc pentru explicațiile clare! Am actualizat postarea originală pentru a include sugestiile tale :)

Fafanellu Fafanellu
30 apr. 2016 12:35:09
Arată celelalte 4 comentarii