AJAX in WordPress: Best Practice

Chi, come me, utilizza WordPress da molto tempo potrebbe pensare di sapere già tutto sull’argomento, probabilmente per la semplicità di utilizzo e funzionamento di questa piattaforma.

Ciò che però potrebbe sfuggire anche ai più esperti è il fatto che WordPress mette a disposizione degli utenti molti strumenti che semplificano l’utilizzo di determinate tecnologie del web, facendo già un buon 60-70% del lavoro per noi.

Nell’articolo di oggi vedremo come implementare richieste asincrone all’interno di un sito sviluppato con WordPress. Può sembrare complicato, in realtà è molto semplice se si sfruttano gli strumenti messi a disposizione dalla piattaforma.

In giro ci sono molti plugin che gestiscono le richieste AJAX in modo del tutto personalizzato, senza utilizzare questi utili strumenti messi a disposizione da WordPress per gestire le richieste asincrone in tutta sicurezza, pulizia e semplicità. Con questo articolo vorrei mostrarvi qual è la best practice da seguire per implementare le funzionalità di AJAX su WordPress.

PREMESSA

La lettura di questo articolo necessita di conoscenze riguardo l’implementazione di funzionalità AJAX e una minima conoscenza sull’utilizzo di ganci e filtri all’interno di WordPress, tra le operazioni più comuni durante lo sviluppo di un plugin.

Qualora non abbiate ancora una conoscenza, anche minima, di questi due argomenti, consiglio la lettura di due articoli di Maurizio:

ALL’OPERA

Prima di iniziare, ripassiamo come funziona il sistema di integrazione di una funzionalità in AJAX:

– Il client effettua una chiamata asincrona al server che la riceve ed elabora una risposta da inviare nuovamente al client. Il client quindi aggiorna il frontend senza bisogno di ricaricare la pagina.

Dobbiamo quindi, in qualità di sviluppatori, elaborare una richiesta lato server, in modo che quest’ultimo fornisca una risposta da inviare al client.

Ciò significa che la richiesta AJAX (come spiegato nell’articolo di Maurizio) deve essere inviata a uno script php che elabora la richiesta per poi fornire il risultato.

Se questo ragionamento viene effettuato su WordPress, avremo la necessità di utilizzare gli strumenti e le funzioni messe a disposizione dalla piattaforma.

I dubbi che potrebbero a questo punto sorgere sono i seguenti:

Come faccio ad eseguire una richiesta a uno script php, se l’unico punto di accesso a WordPress è la index.php nella root della mia installazione?
Se faccio la richiesta ad uno script php che creo io all’interno del mio plugin, come faccio a caricare tutte le funzionalità di WordPress?
Posso mica portarmele dietro durante la richiesta asincrona?

Partendo dall’ultima domanda, la risposta chiaramente è “NO”: WordPress ha già fatto il suo lavoro nel caricare tutte le sue funzionalità nella pagina in cui ti trovi. Tuttavia, per effettuare questa nuova richiesta, ci sarà bisogno di ricaricare WordPress.

Come risposta alle altre due domande, gli utenti  più avanzati potrebbero pensare a una soluzione del genere:

<?php require_once( ‘../../../../wp-load.php’ ) ?>

Bene! È la cosa più sbagliata che si possa fare.
Utilizzando questa riga di codice si rischierebbe di:

  • Non raggiungere correttamente il file: potresti non sapere esattamente dove si trova wp-load.php. La cartella wp-content o la cartella dei plugins possono essere infatti dislocate anche in posti diversi oppure possono cambiare posizione nel corso del tempo, quindi cambierebbe di conseguenza anche il percorso per raggiungere il file. Dovresti quindi ricordarti di aggiornare il percorso di volta in volta… Scomodità inutile;
  • Caricare più di quanto serva: WordPress potrebbe caricare dati che non servono per una richiesta AJAX;
  • Aprire nuovi buchi di sicurezza: questa riga di codice è pur sempre un file direttamente accessibile e, se non aggiungiamo determinati controlli, potrebbe essere un punto di riferimento per attacchi.

admin-ajax.php

Per ovviare a questa bad practice, ci viene incontro il file admin-ajax.php, presente all’interno della cartella wp-admin. Questo è il file che WordPress utilizza per elaborare tutte le richieste AJAX; la best practice da attuare è inviare qualunque richiesta AJAX a questo script, anticipandoci già buona parte del lavoro ed annullando tutti gli svantaggi che porta l’inclusione diretta di wp-load.php.

Tramite questo script, potremmo sempre avere a disposizione gli strumenti di WordPress in tutta sicurezza e facilità.

Ma quindi devo modificare il file admin-ajax.php per elaborare la mia richiesta?

La risposta è: “NO”!

Qui entrano in gioco gli action, spiegati nell’articolo di Maurizio che vi ho suggerito all’inizio dell’articolo. Ci limiteremo semplicemente ad aggiungere un add_action  ad un gancio ben preciso all’interno del nostro codice, in modo da aggiungere un’azione all’interno del file admin-ajax.php, senza modificare realmente il contenuto del file.

Facciamo subito un esempio: mettiamo il caso che il nostro plugin debba dare la possibilità di votare un qualunque articolo e di salvare tutti i voti che gli utenti aggiungono per ogni post (l’esempio sarà molto semplice, con il solo scopo di mostrare come integrare funzionalità AJAX con WordPress).

Prepariamo l’HTML:

<form method="post" id="post-rate">
    <label><input type="radio" name="rate" value="5" /> <small><?php _e( 'Spettacolare!', 'ypr' ) ?></small></label><br />
    <label><input type="radio" name="rate" value="4" /> <small><?php _e( "Bell'articolo.", 'ypr' ) ?></small></label><br />
    <label><input type="radio" name="rate" value="3" /> <small><?php _e( 'Così così..', 'ypr' ) ?></small></label> <br />
    <label><input type="radio" name="rate" value="2" /> <small><?php _e( 'Mah..', 'ypr' ) ?></small></label><br />
    <label><input type="radio" name="rate" value="1" /> <small><?php _e( 'Non ci ho capito niente!', 'ypr' ) ?></small></label><br />

    <br />

    <input type="hidden" name="post_id" value="<?php echo $post->ID ?>" />
    <input type="submit" value="<?php _e( 'Vota!', 'ypr' ) ?>" />
    <img src="<?php echo plugins_url( 'loading.gif', __FILE__ ) ?>" style="display:none;" class="loading" />
</form>

Includiamo un nuovo file javascript, che chiamerò per mia fantasia custom.js, dove aggiungerò successivamente il codice js utile ad inviare la richiesta AJAX:

function my_enqueue_assets() {
    wp_enqueue_script( 'my-custom', 'custom.js', array( 'jquery' ) ); 
    wp_localize_script( 'my-custom', 'my_vars', array(
        'ajaxurl'   => admin_url( 'admin-ajax.php' ))
    );                               
}
add_action( 'wp_enqueue_scripts', 'my_enqueue_assets' );

DI seguito il codice javascript contenuto dentro custom.js:

jQuery(document).ready(function($){
    $('#post-rate').on( 'submit', function(e){
        e.preventDefault();  // evito di far ricaricare la pagina dopo aver effettuato il submit
    
        $.post(
			my_vars.ajaxurl,
			{
				action : 'add_post_rating',      	
				rate : form.find('input[name=rate]:checked').val(),
                post_id : form.find('input[name=post_id]').val()
			},
            
			function( response ) {
                // qualcosa da eseguire se la richiesta è andata a buon fine
			}
		);            
    });             
});

Andiamo nel dettaglio:

  1. L’HTML si limita a stampare il form di scelta;
  2. Tramite le funzioni di WordPress, includo il file custom.js all’interno della pagina e utilizzo anche la funzione wp_localize_script per aggiungere un oggetto globale nella pagina, dove definisco alcuni dati importanti. Senza questa funzione non avrei la possibilità di conoscere all’interno un file esterno (nel nostro caso custom.js).
    Nell’esempio che segue, mi sarà utile per passare l’url del file admin-ajax.php. Quello che verrà generato sarà:

     
    <script type='text/javascript'>
    /* <![CDATA[ */
    var my_vars = {"ajaxurl":"http:\/\/test.wp.com\/wp-admin\/admin-ajax.php"};
    /* ]]> */
    </script>
    

    NB: questa operazione deve essere fatta soltanto lato frontend. Se la richiesta deve essere eseguita nel pannello di amministrazione, WordPress mette già a disposizione la variabile global ajaxurl atta allo scopo;

  3. Il codice di custom.js si occupa essenzialmente di inviare la richiesta AJAX al server.

Come puoi notare nel codice javascript, il file che riceverà la richiesta sarà my_vars.ajaxurl, che altro non è che l’url alla pagina admin-ajax.php, definita tramite la funzione wp_localize_script.
Inoltre, noterai che tra i dati passati alla richiesta, aggiungo anche un campo action. Bene, questo è il nome dell’azione che utilizzeremo per agganciare il nostro codice php da elaborare durante la richiesta AJAX.

Il valore action è quello che WordPress si aspetta per sapere quale codice utilizzare per elaborare la richiesta ed è un dato richiesto dalla piattaforma. Il nome dell’azione può essere scelto a nostro piacimento (nel mio caso l’ho chiamato add_post_rating), ma il nome del dato deve essere action.

wp_ajax_

Adesso l’unica cosa che manca è elaborare la richiesta asincrona lato server. Come detto precedentemente, l’unica cosa da fare sarà agganciare il nostro codice tramite add_action e il nome del gancio alla quale dovremmo agganciarci sarà: “wp_ajax_$action”, dove per $action intendo lo stesso valore di action che abbiamo definito nel codice javascript (nel nostro caso è add_post_rating).

Il tutto si traduce quindi in:

function my_add_rate_ajax() {
    $user_rate = intval( $_REQUEST['rate'] );
    $post_id   = intval( $_REQUEST['post_id'] );  
    
    // recupero l'attuale votazione memorizzata all'interno del post
    $post_rate    = floatval( get_post_meta( $post_id, '_post_rate', true ) );
    $rating_count = intval( get_post_meta( $post_id, '_post_rate_count', true ) );
    
    // se c'è già un valore memorizzato, calcola il voto insieme a quello dato dall'utente
    if ( $post_rate )  {
        $post_rate = ( $post_rate + $user_rate ) / 2;
        $rating_count++;
    }
    
    // altrimenti aggiungi semplicemente il voto dell'utente nel database      
    else {
        $post_rate = $user_rate;
        $rating_count = 1;
    }     
    
    // aggiorno il valore nel database
    update_post_meta( $post_id, '_post_rate', $post_rate ); 
    update_post_meta( $post_id, '_post_rate_count', $rating_count ); 
    
    // do una risposta al client
    echo "Grazie per aver votato!";
    
    // chiudo l'esecuzione della chiamata AJAX, evitando di far ritornare errori da parte di WordPress
    die();
}
add_action( 'wp_ajax_add_post_rating', 'my_add_rate_ajax' );

Quando la richiesta arriverà ad admin-ajax.php, WordPress reindirizzerà la richiesta alla nostra funzione, tramite il valore dato ad action.

A questo punto, il codice da noi definito verrà elaborato soltanto per gli utenti autenticati, in quanto admin-ajax.php è un file che risiede nel lato admin di WordPress. Per eseguire l’elaborazione nel frontend per utenti non loggati, bisognerà agganciare la funzione anche al gancio “wp_ajax_nopriv_$action”, quindi il codice precedente sarà modificato in:

function my_add_rate_ajax() {
…
}
add_action( 'wp_ajax_add_post_rating', 'my_add_rate_ajax' );
add_action( 'wp_ajax_nopriv_add_post_rating', 'my_add_rate_ajax' );

Entrambi i ganci vanno definiti, il primo add_action verrà eseguito per gli utenti autenticati al momento della richiesta, mentre il secondo per gli utenti non autenticati.
Infine WordPress lascia a noi la scelta di completare o meno la richiesta, semplicemente richiamando die() alla fine della nostra elaborazione. Senza di questa, WordPress completerà la richiesta ritornando il valore ‘0’, come segno che qualcosa è andato storto o che l’elaborazione non è stata eseguita correttamente.

NONCE: UN PO’ DI SICUREZZA

Per ultimo, aggiungiamo un po’ di sicurezza alla nostra richiesta, tramite i nonce. Tramite questo sistema, possiamo assicurarci che l’azione non venga eseguita da persone non autorizzate.

Dalla richiesta javascript generiamo un codice casuale valido solo per un breve periodo di tempo, soltanto per l’utente che sta eseguendo la richiesta e che verrà verificato in fase di elaborazione.

Se la verifica del codice darà esito negativo, la richiesta verrà stoppata prima di avviare l’elaborazione vera e propria.

Il codice verrà inoltre associato al nome di un’azione che diamo a nostra scelta, nel nostro caso potremmo chiamarla: “add-post-rating-nonce”.

Per integrare questo sistema di sicurezza, ci basteranno fare tre semplici modifiche al codice precedente.

Genero il codice casuale tramite la funzione wp_create_nonce e l’aggiungo all’array passato a wp_localize_script, in quanto ci servirà all’interno del codice di custom.js, per includerlo tra i dati da inviare insieme alla richiesta:

wp_localize_script( 'ypr-custom', 'ypr_vars', array(
    'ajaxurl'   => admin_url( 'admin-ajax.php' ),
    'nonce'     => wp_create_nonce( 'add-post-rating-nonce' ))
);  

Aggiungo il dato dentro il javascript che invia la richiesta:

{
    action : 'add_post_rating',      
    _nonce : my_vars.nonce,      	
    rate : form.find('input[name=rate]:checked').val(),
    post_id : form.find('input[name=post_id]').val()
},

Ed infine verifico che il nonce sia corretto durante l’elaborazione:

function my_add_rate_ajax() {
    if ( ! wp_verify_nonce( $_REQUEST['_nonce'], 'add-post-rating-nonce' ) )
        die ( 'Non autorizzato!');
…
}
add_action( 'wp_ajax_add_post_rating', 'my_add_rate_ajax' );
add_action( 'wp_ajax_nopriv_add_post_rating', 'my_add_rate_ajax' );

 

CONCLUSIONE

Alla fine è più complicato spiegarlo che farlo!

Come avrai potuto notare, l’unica cosa di cui ci dobbiamo preoccupare è sviluppare il codice che si occupa di elaborare la richiesta. Non ci serve sapere né a che file fare la richiesta, né ideare modi per far convivere differenti richieste AJAX, né scrivere chissà quante righe di codice superfluo.

Solo un action da definire e un/due add_action da aggiungere. Stop!

Vi rendo anche disponibile un piccolo plugin di dimostrazione per provare le modalità spiegate per l’integrazione AJAX, scaricabile da GitHub.

E tu hai mai utilizzato questo metodo? Anche tu stavi lì a cercare di ideare metodi fantasiosi e complicati per gestire le richieste asincrone nel tuo sito?

Tag: ,

L'autore

Da sempre amante di codice, programmazione e buona musica. Specializzato in Wordpress e sviluppo web (HTML, CSS, PHP, MySQL, ecc..), ma sempre alla ricerca di nuove tecnologie web. Attualmente lavora nel team di sviluppo di YIW, con allegria e passione.

Altri articoli scritti da

Articoli correlati

5 commenti

Trackback e pingback

  1. AJAX in WordPress: Best Practice | Your Inspira...
    […] Nell'articolo di oggi vedremo come implementare richieste asincrone all'interno del nostro sito sviluppato con WordPress.  […]