=== Imparando.net ===

Silex

Silex è un microframework che usa i componenti di Symfony per mettere a disposizione una piattaforma light per la costruzione di siti web dinamici e anche, come si vedrà in questo articolo, per costruire una API Rest.

L'esempio mostrato riguarda la costruzione di una API che espone le funzionalità CRUD (Create, Retrieve, Update, Delete) di un semplice repository di articoli, ognuno dei quali è caratterizzato da titolo, testo e data di creazione.

Installazione di Silex

La procedura qui mostrata si riferisce a Linux, ma con pochi accorgimenti può essere applicata a altre piattaforme.

Come prima cosa conviene creare una nuova cartella che sarà la home del "sito", con le seguenti istruzioni.

cd /var/www/html
                    sudo mkdir blog
                    sudo chown developer:developer blog
In questo caso la root di Apache si trova in /var/www/html, come in molte distribuzioni Linux, e all'interno di questa è stata creata la cartella blog, a cui è stato cambiato il proprietario per renderlo quello dell'utente con cui si sta lavorando, in questo caso developer.

Successivamente bisogna scaricare Composer per gestire e installare le dipendenze necessarie per usare Silex

cd blog
                    sudo curl -sS https://getcomposer.org/installer | sudo php
Composer è un gestore di dipendenze per PHP e permette di installare una libreria che interessa, nel nostro caso Silex, senza preoccuparsi delle dipendenze che si porta dietro, in quanto sarà questo software a verificarle e installarle di conseguenza.

A questo punto è necessario creare un file di configurazione di Composer, composer.json, che al suo interno deve indicare le librerie che ci interessano, nel nostro caso appunto Silex.

{
                        "require": {
                            "silex/silex": "~1.2"
                        }
                    }
Una volta creato il file è sufficiente eseguire il seguente comando all'interno della cartella blog
php composer.phar update
e partirà il download e la successiva creazione di una cartella vendor all'interno della quale verranno scaricate tutte le librerie necessarie.

Per testare se tutto è andato a buon fine è necessario creare un file index.php con all'interno il seguente codice

require_once __DIR__.'/vendor/autoload.php';
                    $app = new Silex\Application();
                    $app['debug'] = true;
                    $app->run();
                    
e verificare nel proprio browser che l'applicazione risponda all'indirizzo http://localhost/blog/

Impostazione di mod_rewrite

Nelle applicazioni web moderne è buona norma avere un front controller, cioè una pagina attraverso la quale passeranno tutte le richieste provenienti dai vari client. Per permettere questo Apache mette a disposizione un modulo, mod_rewrite, che può modificare le richieste in modo che subiscano una riscrittura e appaiano come indirizzate a index.php con una serie di parametri. Per capirci, pensiamo di voler richiedere i dati di un articolo con un certo ID, ad esempio 1, se vogliamo aderire alle convenzioni REST un URL corretto potrebbe essere fatto in questo modo:
http://nome_sito/blog/articles/1
Interpretando la richiesta come quella di una pagina statica Apache cercherebbe un file di nome 1 all'interno della cartella articles, mentre invece grazie alla riscrittura permessa da mod_rewrite, Apache chiamerà il file index.php che quindi intercetterà tutte le chiamate all'applicazione.

Per abilitare la riscrittura bisogna per prima cosa abilitare la possibilità di usare i file .htaccess nella cartella che ospita l'applicazione, aggiungendo alla configurazione di default di Apache le seguenti righe

<Directory /var/www/html/blog>
                                    Options Indexes FollowSymLinks MultiViews
                                    AllowOverride All
                                    Order allow,deny
                                    allow from all
                    </Directory>
usando ad esempio nano
sudo nano /etc/apache2/sites-available/000-default.conf
A questo punto è necessario abilitare mod-rewrite con il seguente comando
sudo a2enmod rewrite
Infine è necessario scrivere il file .htaccess all'interno della cartella blog in questo modo
<IfModule mod_rewrite.c>
                        Options -MultiViews
                    
                        RewriteEngine On
                        RewriteCond %{REQUEST_FILENAME} !-f
                        RewriteRule ^ index.php [QSA,L]
                    </IfModule>
e riavviare Apache
sudo systemctl restart apache2.service

Accesso al database con RedBeanPHP

Per poter far funzionare l'esempio con dei dati verrà utilizzato RedBeanPHP, un ORM dall'utilizzo molto semplice che permetterà di creare dei dati a cui potrà accedere Silex.
Uno dei vantaggi nell'utilizzo di questa libreria è che non richiede nessuna dipendenza, basta includere un unico file, rb.php, e la libreria può essere immediatamente utilizzata. Dopo aver quindi scaricato il file dal loro sito è sufficiente includerlo nel nostro index.php.

include "rb.php";
Le istruzioni fondamentali per poter utilizzare questa libreria sono le seguenti:
  • Aprire la connessione
    R::setup('mysql:host=localhost;dbname=blog',
                        'guest', 'guest');
    In questo esempio viene aperta una connessione verso la macchina locale con il database blog, usando l'utente guest con password guest
  • Creare un "bean", cioè un oggetto che rappresenta un'entità del database
    $article = R::dispense('article');
    In questo esempio si crea un oggetto che rappresenta un articolo: se il database ha già una cartella di nome article, l'oggetto creato avrà come campi le colonne della tabella (e anche altro se è coinvolto in relazioni con altre tabelle), altrimenti sarà un oggetto senza campi, che possono essere aggiunti in seguito e che, quando l'oggetto verrà salvato sul database, creerà una tabella di nome article contenente i campi aggiunti, il cui tipo sarà dedotto dal valore contenuto nei campi stessi.
  • Aggiungere o modificare proprietà
    $article->title = 'Brilliance';
                        $article->text = 'A good book';
    In questo esempio vengono aggiunte o modificate le proprietà title e text dell'oggetto article. Vengono aggiunte se l'oggetto non le ha già tra i propri attributi, altrimenti vengono modificati i valori già presenti. Questo comportamento, voluto dal progettista della libreria, da una parte semplifica la scrittura del codice, dall'altra può portare a dei problemi se viene fatto un errore di scrittura nel nome di una proprietà, in quando andrebbe "silenziosamente" ad aggiungere una nuova proprietà non voluta
  • Salvare un oggetto nel database
    $id = R::store($article);
                        
    In questo esempio un articolo viene salvato nel database e viene ritornato l'id con cui è stato salvato.
  • Recuperare tutti i record di una tabella
    $articles = R::findAll('article');
    In questo esempio tutti gli articoli contenuti nella tabella article vengono inseriti nel vettore di bean $articles
  • Recuperare un articolo di cui si conosce l'id
    $article = R::findOne('article',' id = ? ', [ $id ] );
    Questa modalità è inoltre più sicura rispetto alla tradizionale creazione di una query SQL, in quanto viene usato un preparement statement che fallirà nel caso di un tentativo di SQL injection
  • Trasformare un "bean" in un normale oggetto
    $obj = $article->export();
    Questo è utile perchè nel momento in cui si voglia serializzare un oggetto per passarlo ad esempio come valore di ritorno in formato JSON, basta serializzare l'oggetto esportato

Per creare ora i dati su cui lavorare è sufficiente aggiungere delle righe come le seguenti ad un file PHP e poi mandarlo in esecuzione tramite il CLI

include 'rb.php';
                    R::setup( 'mysql:host=localhost;dbname=blog',
                            'guest', 'guest' );
                    $article = R::dispense( 'article' );
                    $article->title = 'Brilliance';
                    $article->text = 'A good book';
                    $article->creation_date = '2016-04-27';
                    $id = R::store( $article );
                    $article = R::dispense( 'article' );
                    $article->title = 'Pilgrim';
                    $article->text = 'Another good book';
                    $article->creation_date = '2016-04-28';
                    $id = R::store( $article );
Adesso che il database è stato popolato è possibile tornare a Silex per vedere come mostrare i dati tramite una Rest API

Utilizzare Silex

Tornando a Silex, la forma base in cui si può utilizzare prevede che per ogni "rotta" (route in inglese), venga agganciata una risposta, una specie di callback associata all'evento "è stato chiamato questo URL". Per capire basta il seguente esempio.

$app->get('/helloworld', function (Silex\Application $app)  {
                      return "Hello world";
                    });
dove il metodo get indica che la funzione verrà chiamata in risposta a una richiesta di tipo HTTP GET, il primo parametro indica quale richiesta scatenerà la callback e il secondo parametro è la funzione che verrà chiamata, in questo caso restituirà la stringa "Hello world!".

Per fare un esempio più realistico, il codice per ottenere la lista di tutti gli articoli in formato JSON sarà il seguente.

$app->get('/articles', function ()  {
                        $articles_beans = R::findAll('article');
                        foreach ($articles_beans as $article)
                          $articles_list[] = $article->export();
                        return new JsonResponse($articles_list); // Return it as a JSON resource
                    });
                      
Anche in questo caso il metodo usato è il GET, la route è /articles e la funzione, dopo aver estratto tutti i record dalla tabella article del database, popola un vettore con un ciclo e lo restituisce attraverso la creazione di un oggetto JsonResponse fornito anch'esso da Silex.

Il risultato sarà qualcosa di questo tipo

[
                      {"id":"1","title":"Brilliance","text":"A good book","creation_date":"2016-04-27"},
                      {"id":"2","title":"Pilgrim","text":"Another good book","creation_date":"2016-04-28"}
                    ]

In questo altro esempio viene invece creata una route per estrarre un particolare articolo dato il suo id

$app->get('/articles/{id}', function ($id)  {
                        $article = R::findOne( 'article', ' id = ? ', [ $id ] );
                        if ($article == NULL)
                          $app->abort(404, "Article not found");
                        return new JsonResponse($article->export()); 
                    });

Conclusioni

Questo tutorial ha introdotto le idee fondamentali che stanno dietro alla costruzione di una Rest API, usando come strumento per l'accesso al database RedBeanPHP e come parte applicativa Silex. Per ulteriori informazioni e un utilizzo più avanzato di questi due strumenti si rimanda alla documentazione contenuta nei rispettivi siti. Tutti i codici utilizzati possono essere trovati su GitHub