All’interno di un sito WordPress capita di implementare funzionalità in cui bisogna fare richieste ajax.
Quando si sviluppa per WordPress è sempre bene usare tutto ciò che il CMS mette a disposizione al fine di garantire sicurezza e stabilità del codice all’interno dell’ambiente.
Per le richieste ajax WordPress ha previsto due modalità ottimali:
- I ganci wp_ajax_{$action} e wp_ajax_nopriv_{$action} (d’ora in poi wp_ajax_e wp_ajax_nopriv_ per semplicità);
- Le RestAPI.
Delle RestAPI parlerò in un prossimo articolo; qui mi limito a dire che sono utili sia per le richieste ajax che per far dialogare WordPress con altri dispositivi, ad esempio le app per smartphone e IPhone.
I ganci wp_ajax_e wp_ajax_nopriv_, invece, sono specifici per le richieste ajax e funzionano allo stesso modo, con la differenza che il primo gestisce le richieste compiute da utenti loggati nel sito, l’altro quelle degli utenti non loggati.
I nomi di questi ganci, però, sono monchi poiché vanno di volta in volta completati con un valore, la chiave ‘action’ in $_GET o $_POST, a seconda del metodo con cui sono inviati i dati. Quindi, ad esempio, se inviamo action=custom_request i ganci saranno wp_ajax_custom_request e wp_ajax_nopriv_custom_request.
Come si imposta una richiesta ajax con wp_ajax_e wp_ajax_nopriv_
Nel frontend
La url di destinazione deve puntare allo script che WordPress destina a questo scopo, cioè {url_del_sito}/wp-admin/admin-ajax.php.
Tra i valori da inviare ci deve essere anche ‘action’ che, come detto sopra, completerà il nome del gancio. Se la richiesta avviene in POST si può creare un <input type=”hidden” name=”action” value=”some_action”> oppure, se si usa FormData in JavaScript, formdata_obj.append(‘action’,’some_action’). Se la richiesta, invece, avviene in GET nella query string andrà aggiunta la coppia chiave-valore action=some_action.
Nel backend
Per prima cosa ci deve essere una funzione PHP che gestisce la richiesta. Questa va agganciata a wp_ajax_{$_REQUEST[‘action’]) e/o wp_ajax_nopriv_{$_REQUEST[‘action’]).
Questa funzione deve richiamare wp_die() quando il processo è terminato, altrimenti WordPress caricherà la home page. Se si invia un responso json è utile usare wp_send_json($response,$status_code, $options) stampandone l’output restituito.
Capiamo le richieste ajax in WordPress con un esempio
Vediamo un esempio pratico di una funzionalità, implementata in un sito WordPress, che prevede una richiesta ajax fatta con wp_ajax_e wp_ajax_nopriv.
Qui abbiamo un semplicissimo form di contatto da cui inviare una mail. Questo può essere compilato sia da utenti loggati che non.
All’invio verranno sanificati i dati per poi essere validati: se non ci sono errori e omissioni si tenta l’invio della mail e a video viene stampato un messaggio di avvenuto o non avvenuto invio, altrimenti, sotto ogni campo avente un dato mancante, o non valido, verrà visualizzato un messaggio di errore.
Sviluppiamo il frontend: il form e la richiesta ajax
Anzitutto scriviamo l’html del form
<form method="post" action="<?php echo admin_url('admin-ajax.php'); ?>" id="custom-contact-form"> <p> <label for="input-first-name">Nome*</label> <input type="text" name="first-name" id="input-first-name"> <p class="msg-error" id="missing-first-name">Scrivere il nome</p> </p> <p> <label for="input-last-name">Cognome*</label> <input type="text" name="last-name" id="input-last-name"> <p class="msg-error" id="missing-last-name">Scrivere il cognome</p> </p> <p> <label for="input-email-address">Indirizzo email*</label> <input type="text" name="email-address" id="input-email-address"> <p class="msg-error" id="invalid-email-address">Indirizzo email non valido</p> </p> <p> <label for="tel">Telefono</label> <input type="text" name="tel" id="input-tel"> </p> <p> <label for="input-subject">Oggetto</label> <input type="text" name="subject" id="input-subject"> </p> <div> <label for="textarea-message">;Testo del messaggio*</label> <textarea name="message" id="textarea-message"></textarea> <p class="msg-error" id="missing-message">Scrivere il testo del messaggio</p> </div> <div> <input type="checkbox" name="acceptance" value="1"> Accetto il trattamento dati secondo le condizioni contenute nella <a href="#" target="_blank">Privacy Policy</a> <p class="msg-error" id="not-acceptance">È necessario consentire il trattamento dei dati personali</p> </div> <div> <!-- Here message ajax response is printed when request is finish --> <div id="wrap-response"></div> <button type="submit">Invia</button> </div> </form>
Dal momento che in questo codice viene preso un valore dinamico, tramite php, ho creato uno shortcode che poi ho integrato nella pagina. Il valore in questione è l’attributo action, la cui url è ricavata con la funzione admin_url() per evitare problemi in caso di migrazione del sito da un dominio all’altro.
E ora vediamo la funzione JavaScript.
(function () { let form = document.getElementById('custom-contact-form'); let wrap_response = document.getElementById('wrap-response'); let msg_errors = form.querySelectorAll('.msg-error'); form.onsubmit = function (event) { //avoid the normal browser http request event.preventDefault(); //say to user the request is sending wrap_response.innerHTML = 'Invio del messaggio in corso...'; //if there are some p.msg-error shown it will be hide like default msg_errors.forEach(function(p_msg){ p_msg.style.display = ''; }); //data in form's input and textarea are stored let formdata = new FormData(form); //action value is added for wp_ajax_{$action} and wp_ajax_nopriv_{$action} hooks formdata.append('action', 'send_custom_contact_form'); const xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4) { //Verify server status code let serverStatus = this.status; //the request is not ok if(serverStatus != 200){ wrap_response.innerHTML = 'Il server non ha risposto correttamente: riprovare.'; return; } //the request is ok if(serverStatus == 200){ //response is a json object, so it will be parsified let response = JSON.parse(this.responseText); //let's check some validion error. if there'is the p with id corresponding to a msg error from server that p will be shown if('validation_errors' in response){ let validation_errors = response.validation_errors; validation_errors.forEach(function(msg){ document.getElementById(msg).style.display = 'block'; }); wrap_response.innerHTML = 'Dati incompleti.'; return; } if('result_email_sent' in response){ if(response.result_email_sent){ wrap_response.innerHTML = 'Messaggio inviato correttamente'; } else{ wrap_response.innerHTML = 'Messaggio non inviato. Ricontrollare i dati prima di tentare un nuovo invio.'; } return; } } } }; xhttp.open('POST', form.getAttribute('action') ); xhttp.send(formdata); }; })();
Il funzionamento è comprensibile leggendo il codice. Si noti, tuttavia, che all’oggetto FormData creato è stata aggiunta la chiave action tramite il metodo append. Come abbiamo già visto questa servirà a completare il nome del gancio WordPress a cui è associata la funzione php che si occuperà di gestire l’invio della mail.
Infine, il metodo open() dell’oggetto XMLHttpRequest prende la url dall’attributo action del form: questo valore è stato ricavato da php, pertanto non può essere inserito in uno script JavaScript, a meno di creare nel controller una variabile JavaScript da richiamare.
Sviluppiamo il backend: la funzione agganciata a WordPress che gestisce l’invio della mail
Agganciamo la funzione preposta all’invio della mail in questo modo:
add_action('wp_ajax_send_custom_contact_form','send_custom_contact_form_email'); add_action('wp_ajax_nopriv_send_custom_contact_form','send_custom_contact_form_email');
Dato che la chiave action ha valore ‘custom_contact_form’ i nomi dei due ganci si completeranno con questo valore.
Ora vediamo come avviene l’invio della mail.
function send_custom_contact_form_email(){ //Preparing data for email. On every value written by user stripslashes is applied because WordPress, on every $_POST value, applies addslashes $email_data = [ 'first-name' => trim( stripslashes( $_POST['first-name'] ) ), 'last-name' => trim( stripslashes( $_POST['last-name'] ) ), 'email-address' => trim( stripslashes( $_POST['email-address'] ) ), 'tel' => trim( stripslashes( $_POST['tel'] ) ), 'subject' => trim( stripslashes( $_POST['subject'] ) ), 'message' => trim( stripslashes( $_POST['message'] ) ), 'acceptance' => isset( $_POST['acceptance'] ) ]; //validation //array to add every message error $validation_errors = []; //First name is required if( empty($email_data['first-name']) ){ $validation_errors[] = 'missing-first-name'; } //Last name is required if( empty($email_data['last-name']) ){ $validation_errors[] = 'missing-last-name'; } //Email address have to be in a valid format string if( !filter_var($email_data['email-address'], FILTER_VALIDATE_EMAIL) ){ $validation_errors[] = 'invalid-email-address'; } //Message text is required if( empty($email_data['message']) ){ $validation_errors[] = 'missing-message'; } //Is necessary to acceptance data treatment according Privacy Policy conditions if( !($email_data['acceptance']) ){ $validation_errors[] = 'not-acceptance'; } //If one or more validation errors are occour a json object is send and WordPress is stopped if( !empty($validation_errors) ){ echo wp_send_json(['validation_errors' => $validation_errors]); wp_die(); } //Preparing the mail to send $to = 'admin@mail.it'; $subject = "Messaggio dal sito www.miosito.it. Oggetto:“" . $email_data['subject']."”"; $headers = ['Content-Type: text/html; charset=UTF-8']; $mail_content = "Un utente ha lasciato il messaggio.<br/><br/>"; $mail_content .= "Nome: " . $email_data['first-name']."<br/>"; $mail_content .= "Cognome: " . $email_data['last-name']."<br/>"; $mail_content .= "Indirizzo email: " . $email_data['email-address']."<br/><br/>"; $mail_content .= "Testo del messaggio:<br/>“" . $email_data['message'] . "”<br/>"; //Email sent result is stored in a variable and value of it is sent in a JSON object, than WordPress is stopped $email_sent = wp_mail($to, $subject, $mail_content, $headers); echo wp_send_json(['result_email_sent' => $email_sent]); wp_die(); }
Non stiamo qui a commentare tutto il codice ma vediamo alcuni concetti importanti.
Anzitutto, nella sanificazione dei dati, su ogni valore scritto dall’utente viene applicato stripslashes: WordPress filtra l’array $_POST applicando addslashes su ogni stringa e questo potrebbe creare dei problemi, seppur si tratti, molto probabilmente, di una misura di sicurezza. È bene tenerlo presente se si vuole evitare brutte sorprese con qualche stringa contenente eventuali caratteri interessati dal filtro (es. i doppi apici).
Quando c’è da inviare al client una risposta, in caso di validazione non andata a buon fine oppure dopo il tentativo di invio della mail, usiamo wp_send_json() e gli passiamo un array che sarà trasformato in un oggetto JSON. Subito dopo richiamiamo wp_die() per interrompere tutto.
Per tentare di inviare la mail utilizziamo la funzione wp_mail() e ne stampiamo l’esito in un array che inviamo codificato in JSON.
Se si sta sviluppando in locale è molto probabile che l’ambiente di test (XAMPP, Local o altri) sia sprovvisto del server di posta. In questo caso si può optare di usare un server SMTP fornito da qualsiasi provider di posta che consenta questo genere di test (di recente Gmail ha bloccato questa possibilità). Qui ci viene utile un plugin, Easy WP SMTP by SendLayer, che consente di sfruttare l’SMTP di qualsiasi provider: basterà dare le opportune configurazioni, che si possono trovare nelle istruzioni fornite dal provider, ad eccezione della password, e il gioco è fatto. Il plugin, tra l’altro, permette di testare con un invio apposito la correttezza dei settaggi.
Articoli utili
Per fare le operazioni qui spiegate occorre sapere alcuni concetti, approfonditi su questo sito in altri articoli:
- Come creare uno shortcode personalizzato; questa procedura è utile per inserire del codice html, in una pagina WordPress, misto a codice php;
- Come implementare codice JavaScript nei siti WordPress, in questo modo è possibile integrare codice JavaScript gestendo le dipendenze e utilizzare valori ricavati da php. Nell’articolo ci sono anche link a specifiche pagine del WordPress codex, come le librerie JavaScript già incluse in WordPress
- Creare un plugin per aggiungere funzionalità personalizzate; sviluppare un plugin personalizzato è la soluzione ideale per testare quanto è stato spiegato.