Home Blog Pagina 3

Creare API GraphQL con il Package Laravel-GraphQL

0

Mai sentito parlare di GraphQL? No?

Nessun problema: si tratta di una cosa abbastanza recente, tutto sommato. L’ultimo aggiornamento della specifica, che puoi trovare qui, risale a Ottobre 2016.

GraphCosa?

In poche parole, GraphQL è un linguaggio costruito appositamente per definire delle richieste da mandare ad un’API. Non si tratta di un linguaggio di programmazione, ma di un insieme di regole che definiscono come queste richieste devono essere costruite.

La cosa interessante, di tutta questa faccenda, è che una richiesta GraphQL non specifica soltanto “cosa” chiedere, ma anche “come” chiederlo. Lo so, detta così non è il massimo della chiarezza. Facciamo un esempio, riprendendo quello della specifica.

{
user(id: 4) {
name
}
}

supponiamo di inviare una richiesta GET, ad un’API GraphQL che abbiamo costruito, con il body appena visto. Il risultato dell’operazione sarà…

{
"user": {
"name": "Mark Zuckerberg"
}
}

Ok, ma cosa significa? Un passo alla volta.

Innanzitutto, il segmento user(id: 4) specifica che vogliamo i dati di un utente. Più precisamente, quello il cui valore del campo id è 4. Dopo questa dichiarazione, si apre un blocco in cui appare un solo elemento: name. Questo elemento è la proprietà che stiamo chiedendo all’API, e che vogliamo ci venga restituita.

In poche parole, stiamo chiedendo alla nostra API di restituirci il nome dell’utente 4: “Mark Zuckerberg” è il risultato, all’interno di un oggetto “user” opportunamente creato.

Certo, vista così la cosa è riduttiva. Tuttavia, GraphQL è fondato su principi ben specifici:

  • Gerarchia: GraphQL ha come obiettivo il dare l’importanza alla gerarchia degli elementi. Questo perchè oggi, effettivamente, lo sviluppo di un prodotto prevede la costruzione di view “legate” tra loro da specifiche relazioni, in modo tale da favorirne modularità e riutilizzo. Come paradigma, quindi, GraphQL vuole facilitare il più possibile questo approccio;
  • Incentrato sul Prodotto: essendo molto legato a ciò che il frontend di un’applicazione può richiedere, GraphQL concettualmente è molto legato alla visione del prodotto che si vuole costruire;
  • Tipizzato: lavorare con le query GraphQL significa definire ogni singola query che si vuole usare a livello applicativo. Si tratta di darle una caratterizzazione ben specifica, e questo giova alla qualità del codice, visto che solo le query che hanno una definizione verranno riconosciute come valide;
  • Query Specifiche per il Client: una volta definita la query, le possibilità d’uso sono molteplici e la flessibilità tanta. Una delle caratteristiche più interessanti e peculiari di GraphQL consiste nel poter scegliere cosa il server deve farci ritornarci o no;
  • Introspettività: un altro aspetto interessante di GraphQL. Un server GraphQL non deve solo permettermi di effettuare una certa query, ma anche di sapere quali query posso effettuare oppure no. Un gran bel vantaggio per i client!

Se è la prima volta che ci avviciniamo a GraphQL, probabilmente avremo capito molto poco di questo elenco. Non è un problema, comunque, perchè capiremo meglio a breve. Prima di proseguire, però, vediamo dal vivo un esempio che dovrebbe risultarci quanto meno familiare: le API di Facebook. Andando sulla pagina del Graph API Explorer possiamo infatti fare qualsiasi richiesta vogliamo ai server di Facebook, e controllarne i risultati.

Di default l’interfaccia dovrebbe mostrare la richiesta me?fields=id,name. Clicchiamo su “Submit” per vedere il risultato. Se abbiamo un account Facebook dovremmo poter vedere il nostro id ed il nostro nome.

Nel mio caso:

{
"id": "10203905429385894",
"name": "Francesco Malatesta"
}

Proviamo adesso a cambiare richiesta, aggiungendo il campo gender alla lista di fields richiesti, modificando la stringa della nostra query in me?fields=id,name,gender. Inviamo la richiesta e, sempre nel mio caso…

{
"id": "10203905429385894",
"name": "Francesco Malatesta",
"gender": "male"
}

Se ci facciamo caso, l’endpoint in realtà è lo stesso. Quello che è cambiato è l’insieme dei field richiesti all’endpoint. Questo ci collega al punto 4 dell’elenco dei principi visti prima. Con lo stesso endpoint posso costruire richieste specifiche in base alle mie esigenze, senza “sprechi” di campi richiesti e magari rimasti inutilizzati. Con beneficio dell’intero sistema!

Facciamo un altro test, adesso. Modifichiamo nuovamente la nostra richiesta in me?fields=id,name,permissions{permission}. Stavolta stiamo chiedendo, all’endpoint, di recuperare non soltanto l’id ed il nome dell’utente, ma anche i nomi dei singoli permessi. La notazione permissions{permission} più precisamente indica che vogliamo anche un array di oggetti permissions e dentro ogni oggetto una proprietà permission, che in questo caso corrisponde al nome del permesso.

Il risultato? Eccolo:

{
"id": "10203905429385894",
"name": "Francesco Malatesta",
"permissions": {
"data": [
{
"permission": "user_managed_groups"
},
{
"permission": "email"
},
...
]
}
}

Ecco, questa è la “gerarchia” del primo elemento della lista vista prima. La flessibilità vista poco fa vale anche in questo caso. Prova a modificare di nuovo l’URL della richiesta in me?fields=id,name,permissions{permission,status}, e guarda tu stesso il risultato!

GraphQL e Laravel

Siamo su Laravel-Italia, e come giusto che sia una domanda starà prendendo forma nella nostra testa…

Bene, ma posso costruire anche io delle API del genere con Laravel? Come?

Ovviamente si, è possibile costruirle con Laravel: c’è un package appositamente concepito per lo scopo, Folkloreatelier/laravel-graphql, che oggi scopriremo insieme!

D’altronde, come anche la specifica spiega, GraphQL non è un linguaggio di programmazione… semplicemente un linguaggio! Un insieme di regole, appunto. Quello di cui abbiamo bisogno è “qualcosa” che sia capace di interpretare una richiesta scritta seguendo queste regole… tutto qui!

Che poi è esattamente quello che ci permetterà di fare, a breve, il package in questione.

Installazione

La prima cosa da fare ovviamente è aggiungere il package folklore/graphql al nostro file composer.json, con un

$ composer require folklore/graphql

Una volta finita l’installazione, quindi, aggiungiamo

FolkloreGraphQLServiceProvider::class,

all’elenco dei service provider, nel file config/app.php. Occupiamoci quindi di registrare la Facade GraphQL nello stesso file.

'GraphQL' => 'FolkloreGraphQLSupportFacadesGraphQL',

Infine, pubblichiamo il file di configurazione, config/graphql.php tramite

$ php artisan vendor:publish --provider="FolkloreGraphQLServiceProvider"

Fatto!

Nota: un altro po’ di informazioni interessanti su GraphQL e PHP possono essere trovate sul readme file del repository GitHub di webonyx/graphql-php, di cui il package che abbiamo installato ne è “ponte” con Laravel.

Schema, Query e Mutation

Per lavorare con GraphQL ci sono due concetti fondamentali da conoscere e tenere a mente: la query e la mutation. Come forse il nome già suggerisce, la query è una richiesta che noi inviamo al server per leggere dei dati. D’altra parte, la mutation è una richiesta che ha l’obiettivo di manipolare dei dati.

Niente di complesso insomma: si tratta più che altro di un modo di ragionare specifico al quale non siamo abituati.

Vediamo insieme innanzitutto come costruire una Query per poter ottenere dei dati dal nostro sistema. Dopodichè, passiamo dall’altra parte e analizziamo cosa significa costruire una mutation… e come si fa.

Lo Schema

La lettura di dati, con GraphQL, si fa attraverso una Query. Come già detto in precedenza, una query serve a specificare cosa stiamo richiedendo, e quali campi di quella “cosa” stiamo richiedendo. Inventiamoci un esempio, riprendendo quello visto prima.

Abbiamo questa query:

{
user(id: 4) {
name
}
}

che ha l’obiettivo di ottenere l’utente il cui ID è 4, includendo nell’oggetto ottenuto un campo “name” il cui valore è, appunto, il nome dell’utente. Abbiamo anche detto, prima, che abbiamo la possibilità di modificare questa query in base alla richiesta, senza troppi problemi. Ad esempio,

{
user(id: 4) {
name,
age
}
}

potrebbe essere una richiesta perfettamente valida. La domanda a questo punto è una: come si fa a definire quali campi possono essere richiesti, e quali no? Cosa rende una query valida, e cosa no?

La soluzione è definire, prima di tutto, uno Schema per la Query che vogliamo creare. Lo possiamo fare creando una nuova classe che estende FolkloreGraphQLSupportType. L’esempio presente sul file readme è perfetto: creiamo un file app/GraphQL/Type/UserType.php.

namespace AppGraphQLType;

use GraphQLTypeDefinitionType;
use FolkloreGraphQLSupportType as GraphQLType;

class UserType extends GraphQLType {

protected $attributes = [
'name' => 'User',
'description' => 'A user'
];

public function fields()
{
return [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the user'
],
'email' => [
'type' => Type::string(),
'description' => 'The email of user'
]
];
}
}

Il nostro type adesso deve essere registrato. Lo si può fare aggiungendolo nell’array apposito nel file di configurazione config/graphql.php, in types:

'types' => [
AppGraphQLTypeUserType::class
],

Bene, ci siamo! Il type è stato definito. Tocca alla query!

La Query

Lo step successivo è costruire una query che faccia uso dello specifico type da noi creato poco fa. Ogni query verrà implementata in una classe specifica, estensione di FolkloreGraphQLSupportQuery.

Ecco la nostra UserQuery.

namespace AppGraphQLQuery;

use GraphQL;
use GraphQLTypeDefinitionType;
use FolkloreGraphQLSupportQuery;
use AppUser;

class UsersQuery extends Query {

protected $attributes = [
'name' => 'users'
];

public function type()
{
return Type::listOf(GraphQL::type('User'));
}

public function args()
{
return [
'id' => ['name' => 'id', 'type' => Type::string()],
'email' => ['name' => 'email', 'type' => Type::string()]
];
}

public function resolve($root, $args)
{
if(isset($args['id']))
{
return User::where('id' , $args['id'])->get();
}
else if(isset($args['email']))
{
return User::where('email', $args['email'])->get();
}
else
{
return User::all();
}
}

}

Qui c’è un po’ più di roba da analizzare. Facciamo un passo alla volta. Iniziamo da…

public function type()
{
return Type::listOf(GraphQL::type('User'));
}

Questo metodo si occupa di indicare che type stiamo usando. Abbiamo uno UserType, che identificheremo qui come User. Subito dopo troviamo altri due metodi, args e resolve. Servono a definire tutto quello che riguarda il passaggio di parametri alla query. In questo caso specifico, stiamo implementando la possibilità di ottenere i dati di uno specifico utente partendo da un id, o da un indirizzo email.

Insomma, grazie a questi due metodi, richieste come

{
user(id: 4) {
name
}
}

o

{
user(email: "ciccio@email.com") {
name
}
}

sono perfettamente valide.

Tornando a noi, non rimane altro che registrare la query creata, esattamente come fatto per il type. Stavolta l’elemento dovrà essere aggiunto sotto schemas, sempre nel file di configurazione config/graphql.php:

'schemas' => [
'default' => [
'query' => [
'users' => AppGraphQLQueryUsersQuery::class
],
// ...
]
]

Bene. Anche questa è fatta: prendiamoci una pausa per fare qualche test.

Time for Test!

Se abbiamo implementato tutto correttamente, dovremmo già essere in grado di testare il nostro codice. Come? Con una semplice chiamata: prima, però, ho aggiunto al volo un utente di prova sul database, tramite il DatabaseSeeder.

/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// $this->call(UsersTableSeeder::class);

$user = new AppUser();
$user->name = 'Francesco';
$user->email = 'francesco@email.com';
$user->password = Hash::make('123456');
$user->save();
}

Bene, possiamo finalmente provare. Puntiamo a questo URL:

`http://graph.dev/graphql?query=query+FetchUsers{users{email}}`

dove graph.dev ovviamente è l’hostname scelto per la macchina dove abbiamo installato il progetto. La route /graphql è quella che ci permette di accedere alla nostra API GraphQL. L’URI è definito nel file di configurazione config/graphql.php, alla voce prefix.

Ed ecco il risultato!

{
"data": {
"users": [
{
"email": "francesco@email.com"
}
]
}
}

Proviamo ora a chiamare l’URL

`http://graph.dev/graphql?query=query+FetchUsers{users{id,email}}`

Ecco quale sarà il risultato.

{
"data": {
"users": [
{
"id": "1",
"email": "francesco@email.com"
}
]
}
}

La bellezza delle GraphQL API! Ottenere esattamente ciò che si è chiesto: nulla di più, nulla di meno, con tutta la flessibilità che ne deriva.

Possiamo fare anche altre prove: controlliamo il risultato ottenuto se chiamando

http://graph.dev/graphql?query=query+FetchUsers{users(id:"1"){email}}

o magari…

http://graph.dev/graphql?query=query+FetchUsers{users(id:"2"){email}}

Prima di andare avanti, possiamo fare un esercizio per capire meglio il meccanismo.

Proviamo a raggiungere il seguente URL:

`http://graph.dev/graphql?query=query+FetchUsers{users{id,name,email}}`

Otterremo un errore, perchè il campo name non esiste. Certo, in realtà esiste ma… non l’abbiamo definito nello UserType! Scopriamo come fare e… rendiamo fattibile anche questa query.

La Mutation

Che cos’è una mutation? Come già detto in precedenza, quando vogliamo leggere dei dati dal nostro sistema possiamo usare le query. Fin qui, tutto regolare. Quando si tratta però di modificare dei dati, le mutation sono lo strumento a cui dobbiamo ricorrere.

Ad ogni modo, la mutation non è niente di più che una query che effettua delle modifiche e può tornare dei dati. La possibilità di ritornare dei dati è molto importante: immaginiamo di avere una mutation che crea un utente. Potrebbe essere comodo fare in modo che ritorni l’id del record appena creato, o magari anche l’indirizzo email per poter essere visualizzato in una view apposita.

Nel contesto del nostro package, creare una mutation significa creare una classe che estende la classe base FolkloreGraphQLSupportMutation. Proviamone a creare una il cui scopo è modificare il nome dell’utente: ne inseriremo il codice nel file app/GraphQL/Mutation/UpdateUserNameMutation:

namespace AppGraphQLMutation;

use GraphQL;
use GraphQLTypeDefinitionType;
use FolkloreGraphQLSupportMutation;
use AppUser;

class UpdateUserNameMutation extends Mutation
{
protected $attributes = [
'name' => 'updateUserName'
];

public function type()
{
return GraphQL::type('User');
}

public function args()
{
return [
'id' => ['name' => 'id', 'type' => Type::nonNull(Type::string())],
'name' => ['name' => 'name', 'type' => Type::nonNull(Type::string())]
];
}

public function resolve($root, $args)
{
$user = User::find($args['id']);
if(!$user)
{
return null;
}

$user->name = $args['name'];
$user->save();

return $user;
}
}

Vediamo, nel dettaglio, cosa succede.

Come già fatto in precedenza, tramite il metodo type stiamo assegnando un type specifico all’operazione. Con il metodo args, invece, stiamo definendo i due argomenti che verranno passati alla mutation per effettuare l’operazione. L’obiettivo è cambiare il nome dell’utente il cui id è quello specificato, quindi gli argomenti da passare saranno:

  • l’id dell’utente;
  • il nuovo nome scelto;

A questo punto, il metodo resolve si occupa di effettuare l’operazione di cui abbiamo bisogno. Se tutto va a buon fine, viene ritornata l’istanza dell’oggetto modificato.

Direi che ci siamo: possiamo provare subito la nostra mutation. Nel browser, puntiamo all’indirizzo

`http://graph.dev/graphql?query=mutation+users{updateUserName(id:%221%22,name:%22Ciccio%22){id,name,email}}`

ed aspettiamone il risultato, che sarà…

{
"data": {
"updateUserName": {
"id": "1",
"name": "Ciccio",
"email": "francesco@email.com"
}
}
}

Perfetto! Il nome del nostro utente è stato modificato: da Francesco a Ciccio! Come già anticipato prima, nella richiesta abbiamo anche specificato quali campi vedere in output… comodo no?

Bonus: GraphiQL

Vale la pena menzionare un tool molto comodo, che ci sarà sicuramente d’aiuto in fase di sviluppo. GraphiQL!

Eccolo, in tutto il suo splendore.

GraphiQL permette di provare al volo, sul proprio server, le query e le mutation che abbiamo costruito, per assicurarci che funzionino correttamente. Ancora più interessante è la possibilità, cliccando su “Docs” in alto a destra, di accedere all’elenco di tutte le query e mutation che abbiamo definito per i nostri type.

In poche parole, un tool completo, un must have se si vuole lavorare con GraphQL.

E la buona notizia è che il tool è già installato con il package che stiamo usando! Tutto quello che devi fare per usarlo è raggiungerlo all’indirizzo

`http://graph.dev/graphiql`

e nulla di più.

Conclusioni

Siamo giunti alla fine di questo articolo dedicato a GraphQL. Ovviamente, l’argomento è vasto e questa non è stata che una semplicissima introduzione. Ci sono tantissime risorse utilissime, nel caso si voglia approfondire: prima tra tutte http://graphql.org/, dove è possibile trovare tutto, dalla documentazione a tantissimi esempi.

Nel caso in cui dovessi decidere di costruire delle GraphQL API, vienicelo a raccontare sullo Slack di Laravel-Italia, ti aspettiamo!

I Package della Settimana – N.20 – 19 Dicembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Laravel Broadway: mai sentito parlare di Broadway? Beh, è un signor package che consente, nel modo più leggero e meno impattante possibile, di farti implementare un’applicazione CQRS ed Event Sourced. Laravel Broadway è un ponte tra i due, che ti può aiutare a creare un’applicazione migliore usando il tuo framework preferito. Provalo!
  • Make User Command: abbiamo bisogno di creare al volo un utente con cui fare delle prove? Nessun problema: ci pensa il Make User Command! Come il nome facilmente suggerisce, si tratta di un comando artisan da usare per costruire al volo degli utenti e salvarli sul database;
  • Amazon ECS for Laravel: questo package di Joe Dawson mette a disposizione dello sviluppatore una comodissima API per fare ricerche su Amazon, o magari per controllare uno specifico prodotto tramite il suo ASIN, l’ID univoco di un prodotto Amazon. Sicuramente degno di nota!
  • Laravel GraphQL: Laravel GraphQL consente di implementare GraphQL nella nostra applicazione Laravel, consentendoci di costruire degli endpoint più “intelligenti” che ritornano soltanto quanto viene richiesto, e non di più. Un po’ come avviene, ad esempio, per le API di Facebook;
  • Config Writer: altro package interessante, Config Writer mette a disposizione dello sviluppatore un servizio che può scrivere sui file di configurazione, modificandone i valori a runtime. Da usare molto, molto attentamente… ma può tornare decisamente utile in alcuni casi!

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

“I Package della Settimana” torna dopo capodanno. Nel frattempo, Laravel-Italia augura a tutti i suoi utenti buon Natale, e buon anno nuovo!

Creare l'ambiente di lavoro per il prossimo progetto Laravel con LaraPrep!

0

Se usi spesso Laravel nei tuoi progetti, molto spesso ti sarai ritrovato a sperimentare questo o quel package per capire se risponde alle tue necessità.

In ogni caso, una cosa è certa: di volta in volta hai dovuto ricostruire l’ambiente di lavoro. Certo, con Laravel è roba di pochi minuti, basta mettere su una macchina Vagrant, configurarla e partire. Tuttavia, si tratta pur sempre di un qualcosa di automatizzabile: perchè non farlo?

Così ho buttato giù al volo uno script bash, LaraPrep, che fa proprio questo: preparare un ambiente di lavoro in pochi minuti, con una singola istruzione.

Più precisamente, questa:

`./laraprep.sh test’

Cosa?

Una volta eseguito il comando…

  • viene scaricato Homestead Improved nella cartella “test”;
  • viene generato casualmente un IP (che non esiste nel file hosts) e lo associa all’hostname “test.dev”, salvando il record nel file hosts;
  • viene modificato il file Homestead.yml opportunamente;
  • viene avviata la VM;
  • entra tramite “vagrant ssh” e crea un progetto Laravel fresco fresco e pronto all’uso;

Tutto con un solo comando: più comodo di così!

Buon divertimento!

Laravel Cafè #6 – Una Cache Performante

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Nei giorni scorsi ho fatto una mia personale pausa caffè leggendo questo articolo il quale mi ha dato diversi spunti interessanti per questo Laravel Cafè. Oggi metteremo alla prova Laravel, cercando di capire come si comportano i diversi driver per la Cache messi a disposizione dal framework. Salto l’introduzione sulla configurazione e utilizzo della Cache rimandandoti direttamente alla documentazione ufficiale per concentrarmi sull’ambiente utilizzato, il test e i suoi risultati.

A differenza del test che effettua l’autore dell’articolo originale, ho utilizzato in primo luogo una macchina virtuale molto simile ad un ambiente di produzione che può essere messo a disposizone da un qualunque provider VPS e la base dati usata è decisamente più corposa rispetto all’originale.

Ambiente

Partiamo dal dietro le quinte del dietro le quinte: una macchina virtuale su cui è installato Ubuntu Server e di seguito sono elencati i dettagli della macchina. Un piccolo appunto, la vm è collegata all’interno di una rete locale con il mio computer, quindi tutti i possibili ritardi dovuti alla rete dovrebbero essere assolutamente irrisori, magari in un futurò berremo un caffè eseguendo i test su una macchina remota!

Software di virtualizzazione: Oracle VM Virtualbox

  • Macchina virtuale linux – Ubuntu 16.04.1 LTS x64
  • Hard disk: hd vdi da 20GB
  • Timing cached reads: 38426 MB in 2.00 seconds = 19242.88 MB/sec
  • Timing buffered disk reads: 1216 MB in 3.00 seconds = 405.21 MB/sec
  • CPU: 1 CPU
  • RAM: 4000 MB

Software aggiuntivi

Per quanto riguarda l’applicazione in Laravel che elaborerà il test, ho configurato un’installazione standard di Laravel 5.3, con l’aggiunta dei packages strettamente necessari al test.

Il Codice

Prima di vedere il dettaglio del codice dell’applicazione di test vorrei fare una breve premessa. Anche se l’articolo originale utilizza postman per effettuare i test, io ho preferito farmi uno script molto semplice che utilizza curl per due motivi:
1. curl usato nativamente lo ritengo più preciso di Postman.
2. Sopra i 100.000 risultati Postman va in crash probabilmente a causa del peso del json in response.

Il test effettuato si basa solo sul tempo di risposta dell’applicazione dal momento in cui viene effettuata la chiamata http al momento in cui viene ricevuto interamente il response. Come anticipato è utilizzata una base di dati fake sia per richiamare il metodo Cache::get(...) che per il metodo Cache::tags(...)->get(...); (quest’ultimo solo per Memcached e Redis dato che sono gli unici driver a supportare i tag). La tabella utilizzata per il test contiene un milione di record e da PHPMyAdmin risulta pesare 1.1GB.

Vediamo quindi la parte di codice implementato. Ho creato un modello Articles e corrispettiva migrazione:

// Migrazione
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->string('title', 100);
$table->string('slug', 100);
$table->text('body');
$table->timestamps();
});

// Model
namespace App;

use IlluminateDatabaseEloquentModel;

class Article extends Model
{

/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['title', 'slug', 'body'];

}

Per popolare i dati fake ho utilizzato una model factory e relativo seeder. Quest’ultimo è stato limitato alla creazione di 100.000 record alla volta e nonostante abbia provato diverse strade, questo è un limite che non sono riuscito a superare senza che il processo andasse in crash:

// database/factories/ModelFactory.php
$factory->define(AppArticle::class, function (FakerGenerator $faker) {
return [
'title' => $faker->realText($faker->numberBetween(50, 100)),
'slug' => $faker->slug,
'body' => $faker->realText(10000),
];
});

// database/seeds/ArticlesTableSeeder
use IlluminateDatabaseSeeder;

class ArticlesTableSeeder extends Seeder
{

/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
factory(AppArticle::class, 100000)->create();
}

}

Per semplicità e velocità non ho creato alcun controller, ma delle route con relativa callback per caricare i dati dal database e dalla cache (con e senza tag) e restituire un response in JSON. Come si nota dal codice, ciascuna route ha un parametro count che determina il range di valori da caricare.

Route::get('/test/{count}/db', function ($count) {
$articles = AppArticle::limit($count)->get();
return response()->json($articles);
});

Route::get('/test/{count}/cache', function ($count) {
$articles = Cache::get('articles_' . $count, function() use ($count) {
return AppArticle::limit($count)->get();
});

return response()->json($articles);
});

Route::get('/test/{count}/tags', function ($count) {
$articles = Cache::tags(['articles'])->get('articles_' . $count, function() use ($count) {
return AppArticle::limit($count)->get();
});

return response()->json($articles);
});

curl

Passiamo quindi ai risultati ricordandoci che per i driver si parte sempre con la cache vuota e di conseguenza la prima chiamata recuperare i dati e successivamente popola la cache. La struttura del test è ad ogni modo molto semplice:
– 5 chiamate http da 10, 100, 1000, 10.000, 100.000 e 1.000.000 di risultati che restituiscono i dati dal database
– 5 chiamate http per ciascun driver da 10, 100, 1000, 10.000, 100.000 e 1.000.000 di risultati che restituiscono i dati contenuti nella cache

TEST 1: recupero dati da database
* 1° chiamata
* 10: 62 ms
* 100: 26 ms
* 1000: 57 ms
* 10000: 938 ms
* 100000: 5104 ms
* 100000: 12717 ms

  • 2° chiamata
  • 10: 61 ms
  • 100: 28 ms
  • 1000: 56 ms
  • 10000: 1025 ms
  • 100000: 4690 ms
  • 100000: 12586 ms

  • 3° chiamata

  • 10: 15 ms
  • 100: 31 ms
  • 1000: 47 ms
  • 10000: 933 ms
  • 100000: 4690 ms
  • 100000: 14601 ms

  • 4° chiamata

  • 10: 11 ms
  • 100: 28 ms
  • 1000: 49 ms
  • 10000: 700 ms
  • 100000: 4709 ms
  • 100000: 13327 ms

  • 5° chiamata

  • 10: 15 ms
  • 100: 27 ms
  • 1000: 130 ms
  • 10000: 481 ms
  • 100000: 5081 ms
  • 100000: 18040 ms

media
* 10: 32,8 ms
* 100: 28 ms
* 1000: 67,8 ms
* 10000: 815,4 ms
* 100000: 4854,8 ms
* 1000000: 14254,2 ms


  • 1° chiamata
  • 10: 317 ms
  • 100: 202 ms
  • 1000: 181 ms
  • 10000: 1827 ms
  • 100000: 13057 ms
  • 100000: 16193 ms
  • 2° chiamata

  • 10: 87 ms
  • 100: 194 ms
  • 1000: 168 ms
  • 10000: 1505 ms
  • 100000: 10531 ms
  • 100000: 14374 ms

  • 3° chiamata

  • 10: 70 ms
  • 100: 94 ms
  • 1000: 167 ms
  • 10000: 1043 ms
  • 100000: 10561 ms
  • 100000: 14834 ms

  • 4° chiamata

  • 10: 63 ms
  • 100: 45 ms
  • 1000: 169 ms
  • 10000: 1414 ms
  • 100000: 11587 ms
  • 100000: 15879 ms

  • 5° chiamata

  • 10: 107 ms
  • 100: 32 ms
  • 1000: 167 ms
  • 10000: 1339 ms
  • 100000: 12080 ms
  • 100000: 16678 ms

media
* 10: 128,8 ms
* 100: 113,4 ms
* 1000: 170,4 ms
* 10000: 1425,6 ms
* 100000: 11563,2 ms
* 1000000: 15591,6 ms

TEST 2: file driver
* 1° chiamata
* 10: 48 ms
* 100: 25 ms
* 1000: 48 ms
* 10000: 416 ms
* 100000: 4449 ms
* 100000: 10622 ms

  • 2° chiamata
  • 10: 8 ms
  • 100: 10 ms
  • 1000: 41 ms
  • 10000: 417 ms
  • 100000: 4960 ms
  • 100000: 13674 ms
  • 3° chiamata

  • 10: 12 ms
  • 100: 17 ms
  • 1000: 46 ms
  • 10000: 413 ms
  • 100000: 4676 ms
  • 100000: 12682 ms

  • 4° chiamata

  • 10: 9 ms
  • 100: 15 ms
  • 1000: 43 ms
  • 10000: 435 ms
  • 100000: 4722 ms
  • 100000: 14586 ms

  • 5° chiamata

  • 10: 13 ms
  • 100: 8 ms
  • 1000: 41 ms
  • 10000: 416 ms
  • 100000: 4727 ms
  • 100000: 21205 ms

media
* 10: 18 ms
* 100: 15 ms
* 1000: 43,8 ms
* 10000: 419,4 ms
* 100000: 4706,8 ms
* 1000000: 14553,8 ms


  • 1° chiamata
  • 10: 396 ms
  • 100: 343 ms
  • 1000: 222 ms
  • 10000: 1568 ms
  • 100000: 15365 ms
  • 100000: 16403 ms
  • 2° chiamata

  • 10: 228 ms
  • 100: 204 ms
  • 1000: 234 ms
  • 10000: 1432 ms
  • 100000: 11894 ms
  • 100000: 16597 ms

  • 3° chiamata

  • 10: 193 ms
  • 100: 58 ms
  • 1000: 136 ms
  • 10000: 1745 ms
  • 100000: 11666 ms
  • 100000: 15965 ms

  • 4° chiamata

  • 10: 117 ms
  • 100: 22 ms
  • 1000: 219 ms
  • 10000: 1483 ms
  • 100000: 10461 ms
  • 100000: 15386 ms

  • 5° chiamata

  • 10: 400 ms
  • 100: 28 ms
  • 1000: 144 ms
  • 10000: 1696 ms
  • 100000: 10414 ms
  • 100000: 16459 ms

media
* 10: 266,8 ms
* 100: 131 ms
* 1000: 191 ms
* 10000: 1584,8 ms
* 100000: 11960 ms
* 1000000: 16162 ms

TEST 3: array driver
* 1° chiamata
* 10: 315 ms
* 100: 262 ms
* 1000: 433 ms
* 10000: 8109 ms
* 100000: 4449 ms
* 100000: memoria esaurita

  • 2° chiamata
  • 10: 45 ms
  • 100: 50 ms
  • 1000: 1143 ms
  • 10000: 7777 ms
  • 100000: 4960 ms
  • 100000: memoria esaurita
  • 3° chiamata

  • 10: 43 ms
  • 100: 43 ms
  • 1000: 1089 ms
  • 10000: 4770 ms
  • 100000: 4676 ms
  • 100000: memoria esaurita

  • 4° chiamata

  • 10: 34 ms
  • 100: 33 ms
  • 1000: 986 ms
  • 10000: 4745 ms
  • 100000: 4722 ms
  • 100000: memoria esaurita

  • 5° chiamata

  • 10: 33 ms
  • 100: 27 ms
  • 1000: 927 ms
  • 10000: 4861 ms
  • 100000: 4727 ms
  • 100000: memoria esaurita

media
* 10: 94 ms
* 100: 83 ms
* 1000: 915,6 ms
* 10000: 6052,4 ms
* 100000: 4706,8 ms
* 1000000: —


  • 1° chiamata
  • 10: 154 ms
  • 100: 180 ms
  • 1000: 701 ms
  • 10000: 3987 ms
  • 100000: 13867 ms
  • 100000: memoria esaurita
  • 2° chiamata

  • 10: 68 ms
  • 100: 32 ms
  • 1000: 128 ms
  • 10000: 1648 ms
  • 100000: 10810 ms
  • 100000: memoria esaurita

  • 3° chiamata

  • 10: 52 ms
  • 100: 35 ms
  • 1000: 222 ms
  • 10000: 1041 ms
  • 100000: 11431 ms
  • 100000: memoria esaurita

  • 4° chiamata

  • 10: 87 ms
  • 100: 29 ms
  • 1000: 229 ms
  • 10000: 1587 ms
  • 100000: 19202 ms
  • 100000: memoria esaurita

  • 5° chiamata

  • 10: 54 ms
  • 100: 41 ms
  • 1000: 206 ms
  • 10000: 1637 ms
  • 100000: 11698 ms
  • 100000: memoria esaurita

media
* 10: 83 ms
* 100: 63,4 ms
* 1000: 297,2 ms
* 10000: 1980 ms
* 100000: 13401,6 ms
* 1000000: —

TEST 4: database driver
* 1° chiamata
* 10: 92 ms
* 100: 19 ms
* 1000: 56 ms
* 10000: 412 ms
* 100000: 4696 ms
* 100000: memoria esaurita

  • 2° chiamata
  • 10: 17 ms
  • 100: 17 ms
  • 1000: 58 ms
  • 10000: 428 ms
  • 100000: 4107 ms
  • 100000: memoria esaurita
  • 3° chiamata

  • 10: 21 ms
  • 100: 18 ms
  • 1000: 60 ms
  • 10000: 414 ms
  • 100000: 4687 ms
  • 100000: memoria esaurita

  • 4° chiamata

  • 10: 20 ms
  • 100: 20 ms
  • 1000: 59 ms
  • 10000: 416 ms
  • 100000: 4707 ms
  • 100000: memoria esaurita

  • 5° chiamata

  • 10: 22 ms
  • 100: 18 ms
  • 1000: 58 ms
  • 10000: 424 ms
  • 100000: 4730 ms
  • 100000: memoria esaurita

media
* 10: 34,4 ms
* 100: 18,4 ms
* 1000: 58,2 ms
* 10000: 418,8 ms
* 100000: 4585,4 ms
* 1000000: —


  • 1° chiamata
  • 10: 35 ms
  • 100: 38 ms
  • 1000: 149 ms
  • 10000: 2164 ms
  • 100000: 13251 ms
  • 100000: memoria esaurita
  • 2° chiamata

  • 10: 22 ms
  • 100: 26 ms
  • 1000: 198 ms
  • 10000: 1130 ms
  • 100000: 11363 ms
  • 100000: memoria esaurita

  • 3° chiamata

  • 10: 21 ms
  • 100: 38 ms
  • 1000: 113 ms
  • 10000: 1386 ms
  • 100000: 10897 ms
  • 100000: memoria esaurita ms

  • 4° chiamata

  • 10: 27 ms
  • 100: 33 ms
  • 1000: 201 ms
  • 10000: 1035 ms
  • 100000: 10349 ms
  • 100000: memoria esaurita

  • 5° chiamata

  • 10: 21 ms
  • 100: 39 ms
  • 1000: 115 ms
  • 10000: 1585 ms
  • 100000: 10326 ms
  • 100000: memoria esaurita

media
* 10: 25,2 ms
* 100: 34,8 ms
* 1000: 155,2 ms
* 10000: 1460 ms ms
* 100000: 11237,2 ms
* 1000000: —

TEST 5a: memcached driver
* 1° chiamata
* 10: 78 ms
* 100: 17 ms
* 1000: 55 ms
* 10000: 422 ms
* 100000: 4703 ms
* 100000: 8257 ms

  • 2° chiamata
  • 10: 18 ms
  • 100: 18 ms
  • 1000: 57 ms
  • 10000: 420 ms
  • 100000: 4668 ms
  • 100000: 9378 ms
  • 3° chiamata

  • 10: 19 ms
  • 100: 23 ms
  • 1000: 57 ms
  • 10000: 413 ms
  • 100000: 4691 ms
  • 100000: 9270 ms

  • 4° chiamata

  • 10: 20 ms
  • 100: 19 ms
  • 1000: 54 ms
  • 10000: 423 ms
  • 100000: 4713 ms
  • 100000: 8522 ms

  • 5° chiamata

  • 10: 9 ms
  • 100: 21 ms
  • 1000: 57 ms
  • 10000: 425 ms
  • 100000: 4720 ms
  • 100000: 8691 ms

media
* 10: 28,8 ms
* 100: 19,6 ms
* 1000: 56 ms
* 10000: 420,6 ms
* 100000: 4699 ms
* 1000000: 8823,6 ms


  • 1° chiamata
  • 10: 350 ms
  • 100: 350 ms
  • 1000: 168 ms
  • 10000: 1489 ms
  • 100000: 13345 ms
  • 100000: 10360 ms
  • 2° chiamata

  • 10: 120 ms
  • 100: 287 ms
  • 1000: 162 ms
  • 10000: 1555 ms
  • 100000: 14447 ms
  • 100000: 10391 ms

  • 3° chiamata

  • 10: 95 ms
  • 100: 55 ms
  • 1000: 173 ms
  • 10000: 1469 ms
  • 100000: 13944 ms
  • 100000: 11183 ms

  • 4° chiamata

  • 10: 102 ms
  • 100: 37 ms
  • 1000: 189 ms
  • 10000: 1498 ms
  • 100000: 9950 ms
  • 100000: 11584 ms

  • 5° chiamata

  • 10: 109 ms
  • 100: 37 ms
  • 1000: 172 ms
  • 10000: 1478 ms
  • 100000: 10378 ms
  • 100000: 11608 ms

media
* 10: 155,2 ms
* 100: 153,2 ms
* 1000: 172,8 ms
* 10000: 1497,8 ms
* 100000: 12412,8 ms
* 1000000: 11025,2 ms

TEST 5b: memcached driver con tags
* 1° chiamata
* 10: 76 ms
* 100: 27 ms
* 1000: 111 ms
* 10000: 1510 ms
* 100000: 10306 ms
* 100000: 10304 ms

  • 2° chiamata
  • 10: 56 ms
  • 100: 29 ms
  • 1000: 115 ms
  • 10000: 1037 ms
  • 100000: 10306 ms
  • 100000: 10343 ms
  • 3° chiamata

  • 10: 20 ms
  • 100: 22 ms
  • 1000: 124 ms
  • 10000: 1037 ms
  • 100000: 10306 ms
  • 100000: 10342 ms

  • 4° chiamata

  • 10: 21 ms
  • 100: 26 ms
  • 1000: 114 ms
  • 10000: 1026 ms
  • 100000: 10300 ms
  • 100000: 9810 ms

  • 5° chiamata

  • 10: 13 ms
  • 100: 27 ms
  • 1000: 110 ms
  • 10000: 1023 ms
  • 100000: 10309 ms
  • 100000: 10606 ms

media
* 10: 37,2 ms
* 100: 26,2 ms
* 1000: 114,8 ms
* 10000: 1126,6 ms
* 100000: 10305,4 ms
* 1000000: 10281 ms


  • 1° chiamata
  • 10: 25 ms
  • 100: 27 ms
  • 1000: 111 ms
  • 10000: 1028 ms
  • 100000: 10597 ms
  • 100000: 10328 ms
  • 2° chiamata

  • 10: 13 ms
  • 100: 25 ms
  • 1000: 118 ms
  • 10000: 1037 ms
  • 100000: 10326 ms
  • 100000: 10341 ms

  • 3° chiamata

  • 10: 17 ms
  • 100: 30 ms
  • 1000: 116 ms
  • 10000: 1039 ms
  • 100000: 10597 ms
  • 100000: 10395 ms

  • 4° chiamata

  • 10: 14 ms
  • 100: 26 ms
  • 1000: 117 ms
  • 10000: 1024 ms
  • 100000: 10597 ms
  • 100000: 10042 ms

  • 5° chiamata

  • 10: 20 ms
  • 100: 26 ms
  • 1000: 115 ms
  • 10000: 1041 ms
  • 100000: 10597 ms
  • 100000: 13035 ms

media
* 10: 17,8 ms
* 100: 26,8 ms
* 1000: 115,4 ms
* 10000: 1033,8 ms
* 100000: 10542,8 ms
* 1000000: 10828,2 ms

TEST 6a: redis driver
* 1° chiamata
* 10: 158 ms
* 100: 33 ms
* 1000: 59 ms
* 10000: 420 ms
* 100000: 4067 ms
* 100000: 8423 ms

  • 2° chiamata
  • 10: 27 ms
  • 100: 26 ms
  • 1000: 55 ms
  • 10000: 418 ms
  • 100000: 4690 ms
  • 100000: 8406 ms
  • 3° chiamata

  • 10: 19 ms
  • 100: 21 ms
  • 1000: 51 ms
  • 10000: 426 ms
  • 100000: 4909 ms
  • 100000: 8525 ms

  • 4° chiamata

  • 10: 20 ms
  • 100: 20 ms
  • 1000: 57 ms
  • 10000: 410 ms
  • 100000: 4929 ms
  • 100000: 8433 ms

  • 5° chiamata

  • 10: 30 ms
  • 100: 20 ms
  • 1000: 59 ms
  • 10000: 424 ms
  • 100000: 4909 ms
  • 100000: 8456 ms

media
* 10: 50,8 ms
* 100: 24 ms
* 1000: 56,2 ms
* 10000: 419,6 ms
* 100000: 4700,8 ms
* 1000000: 8448,6 ms


  • 1° chiamata
  • 10: 366 ms
  • 100: 359 ms
  • 1000: 198 ms
  • 10000: 1970 ms
  • 100000: 17443 ms
  • 100000: 10398 ms
  • 2° chiamata

  • 10: 151 ms
  • 100: 38 ms
  • 1000: 188 ms
  • 10000: 1526 ms
  • 100000: 9892 ms
  • 100000: 11747 ms

  • 3° chiamata

  • 10: 165 ms
  • 100: 43 ms
  • 1000: 205 ms
  • 10000: 1483 ms
  • 100000: 10385 ms
  • 100000: 10437 ms

  • 4° chiamata

  • 10: 143 ms
  • 100: 42 ms
  • 1000: 206 ms
  • 10000: 1555 ms
  • 100000: 10402 ms
  • 100000: 9767 ms

  • 5° chiamata

  • 10: 129 ms
  • 100: 37 ms
  • 1000: 191 ms
  • 10000: 1548 ms
  • 100000: 10425 ms
  • 100000: 10380 ms

media
* 10: 190,8 ms
* 100: 103,8 ms
* 1000: 197,6 ms
* 10000: 1616,4 ms
* 100000: 11709,4 ms
* 1000000: 10545,8 ms

TEST 6b: redis driver con tags
* 1° chiamata
* 10: 60 ms
* 100: 25 ms
* 1000: 104 ms
* 10000: 1035 ms
* 100000: 10353 ms
* 100000: 10360 ms

  • 2° chiamata
  • 10: 16 ms
  • 100: 26 ms
  • 1000: 110 ms
  • 10000: 1041 ms
  • 100000: 10335 ms
  • 100000: 9832 ms
  • 3° chiamata

  • 10: 14 ms
  • 100: 24 ms
  • 1000: 107 ms
  • 10000: 1042 ms
  • 100000: 10344 ms
  • 100000: 10347 ms

  • 4° chiamata

  • 10: 14 ms
  • 100: 22 ms
  • 1000: 109 ms
  • 10000: 1042 ms
  • 100000: 10340 ms
  • 100000: 10372 ms

  • 5° chiamata

  • 10: 11 ms
  • 100: 22 ms
  • 1000: 108 ms
  • 10000: 1031 ms
  • 100000: 10356 ms
  • 100000: 10363 ms

media
* 10: 23 ms
* 100: 23,8 ms
* 1000: 107,6 ms
* 10000: 1038,2 ms
* 100000: 10345,6 ms
* 1000000: 10254,8 ms


  • 1° chiamata
  • 10: 23 ms
  • 100: 22 ms
  • 1000: 110 ms
  • 10000: 1029 ms
  • 100000: 10334 ms
  • 100000: 10361 ms
  • 2° chiamata

  • 10: 14 ms
  • 100: 22 ms
  • 1000: 116 ms
  • 10000: 1038 ms
  • 100000: 10315 ms
  • 100000: 10355 ms

  • 3° chiamata

  • 10: 12 ms
  • 100: 21 ms
  • 1000: 114 ms
  • 10000: 1022 ms
  • 100000: 10328 ms
  • 100000: 10335 ms

  • 4° chiamata

  • 10: 11 ms
  • 100: 22 ms
  • 1000: 119 ms
  • 10000: 1041 ms
  • 100000: 10556 ms
  • 100000: 10342 ms

  • 5° chiamata

  • 10: 10 ms
  • 100: 22 ms
  • 1000: 115 ms
  • 10000: 1039 ms
  • 100000: 9779 ms
  • 100000: 1661035178 ms

media
* 10: 14 ms
* 100: 21,8 ms
* 1000: 114,8 ms
* 10000: 1033,8 ms
* 100000: 10262,4 ms
* 1000000: 10348,8 ms

Diamo un piccolo sguardo ai risultati… A grande sorpresa il driver database risulta essere il più performante come media di recupero dei risultati, anche se devo dire che le impostazioni di MySql sono state aumentate (e non di poco) per evitare crash. Come si nota il driver esaurisce la memoria a disposizione sopra i 100.000 risultati, ma con le configurazioni base già sopra i 1000 risultati o il processo si rallenta esponenzialmente o lancia una exception. Più o meno lo stesso discorso vale per il driver array.

Memcached e Redis, con il supporto dei tags, risultano essere estremamente efficenti sia per applicazioni base che per applicazioni con cache molto “ingombranti” e se guardiamo nel dettaglio i risultati, anche le differenze di tempo di risposta tra un risultato e l’altro sono minime.

Il driver file risulta essere invece molto efficace su cache di piccole dimensioni, e conseguentemente, sicuramente il più indicato nella maggior parte dei casi.

Prima di tirare le conclusioni vorrei sottolineare il fatto che con vm “entry level” i driver database e array risultato essere assolutamente inutili su cache di grandi dimensioni, poiché senza aumentare la memoria a disposizione di PHP o MySQL la nostra applicazione non lancia esclusivamente una FatalErrorException: memory exhausted.

Il podio del test è quindi il seguente:
1. Database (fino ai 100.000 risultati)
2. Redis (oltre 100.000 risultati e utilizzando i tags)
3. Memcached (oltre 100.000 risultati e utilizzando i tags)

In verità secondo me il podio vede al primo posto parimerito Redis e Memcached per la facilità di implementazione e le performance, il driver file al secondo posto e il driver database al terzo anche se la poca malleabilità di quest’ultimo su grossi quantitativi di dati potrebbe essere molto limitativo se al posto di semplici collezioni recuperate dal database si inizia a prendere in considerazione immagini o oggetti più complessi.

… ed ora?

E Voi?

Quale è il driver che più utilizzate?
Come gestite il sistema di Cache nelle vostre applicazioni?
Avete effettuato test simili utilizzando ambiente di produzione differenti?

Raggiungeteci sui vari canali di Laravel-Italia, sul forum, su Slack e dite la vostra!

I Package della Settimana – N.19 – 12 Dicembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Laravel Lang: stai costruendo un’applicazione da tradurre in tante, tante lingue e ti stai preoccupando della localizzazione dei messaggi per la validazione, autenticazione, paginazione e recupero delle credenziali? Nessun problema, anche il buon caouecs ci ha pensato. E ha preparato un interessante package contenente i vari file presenti di default in 53 lingue diverse. Niente male, eh?
  • Laravel Gitlab: il package Laravel Gitlab, come il nome suggerisce, permette di integrare la tua applicazione con Git Lab. Il readme è semplice da seguire, e contiene pochi ma ottimi esempi d’uso;
  • TwigBridge: anche qui il nome non lascia molto spazio alla fantasia! TwigBridge permette di usare Twig come sistema di template nella propria applicazione. Per chi viene da Symfony e magari non ha voglia di imparare Blade, o per chi vuole semplicemente provare qualcosa di diverso;
  • Laravel Nested Set: questo package consente di implementare agevolmente un nested set, specifico modo di memorizzare i dati, in un database relazionale, coinvolti in relazioni “gerarchiche”. Ad esempio, una pagina che è “figlia” di un’altra pagina. Il readme del progetto spiega tutto alla perfezione, con l’aggiunta di un’ottima pillola teorica!
  • Trait Command: concludiamo con qualcosa di leggero. Questo package aggiunge a Laravel un comodo comando artisan per generare dei trait. Precisamente, artisan make:trait ModelTrait Traits, dove ModelTrait è il nome del trait che verrà creato, Traits la cartella di destinazione del file;

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Annunci di Servizio – Calendario per Laravel 5.4 e Laravel 5.5

0

Il testo originale di questo articolo di Laravel-News può essere trovato a questo indirizzo.

Nel 2013, Taylor Otwell ha annunciato il ritmo dei rilasci ufficiale delle nuove versioni di Laravel. Seguendo una regola semplice: due rilasci all’anno, uno ogni sei mesi.

Qualche giorno fa, Taylor ha dichiarato su Twitter che modificherà leggermente questa regola, facendo slittare tutto di un mese. Non più Dicembre e Giugno, quindi, ma una release a Gennaio ed una a Luglio.

Per due motivi:

  • avere più tempo a disposizione per il test dopo il rilascio di Symfony;
  • far coincidere il rilascio con Laracon, la conferenza annuale dedicata a Laravel;

Non cambierà comunque molto per gli sviluppatori: Laravel 5.4 verrà rilasciato a Gennaio!

Il ciclo, come già detto, riprenderà regolarmente a Luglio, con Laravel 5.5.

Buona giornata, artigiani!

Laravel Cafè #5 – Laravel Vs. Lumen

0


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L’idea è di creare un punto di discussione. Se hai qualche perplessità sull’argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po’ di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L’Argomento

Nei giorni scorsi tra i moltissimi argomenti interessanti che ogni giorno affrontiamo sul canale Slack di Laravel Italia è emersa una domanda:

Quali sono le differenze sostanziali tra Laravel e Lumen?

Una domanda che in molti si pongono ma alla quale non è sempre semplice dare risposta. Cercando in maniera molto semplice “Laravel vs Lumen” su google ci si trova di fronte a una miriade di risultati, molti dei quali parziali o molto generici. Questo articolo ad ogni modo non è una comparazione tra i due framework, ma delinea semplicemente gli aspetti tecnologici alla base, cercando di dare una visione generale sulle possibili applicazioni di entambi.

Inizio con un piccola nota su entrambi i framework riguardante la documentazione. Dato che l’obbiettivo di oggi è capire un po’ meglio quali sono le differenze alla base tra i due framework, la cosa migliore da fare solitamente è consultare la documentazione ufficiale ma, come già sottolineato in altri articoli, questa risulta un po’ scarna soprattutto se si vuole capire cosa c’è nel dietro le quinte di tutti i workflow, le injection e le interazioni tra le varie componenti. Se però aggiungiamo Laracasts, un codice sorgente ben commentato, una comunità molto attiva a livello internazionale e cercando approfonditamente nel web potremmo soddisfare la nostra sete di conoscenza e delineare meglio le differenze tra i due framework.

Cercando di minimizzare il tutto in una frase: entrambi i framework hanno come obbiettivo l’elaborazione di una request e la restituzione di un response e tutte le differenze tra i due framework risiedono nell’elaborazione della request.

Dalla documentazione inoltre spicca una caratteristica importante: la maggior parte delle componenti alla base sono le stesse e questo permette di utilizzare il codice sviluppato per Laravel su Lumen e viceversa, semplicemente copiando i vari controller, modelli ecc. La domanda ora è “funzionerà tutto sempre e comunque?”, fondamentalmente si ma vanno tenuti in considerazione alcuni accorgimenti, principalmente legati allo “scopo” di una applicazione scritta in Laravel o in Lumen e alla fine dell’articolo vedremo quali.

Come si legge nella homepage e in questa risposta di StackOverflow Lumen is all about speed. La velocità nell’elaborazione della richiesta è la caratteristica peculiare di Lumen, il quale può gestire molte più richieste di Laravel e che, con il passare del tempo, si è focalizzato verso obiettivi più specifici, sacrificando una parte di versatilità, ma rendendolo estremamente adatto alla creazione di microservice.

Cerchiamo di chiarirci un po’ meglio le idee in primis spulciando il dettaglio del lifecycle di una request in Laravel e potremmo farci un quadro generale più completo su tutto ciò che il framework gestisce prima di chiamare una qualunque rotta o un qualunque controller. Allo stesso modo, se facciamo caso alla composizione di uno stacktrace generato successivamente al rendering di una exception, si può intuire la complessità delle operazioni eseguite.

Tutta questa complessità ad ogni modo ha reso il framework flessibile, versatile, facilmente configurabile e con moltissime altre feature e componenti pronte all’uso. Lumen invece sacrifica la flessibilità e la versatilità per guadagnarne in velocità. Come anticipato alcune componenti sono differenti, come nel caso del sistema di routing, o omessi, come nel caso delle Facade, tutto per poter elaborare il maggior numero di richieste possibili nel minor tempo possibile. La sostanziale differenza la si nota comunque nella fase di boot, studiata appositamente per migliorare le prestazioni.

Di questa intervista in cui Taylor Otwell annunciava l’uscita di Lumen, vorrei riportare l’ultima domanda che gli è stata posta per iniziare a tirare le somme:

How were you able to get the framework so fast, while still keeping so many great features?
This is again due to the great convenience of the Illuminate components. Basically, all I needed to do was “glue” them together in a different way than a full-stack framework would glue them together. This means instead of maximum flexibility in the bootstrapping process, Lumen opts for maximum speed. The actual Lumen framework repository is probably only a dozen files or so. The rest is made up of the Illuminate components. This allowed me to flesh out the features of the framework very quickly, though it did take me three or four iterations to find a solution that was really, really fast while still providing very powerful features.

Traduzione

Com’è stato possibile rendere così veloce il framework e mantenere al tempo stesso così tante ottime features?
Ciò è ancora riconducibile alla grande comodità dei componenti Illuminate. Fondamentalmete, tutto ciò di cui avevo bisogno era “incollarle” insieme in maniera diversa da come un full-stack framework avrebbe fatto. Questo significa che Lumen opta per una velocità massima al posto di una flessibilità massima nel processo di bootstrap. L’attuale repository del framework Lumen è probabilmente composta da una dozzina di file poco più. Il resto è formato dalle componenti Illuminate. Questo mi ha permesso di arricchire le feature del framework molto velocemenete, anche se mi ci sono volute tre o quattro iterazioni per trovare una soluzione che fosse veramente, veramente veloce e mantenere comunque delle feature molto potenti.

Siamo ormai alla fine della pausa e vorrei arrivare al “punto” della situazione. Quando utilizzare Lumen? Quando, invece, non usarlo?

Se stiamo partendo con un nuovo progetto, l’overengineering non è che un male. Partire con Laravel è quindi la soluzione più opportuna. Basta pensare anche ad una semplice autenticazione e relativa gestione utenti, che con Lumen è un attimo più macchinosa. Laravel è la scelta precisa, sia per le feature che il framework mette a disposizione che per la grande disponibilità di package aggiuntivi.

A partire dalla versione 5.2, Lumen si sta sempre più focalizzando sulla creazione di microservice il cui scopo è facilitarci la vita gestendo operazioni come la gestione di code, piuttosto che creare dei cronjob di estrapolazione di dati statistici e inviare i relativi risultati attraverso una mail di riepilogo. Ecco, questo è il caso d’uso ideale: una gestione di task semplificata all’osso che carica lo stretto indispensabile e restituisce una risposta rapida e precisa.

Insomma, il nostro progetto sta diventando sempre più grande? Utilizzare una soluzione ibrida può essere molto efficace, spacchettando il monolite in tanti microservice specifici e indipendenti, magari avendo comunque alla base Laravel per la gestione di altre parti, del filesystem e così via… Come al solito le applicazioni possono essere infinite!

… ed ora?

E Voi?

Utilizzate Lumen? Se si per cosa? Quali sono le caratteristiche che più vi piacciono di questi due framework?

Questo articolo è stato solo un breve specchietto sulle principali differenze e come al solito vi invito a continuare la discussione continuando la discussione di questo articolo o raggiungendoci sul nostro Slack!

I Package della Settimana – N.18 – 5 Dicembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • RabbitMQ Queue Driver: qualcuno ha già sentito parlare di RabbitMQ? Bene: questo package è un driver che ne consente l’uso con il nostro framework preferito. Giusto il tempo di un composer install, aggiunta del service provider ed un po’ di configurazione;
  • Laravel Omnipay: Omnipay è un ottimo package che consente di usare, nella propria applicazione, più tipi di pagamento con un’unica API ed un sistema basato su driver. Questo package consente di integrare alla perfezione Omnipay e Laravel… da provare!
  • Laravel API Tester: questo progetto, piccolo ma interessante, consente di avere a disposizione un tool per testare le proprie API al volo, un po’ come si può fare con un tool come Postman, per intenderci. Può essere anche un’occasione per mettere le mani su un progetto fatto con Laravel e vedere com’è stato strutturato!
  • Tail: Laravel Tail, di spatie, reintroduce il comando tail da usare con Artisan, per navigare più agevolmente nei log della propria applicazione. Interessante la possibilità di interrogare anche un file di log in remoto;
  • Laravel Fractal: Laravel Fractal, sempre di spatie, consente di integrare velocemente Laravel con uno dei package più famosi della League PHP. Mette a disposizione una comoda Facade (per i più pigri) e supporta anche Lumen;

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

I Package della Settimana – N.17 – 28 Novembre 2016

0

La rubrica I Package della Settimana si propone, ogni Lunedì mattina, di suggerire cinque package per Laravel descrivendoli brevemente. L’obiettivo? Riuscire a dare nuovi spunti ai lettori, far conoscere nuovi tool ed ottimizzare il flusso di lavoro. Durante la pausa caffè.

Vediamo cosa c’è questa settimana!

I Package della Settimana


  • Sentinel: in tanti già ne hanno sentito parlare. Sentinel è un package interamente dedicato all’autenticazione ed autorizzazione, ed è scritto in PHP puro. Nonostante la sua natura è facilmente configurabile anche su Laravel, e la documentazione stessa riporta come fare. Conta molte più feature di Sentry, del quale si dichiara “competitor”;
  • iSeed: ad alcuni di noi sarà sicuramente capitato di dover partire da un database esistente e ricavare delle migration. Cosa succede, però, per quanto riguarda il seeding? Beh, questo package permette di creare dei seed a partire dallo stato attuale del database. Potrebbe tornare utile!
  • DataDog Statsd Client: come il nome facilmente suggerisce, si tratta di un client da usare per DataDog, il servizio dedicato al monitoring di applicazioni. Tramite una semplice installazione e configurazione, dovrebbe essere facile connettersi al servizio e lavorarci agevolmente;
  • Laravel Doctrine Scout: qualcuno qui, probabilmente, già conosce Laravel Doctrine, “ponte” tra Laravel ed il famoso ORM. Questo package permette di abilitare, anche con Doctrine, le funzionalità di Laravel Scout, che consente di effettuare ricerche full-text;
  • Croppa: finiamo in relax. Croppa è una comoda libreria da usare per generare velocemente delle thumbnail di immagini. La sua peculiarità è che permette di implementare questa operazione sfruttando il formato dell’URL. Dacci un’occhiata, potrebbe tornare utile!

E tu, hai qualche package da suggerire? Lasciaci le tue impressioni qui sotto, in un commento, o faccelo sapere sullo Slack di Laravel-Italia!

Ci vediamo la prossima settimana.

Creare REST API con Laravel API Boilerplate – JWT Edition

0

Uso questo articolo per segnalare un progetto che ho (ri)portato a termine proprio oggi. Quante volte ci è capitato di dover creare un’API con Laravel? Siamo nel 2016, per cui credo un bel po’ di volte. Ecco, è capitato anche a me. La prima volta ho fatto qualche prova e ho messo su un primo tentativo, poi col tempo l’ho migliorato. Alla fine, dopo svariate iterazioni, ho deciso di condividerlo su Github, a disposizione di tutti.

Cosa ci è uscito fuori? Il Laravel API Boilerplate (JWT Edition)!

In poche parole, questo package permette di mettere su in poco tempo, praticamente al volo, tutto quello che serve per sviluppare delle REST API seguendo svariate buone pratiche. Si tratta fondamentalmente di un’integrazione, in Laravel, di tre ottimi package:


“Ok, bella, ma non l’avevi già pubblicato? Perchè farci un articolo oggi?”

Perchè, dopo varie richieste, ho trovato un po’ di tempo per aggiornarlo in modo tale da renderlo compatibile con Laravel 5.3.

Non si tratta però soltanto di un cambio di versione del framework: ho preferito prendermi qualche ora in più per riscrivere totalmente tutto il boilerplate, scrivendo i test e poi implementando il codice. Il risultato è quindi uscito fuori più pulito e più stabile.

Vediamo al volo come installarlo, e come usarlo.

Installazione

Una volta predisposto Homestead, o quello che preferisci per sviluppare, tutto quello che devi fare è eseguire il comando

$ composer create-project francescomalatesta/laravel-api-boilerplate-jwt myNextProject

dove ovviamente myNextProject è il nome della cartella in cui verrà creato il nuovo progetto. Una volta terminato il processo, esegui

$ php artisan migrate

per preparare le tabelle necessarie al boilerplate. Nulla di particolare, sono le stesse migration che trovi con Laravel di default!

E… niente, finito.

Autenticazione

Una delle maggiori comodità di questo boilerplate sta nell’aver già predisposto tutto quello che serve all’autenticazione del nostro utente.

In app/Api/V1/Controllers, infatti, trovi già dei controller pronti all’uso, costruiti ad-hoc per gestire l’accesso degli utenti, la registrazione, il recovery delle credenziali ed il successivo reset delle password. Per mantenermi in linea con Laravel 5.3 ho implementato queste quattro funzionalità in altrettanti controller. Guarda tu stesso nel repository.

Route

Per evitare di farti perdere troppo tempo, ho predisposto in routes/api.php una serie di route già pronte ad essere usate, in modo tale da poter percepire subito le varie possibilità offerte dal progetto.

Guardiamo insieme il file:

<!--?php

use DingoApiRoutingRouter;

/** @var Router $api */
$api = app(Router::class);

$api--->version('v1', function (Router $api) {
$api->group(['prefix' => 'auth'], function(Router $api) {
$api->post('signup', 'SignUpController@signUp');
$api->post('login', 'LoginController@login');
$api->post('recovery', 'ForgotPasswordController@sendResetEmail');
$api->post('reset', 'ResetPasswordController@resetPassword');
});

$api->group(['middleware' => 'jwt.auth'], function(Router $api) {
$api->get('protected', function() {
return response()->json([
'message' => 'Access to this item is only for authenticated user. Provide a token in your request!'
]);
});

$api->get('refresh', [
'middleware' => 'jwt.refresh',
function() {
return response()->json([
'message' => 'By accessing this endpoint, you can refresh your access token at each request. Check out this response headers!'
]);
}
]);
});

$api->get('hello', function() {
return response()->json([
'message' => 'This is a simple example of item returned by your APIs. Everyone can see it.'
]);
});
});

Piuttosto semplice: la route hello è pubblica. Tutti la possono leggere in qualsiasi momento.

Poco più in alto, invece, viene creato un gruppo che condivide il middleware jwt.auth. Le route qui sotto faranno uso di questo middleware, che si occupa di verificare la presenza di un access token nella richiesta.

In questo gruppo c’è la route refresh, che oltre ad essere “protetta” dal middleware jwt.auth fa uso anche dell’altro middleware messo a disposizione da JWT-Auth, jwt.refresh, che restituisce un nuovo token al client ad ogni richiesta. Un ulteriore livello di sicurezza!

… ed ora?

Hai una buona base di partenza per il tuo prossimo progetto. Fammi sapere che ne pensi, e se trovi un bug apri una issue su Github!

E quando vuoi, sei il benvenuto sul nostro Slack!