Creare un badge Instagram nel proprio sito usando le API

Interrogare le API è discretamente semplice. Io l'ho fatto in PHP.
La documentazione sulle API di Instagram si trova qui http://instagr.am/developer/.

Vi spiegherò brevemente come fare i primi passi per operazioni generiche, per operazioni che coinvolgano l'utente, e per impostare il realtime.

PASSO 1: Registrare la propria applicazione

Analogamente ad altri social network, anche Instagram richiede la creazione di un'app per poter utilizzare le API. Si può creare la propria app qui: http://instagr.am/developer/manage/
In cambio si riceveranno un ID ed un SECRET.

L'ID può essere utilizzato per richieste generiche che non coinvolgano relazioni tra utenti. In pratica, quando ci basta giocare con i dati pubblici, come mostrare le foto scattate in un luogo etc.

Il SECRET invece serve per poter autenticare un utente ed elaborare i suoi dati, i suoi contatti etc, quindi per fornire un'esperienza personalizzata. L'utente ovviamente dovrà consentire l'accesso ai suoi dati attraverso una richiesta automatica.

PASSO 2: Recuperare le foto scattate in un luogo

Una delle funzioni più divertenti è vedere le foto di un luogo preciso, nel mio caso del Boca Barranca. Per farlo, non serve effettuare il log-in, basta utilizzare il proprio ID.
I passaggi da seguire sono: trovare l'id del locale a partire da una coordinata geografica, cercare le foto assegnate a quell'id.

Vediamolo passo a passo, poi alla fine vi metto il codice intero.

Scrivo l'URL per cercare la location che mi interessa

	$url="https://api.instagram.com/v1/locations/search?lat=44.525655&lng=12.278917&distance=1000&client_id=12345;

Dove l'ho trovata? Ovviamente, nella documentazione ufficiale.
client_id è l'id della vostra applicazione, l'avete ricevuto quando l'avete creata e potete sempre andarvelo a rivedere alla voce Manage.
distance è il raggio entro il quale cercare, espresso in metri.
lat e lng sono la latitudine e la longitudine. Come li ho trovati, i miei? Con Google Maps.


Ho cercato il locale, cliccato sul tasto a forma di catena per avere il link alla mappa, e copiato il link, che è questo: http://maps.google.it/maps?q=boca+barranca,+ravenna&hl=it&sll=41.426253,4.042968&sspn=27.187351,54.711914&t=v&hq=boca+barranca,&hnear=Ravenna,+Emilia+Romagna&z=11
Vedete che c'è sll=? Subito dopo ci sono, separate dalla virgola, latitudine e logitudine.

Imposto cURL

Senza entrare nei dettagli, userò cURL per effettuare la richiesta. Basta impostarlo, così:

	$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

La penultima riga dice a cURL di dare per validi tutti i certificati SSL. Essendo che il nostro URL è criptato, lo notate perché inizia con https invece che con http, occorrerebbe validare il certificato. C'è un modo più elegante per farlo, ma questo va benissimo per noi adesso.
L'ultima riga invece dice a cURL di restituire la risposta in una variabile, anziché mostrarla a video.

Eseguo la richiesta e interpreto i risultati

	$response=curl_exec($ch);
curl_close($ch);

Ora ho la risposta dentro a $response. La risposta è in formato JSON, che è un modo di strutturare i dati. Decodifico questo formato in favore di uno un po' più commestibile per PHP.

	$locations=json_decode($response);

Ora non mi resta che guardare cosa ha trovato e, se tra questi c'è il Boca Barranca, mi memorizzo il suo ID.

	$locationId=0;
foreach($locations->{'data'}
as $location) {
    if($location->{'name'}=='Boca Barranca')
{
        $locationId=$location->{'id'};
        break;
        }
    }

Esco da questo ciclo con l'Id del mio locale memorizzato in $locationId. Se non ho trovato il locale, $locationId vale 0.
Se volete invece guardare l'elenco di tutti i locali che ha trovato, basta scrivere
print_r($locations);

Tutta questa parte non occorre rifarla ogni volta, una volta scoperto l'id del locale potete usarlo in maniera statica, riducendo il numero di chiamate alle API e quindi aumentando la velocità del nostro script e il benessere del server Instagram.

Recuperare le immagini recenti della nostra location

Con un'altra chiamata cURL recupero le foto recenti. L'indirizzo da utilizzare l'ho trovato sempre sulla documentazione, dove ci sono spiegate tante altre cose da poter fare in maniera analoga.

	$url="https://api.instagram.com/v1/locations/".$locationId."/media/recent/?&client_id=12345";
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$response=curl_exec($ch);
curl_close($ch);
$photos=json_decode($response);

Ora in $photos ho l'elenco di tutte le ultime foto scattate al Boca Barranca. Visualizzo le anteprime a video, linkate all'originale in instagram:

	foreach($photos->{'data'}
as $photo) { ?>
    <a href="<?= $photo->{'link'};
?>"><img src="<?=
$photo->{'images'}->{'thumbnail'}->{'url'}; ?>"
/></a>
    <? }

Avrei potuto fare tante altre cose, utilizzando tutti gli altri campi come i dettagli sull'autore, i commenti alla foto, le dimensioni della foto etc. Tutti i campi sono elencati... nella documentazione ;)

Il codice completo

	$url="https://api.instagram.com/v1/locations/search?lat=44.525655&lng=12.278917&distance=1000&client_id=12345;
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$response=curl_exec($ch);
curl_close($ch);

	

$locations=json_decode($response); $locationId=0; foreach($locations->{'data'} as $location) {     if($location->{'name'}=='Boca Barranca') {         $locationId=$location->{'id'};         break;         }     }

$url="https://api.instagram.com/v1/locations/".$locationId."/media/recent/?&client_id=12345"; $ch=curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); $response=curl_exec($ch); curl_close($ch); $photos=json_decode($response);

	foreach($photos->{'data'}
as $photo) { ?>
    <a href="<?= $photo->{'link'};
?>"><img src="<?=
$photo->{'images'}->{'thumbnail'}->{'url'}; ?>"
/></a>
    <? }

PASSO 3: E se ci servisse l'autenticazione?

Come ho scritto prima, per avere a che fare con le relazioni tra utenti occorre essere autenticati. Cioè occorre dire "Io sono Roberto Pasini, fammi vedere Instagram dalla mia prospettiva".
Autenticarsi permette di fornire delle esperienze personalizzate a tutti gli utenti che accetteranno di concedere l'accesso ai propri dati alla nostra applicazione, e cioè si potranno anche compiere azioni a nome loro, come commentare o dare il like a delle foto.

Come si fa? Sempre con delle chiamate, ma questa volta Instagram ci farà rimbalzare su una propria pagina, dove verrà chiesto il permesso dell'utente, e poi tornerà al nostro sito chiedendoci di confermare l'autorizzazione. Solo allora ci restituirà un token, cioè un codice che è valido temporaneamente per effettuare le operazioni desiderate a nome dell'utente.

Vediamo passo a passo, e in fondo tutto il codice.

Imposto i parametri della mia app

	$clientID='12345';
$clientSecret='abcde';
$code=$_REQUEST["code"];
$my_url="http://www.ilmiosito.com/instagram/handler.php";

La variabile $my_url non è altro che l'indirizzo completo del file che state facendo. Instagram farà sempre riferimento a lui.

Chiedo l'autorizzazione dell'utente

	if(empty($code))
{
    $dialog_url="https://api.instagram.com/oauth/authorize/?client_id=".$clientID."&redirect_uri=".urlencode($my_url)."&response_type=code";
    header('Location:
'.$dialog_url);
    die();
    }

In pratica, se non ho già ricevuto un codice da Instagram, redireziono l'utente sulla pagina che gli chiederà il permesso di utilizzare la mia app. Questo permesso verrà chiesto solo la prima volta che l'utente utilizza la app. L'utente può revocarlo in qualsiasi momento dall'elenco delle sue applicazioni.

Ricevuto il permesso, richiedo il token

Faccio l'ennesima chiamata cURL per ricevere il token che mi permetterà di mettermi nei panni dell'utente. La chiamata è di tipo POST, come potete leggere nella documentazione (sempre lei), e quindi devo passare le 5 variabili via POST (questo è il motivo delle ultime due curl_setopt).

	if(!empty($code))
{
    $token_url="https://api.instagram.com/oauth/access_token";
    $fields_string="client_id=".$clientID."&redirect_uri=".urlencode($my_url)."&client_secret=".$clientSecret."&code=".$code."&grant_type=authorization_code";
    $ch=curl_init();
    curl_setopt($ch,CURLOPT_URL,$token_url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
    curl_setopt($ch,CURLOPT_POST,5);
    curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string)
    $response=curl_exec($ch);
    curl_close($ch);

	

	$auth=json_decode($response);
    }

Ora, se ho in mano il token, posso fare quello che voglio:

	if(isset($auth->{'access_token'}))
{
    // do what you want
    }

Non avete idea di cosa fare? Beh cercate qualche spunto... sulla documentazione.
Il principio di funzionamento è sempre il solito... chiamate cURL e interpretazione della risposta.

Il codice intero

	$clientID='12345';
$clientSecret='abcde';
$code=$_REQUEST["code"];
$my_url="http://www.ilmiosito.com/instagram/handler.php";

	

if(empty($code)) {     $dialog_url="https://api.instagram.com/oauth/authorize/?client_id=".$clientID."&redirect_uri=".urlencode($my_url)."&response_type=code";     header('Location: '.$dialog_url);     die();

if(!empty($code)) {     $token_url="https://api.instagram.com/oauth/access_token";     $fields_string="client_id=".$clientID."&redirect_uri=".urlencode($my_url)."&client_secret=".$clientSecret."&code=".$code."&grant_type=authorization_code";     $ch=curl_init();     curl_setopt($ch,CURLOPT_URL,$token_url);     curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);     curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);     curl_setopt($ch,CURLOPT_POST,5);     curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string)     $response=curl_exec($ch);     curl_close($ch);

    $auth=json_decode($response);     }

	if(isset($auth->{'access_token'}))
{
    // do what you want
    }

PASSO 4: Performance

Fare troppe chiamate alle API può essere lento per il nostro sito, e può sovraccaricare il server di Instagram che, per tutelarsi, se riceve troppe chiamate smette di risponderci.

Ci sono due modi per limitare il numero di chiamate e velocizzare il nostro sito. Io nel sito del Boca Barranca li ho implementati entrambi.

Salvare i dati in locale

Cioè salvare il risultato della chiamata in un database nel proprio server, in modo da leggere i dati in locale senza dover interrogare continuamente il server di Instagram. Cioè voi chiedete una volta ogni tanto i dati a Instagram, tramite lo script visto sopra, vi salvate i risultati e a tutti i vostri utenti potete fornire già i risultati. Poi ogni tanto, tramite cron job o verificando ogni volta la data di ultimo aggiornamento, procedete con un aggiornamento dei dati tramite una nuova richiesta a Instagram.

Questo approccio ha due limiti. Il primo è che può essere usato solo nel caso in cui non forniamo una applicazione user-centered, cioè solo per le operazioni che non richiedono l'autenticazione dell'utente le quali necessitano necessariamente di dati personali diversi da tutti gli altri.
Il secondo è che siamo legati ad un tempo di refresh. Cioè se controlliamo ogni ora la presenza di una nuova foto del Boca Barranca, può darsi che una persona l'abbia scattata alle 21:05 e questa foto non apparirà nel sito fino alle 22:00. In alcuni casi non è un grosso problema, ma nel mio caso mi piacerebbe aggiornare le immagini in tempo reale, così se c'è un concerto nel sito vengono mostrate subito le foto. (poi il Boca è un posto magnifico, c'ha il wi-fi libero e un sacco di gente sempre connessa)
Per queste esigenze hanno inventato il Real-time.

Usare il Realtime

Come ho appena spiegato, il Real-time serve a delegare Instagram dell'aggiornamento delle tue immagini nel sito nel momento in cui qualcosa di nuovo accade.
Per fare questo occorre dire ad Instagram quale genere di evento vogliamo catturare: ad esempio location se vogliamo che aggiorni le nostre foto quando ce ne sono di nuove nella nostra location. Al solito, lo si fa tramite una chiamata cURL alla quale risponderemo con il codice che Instagram ci invia.
Le varie possibilità sono spiegate nella documentazione.

All'inizio del mio file ci metterò questo:

	if(isset($_GET['hub_mode'])&&$_GET['hub_mode']=='subscribe'&&isset($_GET['hub_challenge']))
{
    echo $_GET['hub_challenge'];
    die();
    }

che praticamente, se arriva una richiesta di sottoscrizione al Real-time, lui risponde con il codice di conferma.

La richiesta cURL può essere effettuata attraverso l'API Console, compilando i campi con i valori corretti. Basta farla una volta. Si sceglie, nel mio caso, Subscriptions e poi subscriptions (POST) [Location]. Si preme nell'ingranaggio, si scrive il client_id, il client_secret, si cancella il campo verify_token che non ci serve nel mio caso, poi nel Text si sostituisce {callback URL} con l'URL dello script (lo stesso che prima abbiamo usato su $my_url) e {Location ID you want to subscribe to} lo si sostituisce con l'id della location (che abbiamo trovato prima). Si preme OK. Quindi si preme il tasto POST.

Dovrebbe mostrare il report della chiamata, con la conferma di riuscita o con l'eventuale errore.
Se vi esce l'errore "Unable to reach callback URL" sappiate che questo si verifica sia quando l'indirizzo specificato in $my_url non è corretto e lo script non esiste, sia quando lo script restituisce una pagina vuota, per una qualche ragione. In questo caso, meglio verificarlo di persona andando a controllare col browser che l'URL sia corretto e passandogli delle variabili di test direttamente dall'URL (tanto vuole variabili GET).

Ora, se tutto è andato bene, ogni volta che verrà caricata una nuova foto scattata al Boca, Instagram si occuperà di richiamare il mio script, il quale andrà ad aggiornare il mio database locale di foto facendo l'intera richiesta che abbiamo visto all'inizio.