Cinque tecniche collaudate che il tuo sviluppatore dell'API di WordPress non sta utilizzando

Pubblicato: 2022-03-11

Uno dei modi migliori per elevare il tuo status di sviluppatore WordPress, almeno agli occhi dei tuoi clienti, è diventare esperto nel consumo di API. Ecco uno scenario comune per l'implementazione dell'API di WordPress: il tuo cliente ti chiede di aggiungere un widget al suo sito, ad esempio un widget di abbonamento e-mail. Prendi del codice dal loro servizio di posta elettronica di terze parti, forse è un tag di script o un iframe , lo incolli nella pagina e rispondi al tuo cliente: "Capito!"

Sfortunatamente, hai a che fare con un cliente un po' più esigente e nota le seguenti imperfezioni:

  • Sebbene il widget, come il resto del sito, presenti un font sans-serif, non è proprio quello giusto. Il widget utilizza Helvetica invece del carattere personalizzato che hai installato.
  • Il modulo di iscrizione del widget attiva un nuovo caricamento della pagina, che può essere di disturbo se posizionato a metà di un articolo.
  • Il widget sembra richiedere un momento in più per caricarsi dopo il resto della pagina, il che sembra stridente ed economico.
  • Il cliente desidera che gli abbonati vengano taggati con metadati in base al post da cui si sono iscritti e il widget non offre nulla che assomigli in remoto a questa funzionalità.
  • Il client trova fastidioso dover gestire due dashboard (wp-admin e l'area di amministrazione per il servizio di posta elettronica).

A questo punto, una delle due cose potrebbe ragionevolmente accadere. Potresti dichiarare questi articoli "piaciuti da avere" e rassicurare il tuo cliente sui meriti di una soluzione 80/20, oppure potresti soddisfare quelle richieste. Nella mia esperienza personale, ho scoperto che soddisfare tali richieste, ovvero dimostrare la padronanza di servizi di terze parti, è un modo affidabile per convincere il cliente che sei una sorta di mago di WordPress. Inoltre, è spesso divertente da fare.

Negli ultimi dieci anni, ho utilizzato WordPress come piattaforma per il consumo di API contro probabilmente 50 API diverse. Alcune delle API più comuni sono state MailChimp, Google Analytics, Google Maps, CloudFlare e Bitbucket. Ma cosa succede se hai bisogno di fare di più, e se hai bisogno di una soluzione personalizzata?

Come sviluppare un client API di WordPress

In questo articolo, svilupperò rispetto a un'API generica di "servizio di posta elettronica", facendo del mio meglio per mantenere le cose il più agnostiche possibile. Tuttavia, ritengo sia ragionevole presumere che abbiamo a che fare con un'API REST JSON. Ecco alcuni argomenti di base che potrebbero aiutarti a goderti i punti tecnici in questo articolo:

  • La famiglia di funzioni HTTP di WordPress
  • JSON
  • RIPOSO

Se ti ritrovi una familiarità marginale con questi argomenti e sei interessato a scavare più a fondo, fermati subito e scarica l'eccellente applicazione Postman. Ti permette di comunicare con le API senza scrivere codice.

Screenshot della dashboard del postino

Postino. Forse il mio strumento di sviluppo preferito?

Tuttavia, se non li conosci per niente, continua a leggere comunque. Un pubblico tecnico con un certo grado di esperienza con WordPress otterrà il massimo da questo articolo, ma mi occuperò di spiegare il valore di ciascuna tecnica in un modo meno tecnico. Un lettore non tecnico lascerà questo articolo in grado di valutare il ROI di ogni punto prima di sponsorizzarlo e giudicare la qualità dell'implementazione una volta consegnato.

Nota: se hai bisogno di un rapido corso di aggiornamento, puoi consultare la nostra guida all'API REST di WordPress.

Senza ulteriori preamboli, permettetemi di condividere con voi una manciata di tecniche diverse che mi ritrovo ad apprezzare in quasi ogni API, progetto e team con cui lavoro.

Transitori: quando tenerli, quando piegarli

Nel mio paragrafo di apertura, ho notato che il client trovava fastidioso trovarsi a cavallo di due aree di amministrazione: wp-admin e la dashboard per il loro servizio di posta elettronica. Un buon modo per risolverlo sarebbe fornire loro un widget dashboard in wp-admin, per mostrare un riassunto della loro recente attività di abbonato.

Screenshot del widget dashboard wp-admin

Un esempio del tipo di interfaccia utente dashboard che potremmo fornire all'interno di WordPress, per salvare i nostri clienti in un viaggio verso il loro fornitore di servizi di posta elettronica di terze parti.

Ma ancora una volta, ciò potrebbe richiedere più richieste HTTP all'API remota (l'API fornita dal servizio di posta elettronica), con conseguenti lunghi caricamenti di pagina. La soluzione a questo problema di prestazioni consiste nell'archiviazione delle chiamate API come transitori. Questo articolo del Codex fornisce un'ottima spiegazione che dovresti assolutamente leggere, ma la riassumerò così:

  1. Ottieni i dati dall'API remota.
  2. Memorizzalo utilizzando set_transient() con un tempo di scadenza a tua scelta in base al tuo giudizio su prestazioni, limiti di velocità e margine di errore nella visualizzazione di dati obsoleti in questa particolare applicazione.
  3. Procedi secondo la tua logica aziendale, elaborando i dati, restituendo un valore, qualunque sia il caso.
  4. Quando hai nuovamente bisogno dei dati, ad esempio al caricamento della pagina successiva, controllali nella cache transitoria usando get_transient() prima di concludere che devi recuperarli dall'API.

Considero questa una base utile e praticabile, ma puoi fare un ulteriore passo avanti se pensi per un momento ai verbi REST. Dei cinque metodi più comuni (GET, POST, PATCH, PUT, DELETE), solo uno di questi appartiene alla cache transitoria. riesci a indovinare quale? È OTTENERE. Nei miei plugin, ho quasi sempre una classe PHP dedicata all'astrazione delle chiamate all'API remota in questione e un argomento quando si crea un'istanza di quella classe è il metodo HTTP. Se non è una chiamata GET, non invocherò alcun livello di memorizzazione nella cache.

Inoltre, se non si tratta di una chiamata GET, è ovvio che sto intraprendendo un'azione per alterare i dati remoti in qualche modo, magari aggiungendo, modificando o rimuovendo un abbonato e-mail. Questo potrebbe essere un buon momento per invalidare la cache esistente per quella risorsa, tramite delete_transient() .

Per tornare al nostro esempio di un'API di abbonamento e-mail di WordPress, ecco come funzionerebbe in pratica:

  • Un widget dashboard per mostrare gli abbonati recenti chiamerà l'endpoint API per /subscribers tramite una richiesta GET. Poiché è una richiesta GET, viene archiviata nella mia cache transitoria.
  • Un widget della barra laterale per l'iscrizione all'elenco di posta elettronica chiamerà l'endpoint API per /subscribers tramite una richiesta POST. Poiché è una richiesta POST, non solo eviterà la mia cache transitoria, ma mi provocherà a eliminare la parte pertinente della mia cache transitoria, in modo che il widget del dashboard rifletta questo nuovo abbonato.
  • Quando nomino i transitori, spesso li organizzo nominandoli letteralmente in base all'URL dell'API remota che sto chiamando. Questo è un modo pratico per identificare il transitorio corretto da eliminare. Se è un endpoint che accetta argomenti, li concatenerò in una stringa e li aggiungerò anche al nome transitorio.

In qualità di cliente o altro stakeholder meno tecnico, dovresti richiedere specificamente la memorizzazione nella cache transitoria, o almeno una relativa discussione, ogni volta che l'applicazione estrae dati da un servizio remoto. Dovresti familiarizzare con l'eccellente plug-in Query Monitor per avere una visione di come funzionano i transitori. Ti fornirà un'interfaccia per sfogliare quali dati vengono nascosti come transitori, con quale frequenza e per quanto tempo.

A volte i transitori non sono abbastanza buoni

Alcuni servizi di hosting WordPress premium in realtà non ti consentono di utilizzare i transitori in produzione. Hanno codice in esecuzione, forse sotto forma di un plug-in MU o qualche altro script, che intercetterà il tuo tentativo di utilizzare l'API transitoria e memorizzerà invece tali informazioni tramite la cache degli oggetti. WP-Engine, nella sua configurazione più comune, ne è un ottimo esempio.

Screenshot della vista phpMyAdmin descritta nella didascalia

Uno spettacolo allarmante nell'interfaccia utente di phpMyAdmin: un sito di produzione completamente privo di transitori? Questo probabilmente significa che la memorizzazione nella cache degli oggetti è al lavoro.

Se stai semplicemente archiviando e recuperando dati, in realtà non devi preoccuparti di questo e potresti anche non accorgerti che sta accadendo. L'intera famiglia di *_transient() ti fornirà lo stesso risultato finale, solo filtrato per utilizzare la cache degli oggetti invece della cache transitoria. Tuttavia, potresti riscontrare problemi quando tenti di eliminare i transitori. Ecco perché.

Se l'integrazione dell'API è abbastanza complessa da meritare una propria pagina delle impostazioni, potresti voler includere un'interfaccia utente per consentire all'utente amministratore di svuotare l'intera cache transitoria per il tuo plug-in . L'uso più comune di questo pulsante sarebbe quando il client modifica alcuni dati direttamente sul servizio remoto e desidera invalidare la cache che stiamo archiviando in WordPress. Questo pulsante potrebbe anche tornare utile se il client modifica le credenziali dell'account, le chiavi API o semplicemente come pulsante di "ripristino delle impostazioni di fabbrica" ​​per il debug.

Screenshot dei pulsanti di opzione

Un esempio di interfaccia utente per consentire al client di svuotare la cache locale per i propri dati API.

Anche se fossi abbastanza intelligente da assegnare uno spazio ai nomi di tutte le tue chiavi transitorie in modo da avere qualche speranza di identificarle per delete_transient() , lo scenario migliore probabilmente coinvolge ancora l'SQL grezzo, che cerco sempre di evitare in WordPress:

 <?php // Purge all the transients associated with our plugin. function purge() { global $wpdb; $prefix = esc_sql( $this -> get_transient_prefix() ); $options = $wpdb -> options; $t = esc_sql( "_transient_timeout_$prefix%" ); $sql = $wpdb -> prepare ( " SELECT option_name FROM $options WHERE option_name LIKE '%s' ", $t ); $transients = $wpdb -> get_col( $sql ); // For each transient... foreach( $transients as $transient ) { // Strip away the WordPress prefix in order to arrive at the transient key. $key = str_replace( '_transient_timeout_', '', $transient ); // Now that we have the key, use WordPress core to the delete the transient. delete_transient( $key ); } } ?>

Non conveniente, non efficiente. Invece, questa situazione richiede la memorizzazione nella cache degli oggetti perché la memorizzazione nella cache degli oggetti ci offre un modo conveniente per raggruppare i valori memorizzati nella cache . In questo modo, quando devi svuotare tutti i valori memorizzati nella cache relativi al tuo plugin, è una semplice chiamata one-liner a wp_cache_delete( $key, $group ) .

Riassumerei tutto questo dicendo: non puoi essere un esperto nel consumo di API se non sei ancora un esperto nella gestione della cache per quei dati.

Come cliente, la cosa fondamentale a cui prestare attenzione è il comportamento aberrante della cache tra gli ambienti di staging e di produzione. In altre parole, anche se testare un nuovo lotto di lavoro nello staging è sempre una buona pratica, il caching è qualcosa che deve essere testato anche in produzione con la stessa attenzione.

L'API remota può aiutarti a informare la tua gerarchia di classi PHP

Quando elaboro le varie classi PHP per il mio plug-in, spesso trovo utile imitare il modo in cui sono definiti gli endpoint dell'API, ad esempio, cosa sembrano avere in comune i seguenti endpoint?

  • https://api.example-email-service.com/v1/subscribers.json
  • https://api.example-email-service.com/v1/lists.json
  • https://api.example-email-service.com/v1/campaigns.json

Restituiscono tutti collections , con cui intendo il risultato di una richiesta GET, restituendo da zero a molti risultati in cui ogni risultato è un membro di un array. Potrebbe sembrare abbastanza ovvio, ma trovo che sia un utile prompt per la seguente struttura di classi nel mio codice PHP:

  • class.collection.php , una classe astratta
  • class.subscribers.php estende la classe astratta, Collection .
  • class.lists.php estende la classe astratta, Collection .
  • class.campaigns.php estende la classe astratta, Collection .

La classe astratta prenderebbe come unico argomento una matrice di parametri di query: cose come l'impaginazione, l'ordinamento delle colonne, l'ordinamento e i filtri di ricerca. Avrebbe metodi per attività comuni come chiamare l'API remota, gestire gli errori e forse trasformare i risultati in un menu <select> HTML o in un AutoSuggest jQueryUI. Le classi che istanziano la classe astratta potrebbero essere piuttosto brevi, forse facendo poco più che specificare la stringa da utilizzare nell'URL dell'endpoint dell'API *.json .

Screenshot del playground dell'API Mailchimp

Mailchimp pubblica un "parco giochi" API per chiamate API sandboxing e simili. Agisce anche come un modo conveniente per navigare nell'intera gerarchia di dati della loro API, offrendoci un'utile visione di come potremmo strutturare la nostra gerarchia di classi.

Allo stesso modo, cosa hanno in comune i seguenti endpoint?

  • https://api.example-email-service.com/v1/subscribers/104abyh4.json
  • https://api.example-email-service.com/v1/lists/837dy1h2.json
  • https://api.example-email-service.com/v1/campaigns/9i8udr43.json

Restituiscono tutti un articolo , con il quale intendo esattamente un membro specifico e unico di una raccolta: cose come un particolare abbonato e-mail, un elenco e-mail o una campagna e-mail. Pertanto, mi piace usare la seguente struttura nel mio codice PHP:

  • class.item.php , una classe astratta
  • class.subscriber.php estende la classe astratta, Item .
  • class.list.php estende la classe astratta, Item .
  • class.campaign.php estende la classe astratta, Item .

La classe astratta prenderebbe come unico argomento una stringa per identificare l'elemento specifico richiesto. Ancora una volta, le classi di cui viene creata un'istanza potrebbero essere piuttosto brevi, forse facendo poco più che specificare la stringa da utilizzare in */duy736td.json .

Esistono molti approcci alla strutturazione dell'ereditarietà delle classi, ma anche se si adotta un approccio diverso da quello che ho delineato sopra, scommetto che ci sono buone probabilità che la struttura dell'API remota possa aiutare a informare la struttura della tua applicazione.

Come client, un sintomo comune di un'architettura scadente è quando ti ritrovi a dover richiedere la stessa modifica più e più volte in un'applicazione. Ad esempio, se richiedi che i rapporti restituiscano 100 risultati per pagina anziché 10 e devi continuare a ripetere tale richiesta per rapporti sugli iscritti, rapporti sulle campagne, rapporti di annullamento dell'iscrizione e così via, potresti rilevare un'architettura di classe scadente. In questa situazione, vale la pena chiedere al tuo team se trarrebbero vantaggio da un ciclo di refactoring: un lavoro in cui l'obiettivo non è quello di modificare il comportamento del prodotto, ma piuttosto di migliorare il codice sottostante in modo che diventi più facile modificare il comportamento del prodotto in futuro.

Il caso d'uso perfetto per WP_Error

Sono imbarazzato ad ammettere che mi ci sono voluti anni in più del dovuto per iniziare correttamente a utilizzare la famiglia di funzioni WP_Error nel mio codice. Tendevo a programmare solo a modo mio, presumendo che non ci sarebbero mai stati errori di cui valesse la pena preoccuparsi a livello di codice o gestirli caso per caso. Lavorare con le API remote elimina questa mentalità come un raggio laser, perché presenta un caso d'uso estremamente conveniente e potente per l'utilizzo di WP_Error .

Ricordiamo in precedenza che ho detto che ho spesso una classe PHP il cui scopo è fare richieste HTTP all'API remota. Quando rimuovi tutto il boilerplate, tutta la manipolazione dei dati, tutte le preoccupazioni secondarie, quella classe si riduce davvero a chiamare wp_remote_request() in modo da ottenere un oggetto di risposta HTTP dall'API. Convenientemente, wp_remote_request() restituirà invece un WP_Error se la chiamata non viene eseguita per qualche motivo, ma che dire se la chiamata riesce a restituire una risposta HTTP di tipo sfavorevole?

Screenshot di un modulo di iscrizione

Tecnicamente parlando, la chiamata API ha funzionato , ma non è esattamente priva di avvertimenti. Questi avvertimenti devono essere acquisiti e segnalati in modo coerente in tutta la base di codice.

Ad esempio, forse abbiamo effettuato una chiamata all'endpoint /lists.json , ma questo particolare account non ha ancora alcun elenco impostato. Ciò restituirebbe una risposta HTTP valida, ma con un codice di stato 400. Anche se non è esattamente un errore fatale di per sé, dal punto di vista di un codice front-end che vuole trasformare questa chiamata API in un menu a discesa, un 400 potrebbe anche essere un WSOD! Pertanto, trovo utile eseguire alcune analisi aggiuntive sul risultato di wp_remote_request() , restituendo potenzialmente un WP_Error dopo tutto:

 <?php function call() { $response = wp_remote_request( $this -> url, $this -> args ); $code = wp_remote_retrieve_response_code( $response ); $first_digit = $code[0]; $good_responses = array( 2, 3 ); if( ! in_array( $first_digit, $good_responses ) { $body = wp_remote_retrieve_body( $response ); $out = new WP_Error( $code, $body ); } else { $out = $response; } return $out; } ?>

Questo modello può aiutare a semplificare il codice che invoca la nostra classe chiamante, perché sappiamo che possiamo tranquillamente fare affidamento su is_wp_error() prima di procedere con il nostro output.

Come cliente, dovresti occasionalmente interpretare il ruolo di un utente malintenzionato, un utente confuso e un utente impaziente. Usa l'app in modi in cui non doveva essere utilizzata. Fai le cose che i tuoi sviluppatori sembrano non volere che tu faccia. Prendi nota di ciò che accade. Ricevi utili messaggi di errore? Ricevi qualche messaggio di errore? In caso contrario, potrebbe valere la pena sponsorizzare un lavoro per una migliore gestione degli errori.

Il bellissimo potere di debug di ob_get_clean()

Il moderno Web programmabile, in cui quasi ogni sito utilizza le API di altri siti e viene a sua volta consumato tramite la propria API, è diventato un'arena incredibilmente potente per il codice. Ma è proprio questa qualità che può anche renderlo piuttosto lento.

È comune che le richieste HTTP remote siano le parti che richiedono più tempo di un determinato caricamento della pagina. Per questo motivo, molti componenti basati su API vengono eseguiti tramite Ajax o cron. Ad esempio, un suggerimento automatico per la ricerca in un elenco di abbonati e-mail dovrebbe probabilmente eseguire il ping dell'origine dati remota su richiesta, a ogni battitura, anziché caricare tutti i 100.000 abbonati all'interno del DOM durante il caricamento della pagina. Se questa non è un'opzione, forse una query di grandi dimensioni potrebbe sincronizzarsi su un'attività cron notturna, in modo che i risultati possano essere estratti da un mirror locale anziché dall'API remota.

Il problema con questo approccio è che può essere difficile eseguire il debug. Invece di attivare semplicemente WP_DEBUG e lasciare che i messaggi di errore arrivino nella finestra del browser, sei bloccato a guardare nella console di rete del browser o a pedinare un file di registro mentre un'attività cron (si spera?) è in esecuzione. Lo trovo scomodo.

Un modo per migliorare questa situazione è effettuare chiamate attente e strategiche a error_log() . Ma ancora una volta, un problema comune con la registrazione è che con applicazioni grandi o occupate, i log degli errori possono crescere troppo o crescere troppo rapidamente per essere utili per il monitoraggio o l'analisi. Pertanto, dobbiamo essere selettivi con ciò che registriamo, riflettendoci tanto quanto facciamo con la nostra logica applicativa effettiva . È un peccato aver preso il tempo per registrare un errore di caso limite esotico che sembra verificarsi solo in modo intermittente su alcune attività cron non frequenti solo per rendersi conto che la vera natura del bug ti ha eluso ancora una volta perché non sei riuscito a registrare un particolare membro dell'array , diciamo, del valore offensivo.

Pertanto, la mia filosofia è diventata, non registro sempre, ma quando lo faccio registro tutto . In altre parole, dopo aver individuato una funzione particolarmente preoccupante, la registrerò con una rete il più ampia possibile:

 <?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?>

Ciò equivale a var_dump() 'ing l'intero valore buggy in una singola voce nel file di registro degli errori.

Screenshot del file di registro degli errori

Un registro degli errori che è diventato troppo grande per essere ergonomico per il debug.

Come client, vale la pena controllare periodicamente l'utilizzo totale della memoria dei file per la tua applicazione. Se noti che ti stai improvvisamente scontrando con i limiti di archiviazione nel tuo account di hosting, ci sono buone probabilità che la colpa sia di un registro degli errori scatenato. I tuoi sviluppatori trarranno vantaggio da un ciclo di lavoro incentrato su una migliore registrazione, e anche i tuoi clienti lo faranno!

Non esattamente Clickbait, ma lo farà

Si prega di perdonare la struttura a elenco di questo articolo. Non potevo forzare questi punti in un tema dell'articolo più unificante perché questi modelli sono molto generici: si applicano a qualsiasi endpoint JSON REST e qualsiasi output di WordPress .

Sono i modelli che vedo ripetersi più e più volte, indipendentemente da cosa sia l'API remota o per cosa la stiamo usando all'interno di WordPress. Sono arrivato al punto di raccogliere tutti questi tipi di principi in un plug-in standard che accelera notevolmente il mio lavoro. Hai punti simili che mantieni per ogni progetto? Per favore condividili così posso rubarli e aggiungerli al mio boilerplate!

Correlati: come affrontare lo sviluppo moderno di WordPress (parte 1)