Codebright It
Codebright It
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Ringraziamenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Errata Corrige . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ii
Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Traduzioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iv
Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
Le Basi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Architettura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Il Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Facades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Flessibilità . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Robustezza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
I Primi Passi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Requisiti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Installazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Configurazione del Server Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Struttura del Progetto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Routing di Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
C’era Una Volta… . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Routing di Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Routes e Parametri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Output e Response . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
INDICE
Filtri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Filtri di Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Filtri Multipli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Filtri e Parametri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Filtri (Classi) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Filtri Globali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Filtri di Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Filtri e Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Creare un Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Routing dei Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Controller RESTful . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Blade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Creare un Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Output PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Strutture di Controllo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Ereditarietà e Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Commenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
I Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Apertura di un Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Campi del Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Pulsanti del Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
Sicurezza del Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Validazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Validazione Semplice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Regole di Validazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Messaggi di Errore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
Regole di Validazione Personalizzate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
Messaggi di Errore Personalizzati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Astrazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
Preparazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
Schema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Creazione di Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Tipi di Campo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Tipi di campo speciali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Modificatori dei Campi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Modifica di una Tabella . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Eliminare le Tabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Tutto il Resto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Migrations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Concetto di Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Creare una Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Eseguire una Migration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Rollback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Trucchi Vari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Preparazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Il “toString()” di Eloquent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
Struttura della query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Metodi di fetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
Regole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Il where “magico” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Query scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Autenticazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
Eventi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
Concept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
Scatenare gli eventi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
Creare i listener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Subscribers (sottoscrittori) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Eventi globali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
Casi d’uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
Il Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
Inversion of Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
INDICE
In Arrivo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
Ringraziamenti
Innanzitutto vorrei ringraziare la mia ragazza, Emma, per supportarmi in tutte queste mie avventure,
ma anche per aver scattato le fantastiche foto dei panda che vedete nelle copertine di entrambi i miei
libri! Ti amo Emma!
Taylor Otwell, l’ultimo anno è stato incredibile. Grazie per avermi dato l’opportunità di far parte
del team e per la tua amicizia. Grazie per aver creato un framework che si è poi rivelato piacevole
da usare, trasformando il nostro codice in poesia. Per metterci così tanto tempo e passione nel suo
sviluppo. Ho apprezzato un sacco il poter lavorare con te a questa nuova versione di Laravel e spero
di lavorare ancora con te a nuovi progetti in futuro!
Ringrazio poi Eric Barnes, Phill Sparks, Shawn McCool, Jason Lewis, Ian Landsman. Grazie per tutto
il supporto con il framework e per essere degli ottimi compagni.
Un grazie va anche ai miei genitori, che da ventotto anni supportano tutto quello che faccio (e anche
per aver comprato un’infinità di copie del mio primo libro per tutta la famiglia!)
Grazie poi a tutti quelli che hanno comprato il mio primo libro, Code Happy, e a tutta la comunità
di Laravel. Senza il vostro supporto questo secondo libro non sarebbe mai venuto alla luce.
Errata Corrige
Probabilmente rispetto al primo libro (almeno un po’, spero) sarò migliorato nella scrittura. Posso
assicurarvi, però, che ci saranno tanti, tanti errori! Se ne scopri qualcuno e vuoi darmi una mano,
scrivimi all’indirizzo [email protected]¹, spiegandomi dove ho sbagliato e cosa.
Tutti gli errori saranno sistemati non appena verranno segnalati o scoperti, e tutte queste “fix”
saranno pubblicate alla prossima release del libro.
¹mailto:[email protected]
Feedback
Ovviamente puoi mandarmi qualsiasi tipo di feedback riguardo i contenuti del libro, con una mail
all’indirizzo di sopra oppure con un tweet a @daylerees. Cercherò di rispondere a tutte le mail e
tweet che riceverò.
Traduzioni
Se dovessi avere voglia di tradurre Code Bright nella tua lingua, mandami una mail all’indirizzo
[email protected]², con la tua proposta. Divideremo a metà i guadagni della copia tradotta, che
dovrà essere venduta allo stesso prezzo di quella in inglese.
Nota: il libro è scritto nel formato Markdown.
²mailto:[email protected]
Introduzione
Bene, è passato un po’ di tempo dall’ultima volta che ho scritto qualcosa seriamente. Code Happy
è stato rilasciato ormai 12 mesi fa e ha totalizzato circa tremila vendite. Vediamo se ricordo ancora
come si fa.
Se hai letto l’altro libro sai sicuramente una cosa: prima di essere uno scrittore sono uno sviluppatore.
Per questa ragione non troverai parole molto lunghe in questo libro. Non troverai niente che
impressionerà Shakespeare (escludendo gli errori grammaticali) e tutto quello che troverai invece
sarà ciò che più riesco ad avvicinare ad un parlare semplice e diretto, assolutamente necessario per
comprendere a pieno Laravel.
Posso assicurarti, inoltre, che vedrai tanta passione! La passione che mi spinge a fare quello che
faccio, per Laravel, che mi porta a scrivere questo libro in maniera più simile possibile ad una
conversazione tra amici, davanti a te. Tra l’altro, se davvero dovessi avere voglia di parlarmi, mi
trovi nel canale IRC di Laravel.
Ok, direi che adesso (soprattutto per i lettori appena “iniziati”) direi che è il momento di dire qualcosa
di me. Ok, a tanti non interessa, ma lasciate un po’ di spazio al mio ego, su!
Mi chiamo Dayle Rees (fino a qua credo ci siate arrivati) e sono uno sviluppatore web, oltre che
un entusiasta del design! Vengo da un piccolo paese sulla costa del Galles, chiamato Aberystwyth.
Al momento in cui ho scritto il mio primo libro lavoravo per la National Library del Galles, ad
Aberystwyth, che è una delle tre copyright libraries nel Regno Unito.
Da allora sono cambiate un po’ di cose: mi sono spostato a Cardiff, la capitale del Galles, e ho
cominciato a lavorare con BoxUK, che si occupa di consulenza e sviluppo su internet. Sono entrato
in contatto con un ottimo gruppo di sviluppatori, pieno di passione per quello che fanno.
Lo sviluppo web, comunque, non è solo il mio lavoro: è anche il mio hobby. Adoro trovare cose
interessanti, trovare codice interessante e design belli ed eleganti. Sono convinto che il nostro talento
ci permette di creare cose bellissime e per questo adoro vedere le idee prendere vita.
Circa un anno fa ho cominciato a dare una mano alla community di Laravel, con alcuni bundles,
dei design e facendo un po’ tutto quello che poteva servire. Poi le cose si sono evolute e il mio
coinvolgimento nel progetto è cresciuto. Adesso Laravel è il mio progetto principale e sono membro
del core team di sviluppo.
Con Laravel 4 (nome in codice Illuminate) ho fatto un bel po’ di cose. Ho lavorato al fianco di Taylor
Otwell per fare in modo di rilasciare il miglior framework che tu abbia mai usato. Non fidarti sulla
parola, però! Comincialo ad usare e ringraziaci più in la, mentre non potrai smettere di scrivere
codice per quanto ti starai divertendo!
Laravel è un perfetto esempio di come anche un tool di sviluppo può rivelarsi creativo. La sua sintassi
elegante e diretta non ha praticamente rivali.
Introduzione vi
Ti dico tutto con una semplice quanto fuorviante risposta: tutto e niente!
Laravel 4, infatti, è stato totalmente riscritto, in modo tale da garantire un salto in avanti fuori dal
comune per quanto riguarda la flessibilità e il testing. Il tutto, ovviamente, corredato da un miliardo
di varie nuove features.
Se Laravel 3 ti dava un sacco di libertà per quanto riguardava la struttura del codice, con Laravel
4 cambia tutto: i programmatori più esigenti potranno infatti modificare il framework stesso ed
adattarlo ai propri bisogni!
Quanto sento discorsi come “cosa è migliorato” o “cosa è cambiato” cerco sempre almeno un
elemento distintivo. Con Laravel 4 è praticamente impossibile, visto che è cambiato tutto.
Una cosa però è rimasta uguale: la sintassi elegante ed espressiva. Fidati, apprezzerai ancora di più
il tempo passato a scrivere codice.
Code Happy, il mio precedente libro, copre le versioni che vanno dalla 3.0 alla 3.2.x. Ora, con tremila
copie vendute credo di aver fatto un lavoro discreto.
Avrei potuto riprenderlo e adattarlo a Laravel 4, ma non sarebbe stato abbastanza efficace e d’aiuto.
D’altronde, come già detto poco fa, la nuova versione stessa del framework è una rivoluzione rispetto
al passato: un adattamento sarebbe stato inadatto!
Inoltre, molte persone hanno basato un sacco di progetti (alcuni molto grandi) su Laravel 3. Sostituire
tutto il mio precedente lavoro con l’adattamento per Laravel 4 avrebbe lasciato un sacco di gente a
piedi.
Ovviamente c’entra anche la mia esperienza. Ho imparato un sacco di cose su come scrivere un libro,
da quando ho finito Code Happy. Adesso è un buon momento per metterle in pratica e, soprattutto,
conoscerne di nuove.
Se vuoi puoi farlo, certamente, anche per fare un confronto su cosa è cambiato nell’ultimo anno. Tra
l’altro ci ho scritto un sacco di cose divertenti. Giuro.
Scherzi a parte, il libro che stai leggendo comprende tutto, a partire dalle basi: non sarà assolutamente
vincolante aver letto Code Happy, tranquillo!
Esattamente come è successo per Code Happy, questo libro viene pubblicato mano a mano che viene
scritto. Questo vuol dire che, nel momento in cui esce un nuovo capitolo tu lo leggi, senza ulteriori
ritardi.
Ovviamente, nel momento in cui vengono aggiunti nuovi capitoli ti verrà mandata una mail per
avvisarti! Inoltre (e soprattutto) i prossimi capitoli del libro saranno gratis, senza altre spese.
Un metodo come questo ci offre un sacco di flessibilità: riesco a rimanere rilassato, sapendo anche
che posso cambiare qualcosa facilmente nel caso in cui mi accorgo di aver sbagliato. Tutto questo,
unito all’assenza di scadenze, è praticamente sinonimo di qualità elevata rispetto allo standard.
Senza considerare che qualsiasi aggiunta o modifica ti verrà mandata senza costi aggiuntivi! Non è
fantastico?
Ottimo! Da ora cominciamo ad imparare qualcosa. Sali a bordo e comincia a divertirti con Laravel!
Per qualsiasi cosa scrivimi, via mail, con un tweet o su IRC!
Introduzione (di Francesco)
Anche stavolta voglio scrivere un paio di righe su questa nuova esperienza. Aspettavo da tempo il
libro e, non appena Dayle è riuscito a passarmelo, ho cominciato subito la traduzione.
Rispetto all’anno scorso, rispetto a Code Happy, sono cambiate un sacco di cose. Il numero di utenti di
Laravel è aumentato, il numero di versione di Laravel è aumentato, la community stessa è diventata
più grande. Ovviamente, è aumentato anche il numero di pagine del libro che stai leggendo rispetto
al precedente ed aumenterà ancora, dato tutto quello che c’è in programma.
Ci saranno “aumenti” e nuove nascite in tutti i sensi: approfitto di questa occasione, infatti, per
annunciare che tra settembre e ottobre di di questo anno comincerò a lavorare, con altri volenterosi
sviluppatori, su quella che diventerà la community ufficiale Italiana di Laravel.
Adesso, però, veniamo al punto: voglio usare questa introduzione per dire un paio di cose sul libro
stesso e ringraziare alcune persone.
Innanzitutto, una nota sulla traduzione: molte persone noteranno che non ho tradotto svariati esempi
di codice. Questo essenzialmente per due motivi.
In primis non si tratta di esempi complessi, per cui comprenderli non sarà difficile. Insomma, se non
capisci la frase “I am Iron Man” hai decisamente sbagliato libro.
Chiaramente, si tratta anche una questione di umorismo. I vari giochi di parole di Dayle rendono
meglio in inglese: ti assicuro che he ha messi tanti e tradurli tutti sarebbe stato impossibile, almeno
senza far perdere loro il significato base.
Poi dai: “Come at me, Bro.” non va tradotto.
Detto questo, ho cercato di rimanere il più possibile fedele al Code Bright che avresti comprato in
inglese: devo dire che ci sono riuscito quasi sempre, con qualche inevitabile eccezione.
Arriviamo ora ai ringraziamenti: un grazie a Taylor Otwell, per aver creato un capolavoro; Dayle
Rees, per aver scritto Code Happy e poi Code Bright; Tutto il team di Laravel, per il meraviglioso
lavoro svolto finora per gli sviluppatori;
Poi voglio ringraziare un po’ di persone qua e la: la mia collega Antonia, donna meravigliosa e per
me molto importante, Fabio Lalli e gli Indigeni Digitali, per lo stimolo che mi danno tutti i giorni tra
post, approfondimenti e dibattiti. Il professore Stefano Epifani, una persona stimolante che ho avuto
la fortuna di conoscere qualche anno fa, ed il professore Umberto Nanni (last but not least), con il
quale ho avuto una piacevolissima conversazione, decisamente interessante, in una calda mattina
d’estate.
È tutto, la smetto qui. Buona lettura a tutti.
Le Basi
Ehi, ma questo capitolo non c’era in CodeHappy!
Acuta osservazione, mio caro fan affezionato. Neanche faccio in tempo a cominciare e mi precedi?
Ottimo inizio!
Ad ogni modo, devi sapere che Laravel 4 utilizza svariate tecnologie in maniera più o meno costante.
Questo vuol dire che, avendo bene in mente alcuni di questi concetti sarà più facile per te capire al
meglio l’intero framework. Ecco perché ho deciso di scrivere questo capitolo!
Sicuramente gli sviluppatori web con un po’ di esperienza avranno già conosciuto alcune (se non
tutte) queste “cose”. Nel caso dovessi vedere che ci sono strumenti o concetti che conosci già, sentiti
libero di saltare a piè pari questo capitolo e vai dritto al successivo.
Altrimenti, cominciamo subito! C’è tanto da vedere in questo capitolo: si comincia dai Namespaces.
Namespaces
A partire da PHP 5.3 una nuova feature ha fatto capolino nel nostro linguaggio preferito. Un qualcosa
chiamato “Namespacing” è stato aggiunto dagli sviluppatori. Molti dei linguaggi più recenti hanno
già questa feature, mentre per PHP le cose sono andate un po’ più lentamente. Ovviamente ogni
nuova feature ha uno scopo: non si aggiungono mica le cose a csao, no?
Vediamo a cosa ci può servire questo Namespacing.
Devi sapere che in PHP non puoi avere due classi che hanno lo stesso nome: devono essere, infatti,
uniche. C’è però un problema: immagina di usare, per definire il tuo utente, una classe User. In un
secondo momento aggiungi una libreria al tuo progetto e, tra le varie classi, ne ha anche un’altra
che si chiama, esattamente, User.
Un problema non indifferente, vero?
Ecco perché sono stati introdotti i Namespaces. Oltre a risolvere questo problema, inoltre, ci
permettono di “racchiudere” il nostro codice in tanti pacchetti, garantendoci quindi una migliore
organizzazione del lavoro. Per capirci meglio, però, facciamo qualche esempio più tangibile.
Namespace Globale
Ok, partiamo da qui, da questa classe.
Le Basi 2
1 <?php
2
3 //app/models/Eddard.php
4 class Eddard {
5
6 }
Hai visto del codice simile un miliardo di volte, vero? Scommetto che sai già come fare per istanziare
un oggetto di questa classe:
1 <?php
2
3 //app/routes.php
4 $eddard = new Eddard();
Ehi ok, va bene, scusa. Comunque, possiamo pensare a questa classe come una classe che si trova
nel “global namespace” (namespace globale). Non so se sia questo il termine preciso, però rende
abbastanza bene l’idea.
Insomma, questa classe esiste senza un namespace particolare, uno spazio particolare in cui viene
messa. Semplicemente è una classe, come tante altre.
Namespacing Semplice
Ok, adesso creiamo un’altra classe, insieme all’originale (la classe Eddard nel namespace “globale”).
1 <?php
2 //app/models/another.php
3 namespace Stark;
4
5 class Eddard {
6 }
1 <?php
2 //app/routes.php
3 $eddard = new Eddard();
Andiamo ad ottenere sempre un’istanza della prima classe, quella nel global! Com’è possibile? Chi
ha deciso quale classe era quella giusta?
Questione di namespace, amico mio.
Facciamo così: specifichiamo che la classe da usare deve essere presa dal Namespace “Stark”.
1 <?php
2 //app/routes.php
3 $eddard = new Stark\Eddard();
Esatto! Possiamo istanziare una classe esattamente come facevamo prima, con l’unica differenza di
specificare anche il namespace di appartenenza nel caso questa non faccia parte del “global”.
Chiaramente PHP non vuole metterti dei limiti: puoi raggiungere qualsiasi livello di complessità
vuoi.
1 Questa\Combinazione\Di\Namespace\E\Classe\Funziona\Lo\Stesso
1 <?php
2 //app/routes.php
3
4 namespace Stark;
5
6 $eddard = new Eddard();
Proprio qui, grazie alla prima linea di codice, abbiamo spostato l’esecuzione del nostro script
all’interno del Namespace “Stark”. Ora, proprio perché siamo al suo interno, abbiamo ricevuto
l’istanza della classe giusta, anche senza specificare lo “Stark” davanti! Gliel’abbiamo detto sopra, e
PHP odia le ripetizioni.
Insomma, tutto è relativo.
Le Basi 4
Adesso che abbiamo cambiato namespace, però, abbiamo creato un “piccolo problema”. Insomma,
come facciamo ad istanziare la classe “Eddard” del namespace globale?
Siamo all’interno di Stark, come posso “guardare fuori”?
PHP offre un trucco per risolvere il problema: si può istanziare una classe del global namespace
usando il prefisso “” come nell’esempio di seguito.
1 <?php
2
3 // app/routes.php
4 $eddard = new \Eddard();
Grazie a questo slash, infatti, PHP sa che ci stiamo riferendo al namespace globale, quindi lavora in
quel contesto.
Ok, adesso usa un po’ la tua immaginazione e la tua fantasia. Immagina che hai un altro namespace
ed un’altra classe. Precisamente, la classe Edmure del namespace Tully (quindi TullyEdmure). come
fare per usare questa classe dal namespace Stark?
1 <?php
2 //app/routes.php
3
4 namespace Stark;
5
6 $edmure = new \Tully\Edmure();
Abbiamo innanzitutto usato lo slash iniziale per tornare al Namespace di partenza, quello globale.
Poi, da li, ci siamo avventurati nel namespace Tully fino ad arrivare alla classe Edmure, che abbiamo
prontamente istanziato.
Insomma, più facile a farsi che a dirsi. Diciamoci la verità, però: dopo un po’ potrebbe essere noioso
stare li di continuo a dare percorsi su percorsi.
Tranquillo, pigrone! C’è la parola chiave “use”.
Vediamo come usarla.
Le Basi 5
1 <?php
2 //app/routes.php
3
4 namespace Stark;
5
6 use Tully\Edmure;
7
8 $edmure = new Edmure();
Usando la parola chiave “use” abbiamo specificato di voler usare la classe “Edmure” dal namespace
“Tully”, usando quel percorso come riferimento per quella classe specifica.
Quindi, nel caso di molte istanze, non dovremo ripetere tutte le volte la stessa formula.
Adesso, visto che ci siamo, non mi voglio fermare qui. Ti svelo un altro piccolo trucchetto che
potrebbe tornarti molto utile. Supponiamo ora di avere un’altra classe, sempre nel namespace “Tully”.
Si chiama “Brynden” ma voglio chiamarla, in quest’occasione, “Blackfish”.
Più leggibile, vero? Vediamo come fare, grazie alla parola chiave “as”.
1 <?php
2 // app/routes.php
3
4 namespace Stark;
5 use Tully\Brynden as Blackfish;
6
7 $edmure = new Blackfish();
Tutto chiaro? In parole povere, non ho fatto altro che dare un “nickname” alla classe Brynden.
Blackfish, appunto. Da quel momento il sistema me la riconosce come tale automaticamente.
Un trucco del genere ovviamente non serve solamente a soddisfare i nostri gusti: può essere molto
utile nel caso di due namespaces con nomi di classi simili.
1 <?php
2
3 namespace Targaryen;
4
5 use Dothraki\Daenerys as Khaleesi;
6
7 // app/routes.php
8
9 class Daenerys {
10
Le Basi 6
11 }
12
13 //Targaryen\Daenerys
14 $daenerys = new Daenerys();
15
16 // Dothraki\Daenerys
17 $khaleesi = new Khaleesi();
Così facendo possiamo usare due classi con lo stesso nome (Daenerys) senza farle entrare in conflitto.
È tutto qui: un gioco il cui scopo è evitare i conflitti, raggruppando gli attori del gioco in base agli
scopi o alle fazioni.
Ok, no, sto uscendo fuori strada.
In ogni caso, puoi usare la parola chiave “use” quante volte vuoi nel tuo codice.
1 <?php
2
3 namespace Targaryen;
4
5 use Dothraki\Daenerys;
6 use Stark\Eddard;
7 use Lannister\Tyrion;
8 use Snow\Jon as Bastard;
Struttura
Riguardo i Namespace, comunque, c’è di più: non servono solo ad evitare i conflitti (come se già non
fosse poco) ma sono molto utili, come già detto prima, per organizzare il nostro codice in maniera
più strutturata rispetto al passato.
Facciamo un esempio.
Supponiamo di voler creare una libreria open source. Ho creato qualcosa di utile e vorrei che tutti
potessero usarla facilmente, sarebbe fantastico!
Il problema, però, è che non voglio causare dei conflitti nei nomi: sarebbe terribilmente scomodo e
sarebbe soprattutto poco furbo se voglio diffondere il mio software il più possibile.
Ecco come potrei organizzare il codice, seguendo quello che abbiamo visto finora:
Ho usato il mio nome, indicando che sono io il creatore del codice originale ed anche per separare il
mio codice da quello della persona che userà la mia libreria. Così, a partire dal namespace di base,
ho creato una struttura tutta mia per avere il codice sotto controllo.
Le Basi 7
Nella sezione dedicata a composer vedremo poi come usare i namespaces per semplificare il
caricamento delle definizioni delle classi. Quando sarà il momento ti consiglio di darci un’occhiata:
non è una cosa da sottovalutare.
Limiti
In verità mi sento un po’ “colpevole” nel chiamare questa parte del capitolo “Limiti”. Anche perché
non stiamo parlando di un bug o di qualcosa del genere.
Come alcuni di voi sapranno, in altri linguaggi i namespaces sono implementati in maniera più o
meno simile a quella che abbiamo visto finora. In alcuni di questi linguaggi inoltre c’è una feature
aggiuntiva molto vantaggiosa.
Nel Java, ad esempio, puoi importare velocemente un certo numero di classi nel namespace corrente
utilizzando quella che in gergo si dice “wildcard”.
Qualcosa di molto simile a:
1 import dayle.blog.*;
Questa istruzione, ad esempio, importerà tutte le classi che si trovano nel “pacchetto” dayle.blog.
Ora, ecco le cattive notizie: con PHP non potrai fare una cosa del genere, ma dovrai importare ogni
singola classe.
Tuttavia, ecco un altro trucco che potrebbe tornarti utile: immagina di avere una struttura di classi
come nell’esempio precedente:
Potremmo usare lo stesso stratagemma di prima, quello del “nickname”, per creare un “sotto-
namespace”, in modo tale da importare il resto con più facilità. Ad esempio:
1 <?php
2
3 namespace Baratheon;
4
5 use Dayle\Blog as Cms;
6
7 // app/routes.php
8
9 $post = new Cms\Content\Post;
10 $page = new Cms\Content\Page;
11 $tag = new Cms\Tag;
Molto utile, soprattutto quando hai a che fare con tante classi!
Ok, adesso basta con i Namespace. È venuto il momento di scoprire il JSON.
Le Basi 8
JSON
Cos’è il JSON?
JSON è una sigla, che sta per JavaScript Object Notation. È stato chiamato così perché, come potrai
ben immaginare, il primo linguaggio a beneficiarne è stato Javascript.
Fondamentalmente, JSON è un modo più “umano” di memorizzare array, oggetti e valori come
stringhe. Viene utilizzato molto per i trasferimenti di dati ed è più leggero e scorrevole della sua
alternativa più famosa: XML.
Spesso viene usato dal front-end di un’applicazione che deve prelevare delle informazioni dal
backend, il tutto senza ricaricare la pagina corrente: una cosa del genere si fa con Javascript e AJAX.
Giusto per farti un esempio, vai su Facebook e commenta una foto: il commento verrà memorizato
e la pagina non verrà ricaricata.
Tornando a noi, c’è da sapere anche che molte API di svariati software oggi usano come formato
standard JSON. Un esempio su tutti è sicuramente Twitter.
A partire dalla versione 5.2.0, anche PHP si è unito all’insieme dei linguaggi capaci di serializzare
e deserializzare oggetti ed array in formato JSON. Onestamente, ho abusato di questa fantastica
feature almeno un miliardo di volte.
Se lavori da un po’ di tempo con il PHP avrai sicuramente usato (o almeno visto qualche volta)
il metodo “serialize()”. Serve a rappresentare un qualsiasi oggetto PHP come una stringa. In un
secondo momento puoi sempre usare il metodo “unserialize()” per ottenere lo stesso oggetto che
avevi memorizzato in precedenza.
Ora, la stessa cosa può essere fatta anche con dati rappresentati in JSON. Sicuramente ti starai
chiedendo: “si, ma a cosa serve se già ho il metodo serialize?”
Semplice: il JSON può essere letto da un sacco di linguaggi senza troppi problemi. Un modo perfetto
per scambiare dei dati da un’applicazione all’altra senza troppi problemi. Vero?
Comunque sia, passiamo alla pratica. Vediamo un po’ di sintassi.
La Sintassi JSON
1 {"name":"Lushui","species":"Panda","diet":"Green
2 Things","age":7,"colours":["red","brown","white"]}
Et voilà!
Ok, mi ero scordato di dirti che normalmente il JSON non è molto leggibile appena viene trasmesso.
Vero, ho detto che è fatto per essere leggibile, ma quasi tutti i linguaggi di programmazione
producono una stringa senza spazi, in modo tale da essere più leggera in fase di trasferimento.
Le Basi 9
La buona notizia comunque è che JSON non conta spazi bianchi e line break, in fase di interpreata-
zione.
Proviamo a sistemare un po’ le cose. Guarda come tutto cambia con qualche a capo e qualche spazio
bianco, a partire dalla stringa vista poco fa.
1 {
2 "name": "Lushui",
3 "species": "Panda",
4 "diet": "Green Things",
5 "age": 7,
6 "colours": ["red", "brown", "white"]
7 }
Ora è più chiaro? Direi di si. Qui puoi ammirare una stringa che rappresenta Lushui, il simpatico
Panda che vedi in copertina e che veglia su di noi. Ciao Lushui.
Come vedi nell’esempio, il tutto si struttura con il concetto di coppia “chiave-valore”. Vediamo ora
in Javscript come viene vista una cosa del genere.
1 var lushui = {
2 name: 'Lushui',
3 species: 'Panda',
4 diet: 'Green Things',
5 age: 7,
6 colours: ['red', 'brown', 'white']
7 };
Avrai sicuramente notato un bel po’ di somiglianze nella sintassi. In Javascript abbiamo anche
effettuato un’assegnazione dell’oggetto ad una variabile ben precisa.
1 var lushui = { .. };
Perchè non l’abbiamo fatto con JSON? Semplice, perché JSON è un linguaggio di trasferimento di
dati. Un formato, per essere precisi. Non è un linguaggio di programmazione, quindi operazioni
come questa non sono contemplate.
Le somiglianze, comunque, non sono una coincidenza. Come già detto poco fa, inizialmente il JSON
è stato ideato per essere usato con il Javascript.
Sia in JSON che in Javascript gli oggetti sono contenuti da parentesi graffe. In entrambi i casi inoltre
consistono in coppie chiave-valore. Nella variante Javascript non c’è bisogno di usare le virgolette
per le chiavi: sono infatti identificate come “variabili”, quindi non sono state messe.
Le Basi 10
JSON però non ha variabili, non è un linguaggio di programmazione! Ecco perché le chiavi hanno
bisogno per forza delle virgolette.
La cosa non finisce qui: se in Javascript ho usato le virgolette singole, con JSON la musica cambia e
devo usare necessariamente le doppie virgolette. Ricordatelo sempre, giovane padawan!
Tornano però altri punti in comune: la chiave dal suo valore viene separata con i due punti, mentre
due paia chiave-valore vengono separate con la virgola.
JSON supporta inoltre sia stringhe che numeri. Come nell’esempio visto in precedenza:
age: 7,
Precisamente, ecco i tipi che si possono usare.
• Double
• Float
• String
• Boolean
• Array
• Object
• Null
I valori numerici vengono rappresentati senza virgolette. Fai attenzione quando usare le virgolette e
quando no. Ad esempio, l’età può essere un valore numerico, mentre un codice di avviamento postale
americano può avere bisogno di uno zero davanti (ad esempio, 07702). Chiaramente in quest’ultimo
caso va usata la stringa, che non tronca lo zero prima del sette.
I Booleani vengono rappresentati, semplicemente, dalle parole chiave “true” oppure “false”. Senza
virgolette, ovviamente.
Il valore “null” funziona in maniera simile al PHP ed è rappresentato proprio con la parola chiave
“null” senza virgolette anche in questo caso.
Ci sono poi anche gli oggetti, che possono essere inseriti all’interno di un altro oggetto, che a sua
volta ne può contenere un altro e via discorrendo.
Così come ci sono anche gli array, molto simili alla loro controparte in Javascript.
1 // JavaScript
2 ['red', 'brown', 'white']
3
4
5 ["red", "brown", "white"]
Le Basi 11
*Nota Avrai notato che non ho aggiunto un commento per lo snippet JSON dell’esempio precedente.
Questo perchè il JSON non supporta i commenti, visto che serve solo per trasferire dei dati. Ricordalo!
Come puoi vedere, sia in Javascript che in JSON gli array vengono delimitati dalle parentesi quadre
e contengono una lista di elementi separati da virgole. Anche qui, l’unica differenza è l’uso delle
doppie virgolette, che in JSON sono obbligatorie.
Lo so, sto (quasi) diventando noioso. Tranquilli, tra poco torniamo nel mondo PHP.
Voglio solo dire l’ultima cosa: avrai sicuramente immaginato che in un array, oltre a valori singoli,
possono esservi sia oggetti che array. Quindi, massima libertà sul contenuto dei tuoi JSON.
Ecco un esempio:
1 {
2 "an_object": {
3 "an_array_of_objects": [
4 { "The": "secret" },
5 { "is": "that" },
6 { "I": "still" },
7 { "love": "shoes!" }
8 ]
9 }
10 }
L’avevo promesso e mantengo la promessa: si torna al PHP. Vediamo come lavorare con JSON usando
PHP.
JSON e PHP
Proprio come ho già detto qualche riga più in alto, a partire dalla versione 5.2.0 PHP ha adottato il
supporto per la serializzazione e deserializzazione dei dati in formato JSON.
Vogliamo farlo anche noi? Certo che vogliamo. Vediamo come si fa.
Serializzare un Array PHP in JSON
Il metodo “json_encode()” serve esattamente a questo scopo.
Eccolo in azione:
1 <?php
2
3 $truth = array('panda' => 'Awesome!');
4 echo json_encode($truth);
1 {"panda":"Awesome!"}
Facile, vero?
Ora vediamo come fare il contrario: si parte da una stringa JSON e si arriva all’array PHP.
Deserializzare un array PHP da una stringa JSON
Il metodo di cui abbiamo bisogno, adesso, è “json_decode()”:
1 <?php
2
3 $truth = json_decode('{"panda":"Awesome!"}');
4 echo $truth['panda'];
Perfetto!
Ah, no, aspetta. Su schermo appare:
Fatal error: Cannot use object of type stdClass as array in …
Occorre una spiegazione aggiuntiva. Il metodo “json_decode()”, infatti, quando richiamato, gene-
ralmente usa un oggetto “stdClass” come base. Insomma, avremo un oggetto al posto di un array.
Quindi, per leggere il risultato dovremo procedere così:
1 <?php
2
3 $truth = json_decode('{"panda":"Awesome!"}');
4 echo $truth->panda;
Fantastico, vero?
No, non ti convince molto. Insomma, davvero dobbiamo tenerci questo oggetto? Avevamo un array
e ci stava comodo! Non ti preoccupare, PHP non ti lascia a piedi.
Se specifichiamo “true” come secondo parametro di “json_decode()”, infatti, otterremo un array e
non un oggetto.
1 <?php
2
3 $truth = json_decode('{"panda":"Awesome!”}', true);
4 echo $truth['panda'];
Simpatico, eh?
Anche con il JSON abbiamo finito. Alcuni di voi però (giustamente) si staranno chiedendo: “perché
tutto questo JSON, qui, in un libro su Laravel?”.
Tranquillo, non te lo dico. Al massimo dopo, tieni gli occhi bene aperti. Ok?
Nel prossimo capitolo daremo uno sguardo al nuovo package manager per PHP. Stiamo parlando di
Composer: impareremo a conoscerlo e capirai anche perché il JSON è così importante.
Le Basi 13
Composer
Penso seriamente che Composer sia qualcosa di davvero speciale nel mondo PHP. Ha cambiato il
modo di gestire le dipendenze delle nostre applicazioni e dolcemente asciugato le lacrime di tanti
sviluppatori.
Ok, un po’ di storia prima di continuare.
Una volta, nei giorni che furono, lavorare ad un’applicazione basata su dipendenze di terze
parti significava spesso installare queste dipendenze con PEAR o PECL. Questi due manager di
dipendenze, però, non avevano un grande repertorio.
Ora, quando un nuovo “pacchetto” diventava disponbile potevi scaricarne una versione specifica,
che sarebbe poi stato installato sul tuo sistema. Tuttavia, questa dipendenza andava a collegarsi più
con PHP che con la tua applicazione.
Beh, detta così rende poco l’idea, per cui cercherò di essere più “diretto”. Immagina di avere due
applicazioni. Una si chiama Topolino e l’altra Minnie (lo so, ho un’originalità disarmante).
Queste due applicazioni hanno, tra i prerequisiti, il pacchetto “Topolinia”. C’è un problema, però:
Topolino ha bisogno di Topolinia 1.5, mentre Minnie ha bisogno di Topolinia 2.0.
Sappiate che per colpa di questa situazione (frequente tra l’altro) un programmatore, da qualche
parte e fino a qualche tempo fa, piangeva lacrime amare. Almeno fino a quando non è arrivato
Composer.
Come già abbiamo detto poco fa, Composer serve a gestire le dipendenze della tua applicazione sotto
una forma detta “pacchetto”. Cos’è un pacchetto?
Immagina un pacchetto come una piccola scatola contenente tutto quello di cui ha bisogno la tua
applicazione, con una descrizione che spiega ciò che fa.
Questo pacchetto ha bisogno solo di un piccolo pezzo di carta (file) al suo interno per dire, appunto,
che è un pacchetto e vuole essere riconosciuto come tale.
Configurazione
Bene. Ci siamo. Ti ricordi quando, poco fa, ti ho spiegato che il JSON serve al trasferimento di dati
da un’applicazione all’altra? Sappi che non serve solo a quello. Rappresentando tranquillamente un
dato più o meno complesso, perché non usarlo come file di configurazione per qualcosa?
Magari per Composer.
Un file JSON ha l’estensione “.json”. Composer si aspetta di trovarne uno nella root del tuo pacchetto,
dal nome “composer.json”. Ricordalo: Laravel usa frequentemente questa prassi.
Immaginiamo di aprire un file di configurazione per un ipotetico pacchetto: ecco cosa potremmo
trovare:
Le Basi 14
1 {
2 "name": "marvel/xmen",
3 "description": "Mutants saving the world for people who hate them.",
4 "keywords": ["mutant", "superhero", "bald", "guy"],
5 "homepage": "http://marvel.com/xmen",
6 "time": "1963-09-01",
7 "license": "MIT",
8 "authors": [
9 {
10 "name": "Stan Lee",
11 "email": "[email protected]",
12 "homepage": "http://marvel.com",
13 "role": "Genius"
14 }
15 ]
16 }
Ecco cosa potrebbe contenere un ipotetico pacchetto X-Men. Perché ho usato gli X-Men? Perchè mi
piacciono, ecco perché.
Ora, non sempre è obbligatorio inserire tutte queste informazioni su un pacchetto. Normalmente un
file JSON del genere viene compilato se hai intenzione di rilasciare pubblicamente un pacchetto e
vuoi dargli tutte le informazioni in maniera più che completa ed esaustiva sulla sua origine.
Ad essere onesto, comunque, in genere ho l’abitudine di compilare ugualmente tutto. Mi da una
sensazione di completezza e, alla fine, non costa niente.
Comunque, riassumendo: un file “composer.json” serve a definire l’identità di un pacchetto. Ci sono
poi delle parole chiave che vengono usate solo in certe circostanze: se vuoi vederne una lista completa
vai pure sul sito di Composer³, che contiene un sacco di informazioni utili.
Un’altra risorsa utile può essere questo cheat sheet⁴ online, soprattutto per i nuovi arrivati.
Torniamo al nostro pacchetto di prova: analizziamo linea per linea quello che abbiamo scritto nel
file di configurazione.
1 "name": "marvel/xmen",
Prima linea: nome del pacchetto. Se hai mai usato Github il formato del nome ti sarà familiare.
Altrimenti non saltare i prossimi paragrafi.
Il nome del pacchetto consiste in due parole, separate da uno slash (“/”). La prima parte rappresenta
il proprietario del pacchetto. Normalmente molti utenti di github usano il proprio username come
³http://getcomposer.org/”Composer”
⁴http://composer.json.jolicode.com/”ComposerCheatSheet”
Le Basi 15
nome del proprietario. In ogni caso puoi usare il nome che vuoi, non ci sono particolari regole a
riguardo.
La seconda parte invece è il nome stesso del pacchetto. Ti consiglio di mantenere un nome semplice
e descrittivo. Generalmente viene usato il nome del repository ma anche in questo caso sta a te
decidere cosa usare.
Andiamo avanti:
1 "description": "Mutants saving the world for people who hate them.",
Spiegazione piuttosto intuitiva: “description” serve a dare una breve descrizione delle funzionalità
del pacchetto. Anche in questo caso ricorda: keep it simple. Usa qualcosa di scorrevole e veloce.
Andrai nei dettagli, se necessario, nel file README.
Qui stiamo definendo delle keywords (parole chiave) per identificare il nostro pacchetto. Un po’ come
succede sui blog: ci sono delle parole chiavi e quando qualcuno cercherà il pacchetto in oggetto ci
arriverà anche grazie a queste parole.
1 "homepage": "http://marvel.com/xmen",
La voce “homepage” è importante ed utile soprattutto nei progetti open source, in cui serve un
riferimento per gli sviluppatori.
1 "time": "1963-09-01",
Probabilmente questa è una delle opzioni che vengono scritte di meno: secondo il cheat sheet, qui
va indicata la release date del pacchetto. In un sistema che gestisce il version control capirai bene il
perché della sua frequente latitanza.
In ogni caso, il formato da utilizzare è “YYYY-MM-DD”, oppure, se volete fare i precisi, “YYYY-MM-
DD HH:MM:SS”.
1 "license": "MIT",
Se il tuo pacchetto è soggetto ad una licenza particolare, questo è il momento per dirlo. Ad esempio,
Laravel usa la licenza MIT.
Le Basi 16
1 "authors": [
2 {
3 "name": "Stan Lee",
4 "email": "[email protected]",
5 "homepage": "http://marvel.com",
6 "role": "Genius"
7 }
8 ]
Arriviamo infine alla sezione “authors”, che identifica i vari autori del codice (in questo caso infatti
abbiamo un array di diversi possibili oggetti). Particolarmente utile nel caso di progetti collaborativi,
in cui ci sono più persone che lavorano allo stesso pacchetto.
Vediamo velocemente cosa contiene l’oggetto che abbiamo appena inserito nell’array:
Se hai difficoltà a capire un attributo come questo, chiudi libro e cambia carriera.
1 "email": "[email protected]",
Sicuramente è una buona prassi fornire l’indirizzo email: un riferimento in più nel caso qualcosa
vada storto ad un utente.
1 "homepage": "http://marvel.com",
Come prima, solo che il sito di riferimento è quello dell’autore e non del pacchetto.
1 "role": "Genius"
Qui definiamo il ruolo del singolo utente nella creazione del pacchetto. In questo caso, appunto,
Genius. Chiaro?
1 {
2 "name": "marvel/xmen",
3 "description": "Mutants saving the world for people who hate them."
4 "keywords": ["mutant", "superhero", "bald", "guy"],
5 "homepage": "http://marvel.com/xmen",
6 "time": "1963-09-01",
7 "license": "MIT",
8 "authors": [
9 {
10 "name": "Stan Lee",
11 "email": "[email protected]",
12 "homepage": "http://marvel.com",
13 "role": "Genius"
14 }
15 ],
16 "require": {
17
18 }
19 }
C’è una sezione, adesso, chiamata “require”. Sarà usata per indicare le varie dipenden.. ehm, mutanti
che comporranno la nostra squadra di X-Men.
Ecco quelli che includeremo nel team:
• Wolverine
• Cyclops
• Storm
• Gambit
1 "require": {
2 "xmen/wolverine": "1.0.0",
3 "xmen/cyclops": "1.0.1",
4 "xmen/storm": "1.2.0",
5 "xmen/gambit": "1.0.0"
6 }
Et voilà.
Una semplice lista in cui inseriamo il nome del pacchetto e la versione da usare. Osserviamo ad
esempio la dipendenza “Gambit”:
Le Basi 18
1 "xmen/gambit": "1.0.0"
Vero! Però dai, dov’è il problema? Se queste dipendenze saranno specificate in modo corretto,
Composer farà tutto automaticamente, arrivando fin dove sarà necessario arrivare pur di farvi
risparmiare del tempo prezioso. Senza considerare gli errori di distrazione che spesso si fanno in
questi casi!
Può capitare, poi, di non avere la necessità di una versione specifica di una dipendenza. Usando una
wildcard come questa preleverai l’ultima versione disponibile nel formato specificato:
1 "xmen/gambit": "1.0.*"
In questo esempio, Composer cercherà l’ultima versione disponibile che comincia per “1.0”. Tra
“1.0.0” e “1.0.1” sceglierà automaticamente la seconda.
Comodo, vero? C’è anche di più!
Ad esempio:
1 "xmen/gambit": ">1.0.0"
1 "xmen/gambit": "<1.0.0"
Se non dovesse bastare, infine, potrai anche specificare un range di versioni adatte:
Le Basi 19
1 "xmen/gambit": ">1.0.0,<1.0.2"
Qui si richiede una versione più recente della 1.0.0 e più vecchia della 1.0.2.
Ok, ok, basta numeri.
Supponiamo adesso che tu non voglia usare la versione stable della dipendenza che hai specificato.
Composer può anche cercare le singole branch di un pacchetto o di una dipendenza:
1 "xmen/gambit": "dev-branchname"
Ad esempio, in questo caso di seguito, cercheremo la branch di sviluppo del progetto Gambit su
Github.
1 "xmen/gambit": "dev-develop"
Per questioni di sicurezza, comunque, Composer non permette di usare versioni “non stabili” quando
si cercano le dipendenze per qualcosa. Bisognerà aggiungere il flag “minimum-stability” alla fine dei
requisiti, nel file “composer.json”.
“require”: { “xmen/gambit”: “dev-master” }, “minimum-stability”: “dev”
Considera inoltre che ci sono anche altre opzioni e valori disponibili per quanto riguarda versioni
minime e compagnia bella, però spiegarle significherebbe entrare nel profondo della questione e,
quindi, leggermente fuori strada.
Preferisco non allungare questo capitolo più di quanto non lo sia già con tutte queste informazioni.
Per ora, comunque, ti consiglio di dare un’occhiata alla [documentazione di Composer sulle ver-
sioni dei pacchetti](<http://getcomposer.org/doc/01-basic-usage.md#package-versions “Composer
documentation for package versions.”>) nel caso volessi saperne di più.
A volte ti potresti trovare in una situazione piuttosto comune: alcune dipendenze potrebbero non
essere necessarie in ambiente di produzione. Facciamo un esempio: per eseguire dei test vogliamo
utilizzare [Codeception](<http://codeception.com/ “Codeception testing framework for PHP.”>).
Di questi test, chiaramente, non ci sarà bisogno in fase di produzione, ma solo di sviluppo. Ecco che
arriva in nostro aiuto “require-dev”.
1 "require": {
2 "xmen/gambit": "dev-master"
3 },
4 "require-dev": {
5 "codeception/codeception": "1.6.0.3"
6 }
Le Basi 20
Tramite questa semplice direttiva abbiamo specificato di cosa abbiamo bisogno esclusivamente
in fase di sviluppo. Le dipendenze in questione verranno quindi scaricate solo nel caso in cui
se specificheremo “–dev” subito dopo l’istruzione dedicata all’installazione. A breve analizzeremo
meglio tutta la procedura di installazione, non temere se ti senti un attimo spaesato.
Avrai notato sicuramente che il formato della sezione “require-dev” è lo stesso di “require”. Ci sono
anche altre sezioni possibili nel tuo file composer.json con questo formato. Analizziamole.
La sezione “conflict” contiene una lista di pacchetti che potrebbero non lavorare bene insieme.
Composer non permetterà quindi l’installazione di tali pacchetti.
La sezione “replace” invece permette di specificare che un pacchetto x potrà essere usato come
rimpiazzo di un altro pacchetto y, eventualmente non disponibile. Una feature particolarmente utile
nel caso di dipendenze risultato di un fork che hanno funzionalità simili.
La sezione “provide” specifica tutti i pacchetti che sono stati forniti con la codebase del pacchetto
attualmente descritto dal file composer.json. In questo caso, ad esempio, il pacchetto “xmen/gambit”
sarà già stato incluso. Non sarà necessario quindi installarlo una seconda volta.
Infine ecco la sezione “suggest”. Come il nome, appunto, suggerisce qui possiamo specificare tutte
quelle dipendenze che, anche se non richieste, sono suggerite.
Direi che abbiamo visto un po’ tutto. La nostra visione di Composer ed il suo funzionamento è più
chiara: possiamo passare all’Auto Loading.
Auto Loading
Perfetto: abbiamo visto tutto quello che riguarda Composer e il management delle dipendenze.
Abbiamo visto come far scaricare questo o quel pacchetto, come suggerirlo… ma come fare poi
per includerlo nel nostro progetto?
Insomma, dopo tutto questo casino dobbiamo metterci a giocare con i “require()”? No, non abbiamo
tempo per queste cose.
Ancora una volta, infatti, Composer può gestire tutte queste cose per noi. Spiegandogli dove si
trovano le classi e quali metodi utilizzare, infatti, verrà creato un file di autoload automatico con
tutte le procedure necessarie.
Non è stupendo? Vediamo come si fa!
Aggiungiamo una nuova voce al nostro file “composer.json”:
Le Basi 21
1 "autoload": {
2
3 }
“autoload” sarà la sezione da usare per specificare tutte le configurazioni di caricamento automatico,
grazie anche alla parola chiave “files”:
1 "autoload": {
2 "files": [
3 "path/to/my/firstfile.php",
4 "path/to/my/secondfile.php"
5 ]
6 }
Il meccanismo di caricamento andrà a controllare i files specificati nell’array. Tutti i file verranno
cercati a partire dalla root directory del progetto.
Nel caso di tanti files, però, come fare? Non c’è problema, si usa “classmap”!
1 "autoload": {
2 "classmap": [
3 "src/Models",
4 "src/Controllers"
5 ]
6 }
1 <?php
2
3 namespace Xmen;
4
5 class Wolverine {
6 // ...
7 }
Utilizzando questa classe la si potrà adeguare alla nomenclatura standard PSR-0 nel momento in cui
avrà, come percorso, “XmenWolverine.php”. Si tratta semplicemente di far coincidere namespace e
nomi con cartelle e files.
Occhio anche alle maiuscole e le minuscole: il nome di una classe deve coincidere perfettamente con
il nome del file, stessa cosa per le cartelle e i namespace.
Chiaramente, nel caso di namespace a più livelli, il risultato sarà una struttura di cartelle: <?php
1 namespace Super\Happy\Fun;
2
3 class Time {
4 // ...
5 }
Il percorso di “Time” sarà quindi “SuperHappyFunTime.php”. Ottima prassi anche per organizzare
il proprio codice!
Adesso, vediamo come “passare” tutte queste informazioni a Composer, in modo tale da istruirlo.
Anche qui abbiamo due voci: una “chiave” (“SuperHappyFunTime”) e un “valore” (“src/”).
Vediamo nel dettaglio cosa succede. Stai attento, perchè questa parte è molto importante.
Innanzitutto, più che un array stavolta abbiamo un oggetto.
Il primo parametro presenta il doppio slash come separatore: non farci caso, dei due slash il primo è
il carattere di escape, necessario in JSON.
Il secondo valore è invece la directory che verrà mappata con il namespace specificato nella chiave.
Ora ricorda quello che sto per dirti: il secondo parametro non è la directory in cui il namespace si
trova. Il secondo paraemtro, (“src/”, in questo caso), è la directory da cui il namespace comincia ad
essere mappato.
Torniamo all’esempio precedente per capirci meglio.
Le Basi 23
1 <?php
2
3 namespace Super\Happy\Fun;
4
5 class Time {
6 // ...
7 }
Magari ti starai aspettando che il file “Time.php” si trovi in “src/”. In realtà non è così: come già detto
“src/” è solo il punto di partenza. Il percorso completo sarà infatti:
1 src/Super/Happy/Fun/Time.php
Ricorda sempre questo concetto, perchè un sacco di gente ci casca facilmente e passa un sacco di
tempo piangendo, a chiedersi cosa ha sbagliato nella propria vita.
Una domanda sorgerà spontanea, a questo punto: quale, in effetti, è il vantaggio di questo metodo?
Adesso arriva il bello: supponiamo di voler aggiungere un nuovo file, “Life.php” allo stesso
namespace. A differenza degli altri metodi precedenti, stavolta non avremo bisogno di rigenerare il
file di autoload!
Avendo specificato una “regola”, più che un percorso preciso, infatti, Composer saprà già cosa fare
senza ulteriori istruzioni aggiuntive.
Un altro consiglio che posso darti è quello di avere uno standard tutto tuo per la gestione dei percorsi,
oltre a quelli di cui abbiamo già parlato. Ecco come lavoro io, in genere (ti ci troverai sicuramente,
è uno standard piuttosto comune in giro):
1 src/ (Classi.)
2 tests/ (Unit/Acceptance tests.)
3 docs/ (Documentazione.)
4 composer.json
Adesso che abbiamo imparato tutto (o comunque quello che ci serve) sull’autoload, facciamo un
ulteriore passo avanti e analizziamo meglio l’installazione e l’uso di Composer.
Le Basi 24
Installazione
Lo so, è una cosa strana: lasciare l’installazione di Composer ed il suo utilizzo (basilare) come ultima
parte di questo lungo capitolo.
Il motivo, tuttavia, è presto detto: prima di lasciarti alla sua disarmante semplicità volevo spiegare
meglio la sua configurazione, in modo tale da capire un po’ meglio cosa succede “sotto il cofano”.
Una nota, prima di procedere: il metodo di installazione che descriverò qui di seguito copre tutti gli
ambienti di sviluppo unix based. Parliamo di Linux e di Mac OSX.
Spero comunque che Taylor modificherà al più presto questo capitolo in modo tale da aggiungerci
anche la procedura per Windows, visto che personalmente evito questo OS come se fosse una piaga.
Dunque: Composer è un’applicazione PHP. Per questo motivo dovrai avere installato PHP (ed il suo
CLI Client) prima di poterlo utilizzare in qualsiasi modo.
Controlliamo se c’è oppure no, tramite questo comando:
php -v
Se PHP c’è, ecco cosa vedrai (più o meno):
1 $ php -v PHP 5.4.4 (cli) (built: Jul 4 2012 17:28:56) Copyright (c)
2 1997-2012 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend
3 Technologies with XCache v2.0.0, Copyright (c) 2005-2012, by mOo
Occhio alle versioni: al di sotto di PHP 5.3.2, infatti, non potrai usare Composer fin quando non farai
tutti gli aggiornamenti necessari. Tra l’altro, senza PHP almeno alla versione 5.3.7, non potrai usare
neanche Laravel.
Puoi usare il comando “CURL” per scaricare Composer. Mac OSX ce l’ha di default e molte
distribuzioni di Linux hanno CURL nei loro repository, nel caso non sia stato già installato.
Forza, scarichiamo Composer!
curl -sS https://getcomposer.org/installer | php
Se l’installazione sarà andata a buon fine (verrai avvertito da un messaggio, non ti preoccupare)
adesso avrai un “composer.phar” nella directory delle applicazioni.
Si tratta dell’eseguibile da usare per lanciare Composer:
1 php composer.phar
1 echo $PATH
Ad ogni modo, già “/usr/local/bin” è più che accettabile come percorso nella maggior parte dei
sistemi. Nel muovere il file, inoltre, rimnominiamolo come “composer” per renderci le cose più
semplici.
Dopo aver installato Composer globalmente possiamo adesso usare il seguente semplicissimo
comando per vedere la stessa lista di comandi vista poco fa:
1 composer
Utilizzo
Supponiamo di aver creato un file “composer.json” nella directory del nostro pacchetto:
1 {
2 "name": "marvel/xmen",
3 "description": "Mutants saving the world for people who hate them."
4 "keywords": ["mutant", "superhero", "bald", "guy"],
5 "homepage": "http://marvel.com/xmen",
6 "time": "1963-09-01",
7 "license": "MIT",
8 "authors": [
9 {
10 "name": "Stan Lee",
11 "email": "[email protected]",
12 "homepage": "http://marvel.com",
13 "role": "Genius"
14 }
15 ],
16 "require": {
17 "xmen/wolverine": "1.0.0",
18 "xmen/cyclops": "1.0.1",
19 "xmen/storm": "1.2.0",
20 "xmen/gambit": "1.0.0"
21 },
22 "autoload": {
Le Basi 26
23 "classmap": [
24 "src/Xmen"
25 ]
26 }
27 }
Da linea di comando, quindi, usiamo il comando “install” per installare il pacchetto, le dipendenze
e configurare l’autoloader in modo adeguato.
composer install
Ecco quindi un possibile output:
Ricorda: questi sono dei pacchetti che non esistono! Li sto usando solo per farti capire la procedura.
Qualcosa però non ci torna… perché sono sette pacchetti, se ne ho messi in lista solo quattro? Beh, se
ci fai caso Wolverine ha a sua volta tre dipendenze, che Composer non ha certo scordato per strada!
Le Basi 27
Adesso ti starai chiedendo tutta questa roba dov’è andata a finire. Normalmente, Composer crea
una cartella “vendor” all’interno del nostro progetto, contenente tutti i sorgenti del pacchetto.
Quindi, ad esempio, il pacchetto “xmen/wolverine” potrà essere trovato in vendor/xmen/wolverine.
Oltre ai files troverai anche il file composer.json del pacchetto stesso.
Composer inoltre memorizza alcuni suoi files relativi all’autoloading in corrispondenza di “vendo-
r/composer”. Ad ogni modo non preoccuparti, non ti serviranno.
Et voilà! Il gioco è fatto.
Ora non ci rimane altro che cominciare ad includere dove serve il paccheto, tramite una sola semplice
istruzione:
1 <?php
2
3 require 'vendor/autoload.php';
L’istruzione di require del file “vendor/autoload.php” serve proprio a richiamare il file di autoload
del quale abbiamo parlato qualche sezione fa!
1 <?php
2
3 $gambit = new \Xmen\Gambit;
Non serviranno altre istruzioni: c’ha già pensato Composer a sistemarti tutto nel modo più adeguato.
Tu non dovrai fare altro che scrivere codice. Niente più errori di distrazione, niente più includes
scomodi.
Questo ovviamente nel caso tu abbia utilizzato PSR-0. Altrimenti, in caso di file o class-mapping,
dovrai prima eseguire l’istruzione “composer dump-autoload” prima di andare a scrivere del codice:
1 composer dump-autoload
Tutti i mapping verranno ricostruiti e verrà creato un nuovo file “autoload.php” con tutti gli ultimi
aggiornamenti.
Vediamo come fare adesso ad aggiungere una nuova dipendenza al nostro progetto: stavolta c’è
bisogno di “xmen/beast”.
Apriamo il nostro file “composer.json”:
Le Basi 28
1 {
2 "name": "marvel/xmen",
3 "description": "Mutants saving the world for people who hate them.",
4 "keywords": ["mutant", "superhero", "bald", "guy"],
5 "homepage": "http://marvel.com/xmen",
6 "time": "1963-09-01",
7 "license": "MIT",
8 "authors": [
9 {
10 "name": "Stan Lee",
11 "email": "[email protected]",
12 "homepage": "http://marvel.com",
13 "role": "Genius"
14 }
15 ],
16 "require": {
17 "xmen/wolverine": "1.0.0",
18 "xmen/cyclops": "1.0.1",
19 "xmen/storm": "1.2.0",
20 "xmen/gambit": "1.0.0",
21 "xmen/beast": "1.0.0"
22 },
23 "autoload": {
24 "classmap": [
25 "src/Xmen"
26 ]
27 }
28 }
Dopo averlo aggiornato non dovremo fare altro che eseguire l’istruzione “composer install”.
Il risultato sarà questo:
Avrai anche notato che Composer ha creato un file di nome “composer.lock”. Nella root directory
della tua applicazione. Di cosa si tratta?
“composer.lock” è un semplice file che contiene tutte le informazioni relative all’ultima esecuzione
di “install”.
In questo modo, la prossima volta che userai “composer install” per un qualsiasi aggiornamento il
sistema già saprà cosa è installato (ed in quale versione) e cosa no. Nci sarà bisogno di modificare il
file composer.lock manualmente, per cui non preoccuparti.
Adesso, invece, vediamo come si fanno ad aggiornare le dipendenze di un nostro progetto.
Supponiamo, infatti, che sia uscita la nuova versione della dipendenza “xmen/gambit”.
Nel file “composer.json” avevamo inserito la seguente linea:
1 "xmen/gambit": "1.0.*"
Se ricordi, grazie a questa wildcard avremmo preso l’ultima versione disponibile conforme al pattern
specificato. Guarda un po’, qualche giorno fa è uscita la versione 1.0.1.
Risolviamo subito il problema: usiamo “composer update” per aggiornare tutto l’aggiornabile!
Aspetta, aspetta, cos’è quel “(including require-dev)”? Dovresti ricordartelo: sono le dipendenze
relative all’installazione specificate a parte.
Se non ne hai bisogno, basta dirlo! Ci pensa Composer:
Le Basi 30
Come già detto prima (lo ripeto giusto per completezza) se invece vuoi installare anche le dipendenze
relative allo sviluppo basterà usare:
Infine, per concludere degnamente questo capitolo, la ciliegina sulla torta. Nel caso tu voglia avere
Composer sempre aggiornato, ricordati di usare “sudo”.
Stop! Per oggi basta. Abbiamo coperto tutte le basi di conoscenza necessarie a comprendere meglio
quello che sarà Laravel. Un capitolo sicuramente molto lungo, ma ti assicuro che sarà altrettanto
utile.
Andiamo avanti, adesso si comincia!
Architettura
Quando ho deciso di scrivere questo libro ho scelto di rendere Code Bright un’esperienza didattica
completa. Da qui la mia introduzione di questo capitolo.
Se non vedi l’ora di cominciare a scrivere codice allora salta direttamente questa parte. Tuttavia,
penso che comprendere l’architettura di tutto il sistema (a costo di annoiarti un po’) ti aiuterà
tantissimo.
In Laravel 3, il cosiddetto “IoC Container” (Inversion on Control Container) ha creato un sacco di
confusione tra gli sviluppatori. Oggi, in Laravel 4, è una parte del framework indispensabile, che
regge insieme tutti i pezzi.
Secondo me è il caso di capirne qualcosa di più, non è vero?
Il Container
In Laravel 4 il Container viene creato nelle fasi iniziali della chiamata al sistema. Viene creato nella
forma di un oggetto, chiamato “$app”.
Ora, una domanda: cosa ti viene in mente quando si parla di un “contenitore”? Anzi, la faccio terra
terra, partiamo dalla definizione che possiamo trovare su un qualsiasi dizionario:
Sostantivo - Contenitore. Un oggetto usato per (o capace di) contenere, a volte per
trasportare. Un esempio: una scatola.
Direi che come descrizione calza abbastanza bene quella del Container di Laravel. Certamente un
componente con questo nome ha il compito di “tenere delle cose al suo interno”.
Poi, dai, siamo seri, perché mai, altrimenti, dovrebbe chiamarsi “Container”?
Anche per quanto riguarda il discorso del “trasporto”, sicuramente un oggetto del genere nel
framework è utile quando bisogna richiamare questo o quel componente in giro nel codice.
Chiaramente, non è come una scatola di cartone. Magari può essere più un sistema di memorizza-
zione a “chiavi”, quello si (per capirci, un sistema a cui ad ogni chiave corrisponde un determinato
valore).
Pensa, ad esempio, ad un insieme di cassette di sicurezza: ad ogni codice corrisponderà una cassetta
ben precisa, quindi un certo contenuto.
Una volta che l’oggetto $app è stato creato puoi accederci ovunque, semplicemente usando il metodo
“app()”. In tutta onestà, molto probabilmente non avrai mai bisogno di usarlo. Capirai il perché a
breve.
Architettura 32
Mi stavo chiedendo… se dessimo un’occhiata dentro questo contenitore? Apriamo il file “app/rou-
tes.php” e modifichiamolo come segue:
1 <?php
2 // app/routes.php
3
4 var_dump($app);
5 die();
Come puoi notare, non ho avuto bisogno del metodo “app()”, in quanto $app è una variabile nel
global scope alla quale possiamo accedere direttamente.
Un oggetto immenso! Non ti spaventare, è normale: hai davanti ai tuoi occhi l’intero core del
framework, sarebbe strano se tutto questo non ci fosse. Un occhio attento avrà anche notato che
questo oggetto è un’istanza della classe “IlluminateFoundationApplication”.
Tutti i componenti di Laravel, infatti, risiedono nel namespace “Illuminate”. Si tratta del nome
in codice utilizzato nelle prime fasi di sviluppo per identificare il progetto e non abbiamo voluto
rimuoverlo. Rappresenta un po’ la nostra voglia di illuminare il mondo PHP!
Con un rapido sguardo al codice sorgente avrai anche notato che la classe “Application” in realtà
estende la classe “Container”. Guarda qui:
https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Application.php⁵
La classe “Container” è il luogo dove tutto accade. Implementa tra l’altro l’interfaccia “ArrayAccess”,
un’interfaccia speciale che permette di accedere agli attributi di un oggetto come se fosse un Array.
Immagina queste due chiamate:
1 <?php
2
3 $object->attribute = 'foo';
4 $object['attribute'] = 'foo';
“pezzi” vengono chiamati “Componenti” e tutti insieme vengono “messi” nel Container per poi
arrivare al framework nella sua definitiva interezza.
Se è la prima volta che usi Laravel, allora l’esempio seguente non avrà molto significato per te. Non
preoccuparti, tienilo comunque a mente anche se ti assicuro che più in la tutto ti sarà più chiaro.
1 <?php
2 // app/routes.php
3
4 $app['router']->get('/', function() {
5 return 'Thanks for buying Code Bright!';
6 });
In poche parole stiamo accedendo direttamente al routing layer del framework per creare una route
che risponda ad una richiesta http (GET) ben precisa. Una sintassi familiare per tutti gli utenti di
Laravel 3.
Abbiamo usato una sintassi uguale a quella che useremmo per accedere ad un qualsiasi elemento di
un array. Tutto questo grazie all’interfaccia “ArrayAccess”.
Volendo, infatti, potremmo anche fare così:
1 <?php
2 // app/routes.php
3
4 $app->router->get('/', function() {
5 return 'Seriously, thanks for buying Code Bright!';
6 });
Facades
Qualche mese fa il mio amico Kenny Meyers ha onorato il mio libro CodeHappy con una recensione
adorabile, su The Nerdary⁶. All’ultima conferenza su Laravel ci ha detto che ha avuto un sacco di
difficoltà con la pronuncia di alcune parole. Questa sicuramente non è facile.
⁶http://www.thenerdary.net/
Architettura 34
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return 'Thanks Kenny, we love you!';
6 });
Anche per i non addetti ai lavori, la sintassi si rivelava subito molto intuitiva. Un nome altamente
descrittivo per il componente ed altrettanto per il metodo “get” utilizzato.
Molti sviluppatori però non erano così entusiasti, a volte, di avere a che fare con i metodi statici:
potevano esserci svariati problemi in fase di testing, rallentando di molto i lavori, oltre che
complicarli.
Ora, questa situazione ha creato un bel po’ di problemi durante il design di Laravel 4. Da una parte
infatti volevamo continuare ad usare una sintassi eccezionalmente facile, dall’altra però volevamo
adottare tutte le best practices per realizzare un prodotto il più valido possibile.
Fortunatamente, Taylor è giunto in nostro soccorso con l’idea delle “classi facades”, il cui nome
deriva appunto dall’omonimo design pattern. Questa prassi infatti ci ha permesso di unire il meglio
di questi due mondi: componenti istanziati con metodi pubblici senza però dover rinunciare alla
comodità dei richiami “statici” ai quali siamo stati abituati.
Vediamo come funzionano.
In Laravel 4 ci sono una serie di classi dotati di un “alias” relativo al root namespace. Sono le
classi Facade di cui abbiamo parlato, ed estendono tutte la classe “IlluminateSupportFacadesFacade”.
Questa classe è molto, molto intelligente. Per spiegarti questa intelligenza farò qualche esempio
semplice.
Riprendiamo l’esempio visto prima, di routing.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return 'Thanks for Facades, Taylor!';
6 });
Architettura 35
In questo esempio la classe Facade è Route. Ogni classe di questo tipo viene collegata ad un’istanza
di un componente nel container. Quando un metodo statico di questa classe viene chiamato viene a
sua volta richiamato il relativo metodo pubblico all’interno del componente, nel container.
Questo vuol dire che il codice appena visto equivale a:
1 <?php
2 // app/routes.php
3
4 $app['router']->get('/', function() {
5 return 'Thanks for Facades, Taylor!';
6 });
Adesso ti starai chiedendo: perché tutto questo è così tanto dannatamente importante per noi?
Semplice: flessibilità.
Flessibilità
In che senso Flessibilità? Non nel senso puramente fisico, sicuramente. In questo caso, infatti, il
significato principale è la capacità di cambiare.
Capacità di cambiare quello che si ha, di cambiare gli oggetti… o meglio ancora, di cambiare i nostri
componenti.
Ok, arrivo subito al punto. Supponiamo che non ti piaccia il router che ha Laravel di default. Hai mille
idee su come migliorarlo e, soprattutto, sai come fare. Ti metti d’impegno, crei il tuo SuperRouter.
Ora, come fare a sostituirlo a quello originale che abbiamo realizzato?
Semplice: basta cambiarlo con quello che già c’è.
Precisamente, così:
1 <?php
2 // app/routes.php
3
4 $app['router'] = new SuperRouter();
In questo modo non solo potrai accedere direttamente al nuovo router che hai creato, ma potrai anche
continuare ad usare la classe facade “Route” che già esiste (che verrà collegata al nuovo componente
creato)!
In poche parole, nel momento in cui tu dovessi averne bisogno, puoi sostituire interi “pezzi” del
framework con altri, in modo tale da raggiungere la forma perfetta per il tuo progetto.
Architettura 36
Robustezza
Come già detto poco fa, Laravel è un insieme di più componenti. Ogni componente è totalmente (ed
esclusivamente)responsabile e competente riguardo la propria funzionalità. Tra l’altro, come se non
bastasse, alcuni componenti del framework possono addirittura lavorare da soli, staccati dal resto.
Per questo motivo, qualora dovessero servire, svariate copie dei singoli componenti possono essere
scaricati dalla pagina Illuminate organisation su GitHub⁷.
Lo stesso set di componenti, inoltre, è disponibile anche su Packagist⁸, in modo tale da poterli usare
agevolmente su un qualsiasi progetto che utilizzi Composer.
Allora, perché ho chiamato questa parte del capitolo “Robustezza”?
Presto detto: l’intero framework contiene, tra le varie cose, anche una suite di più 900 test e 1700
asserzioni.
Grazie a tutti questi test potremo accettare contributi ed effettuare cambiamenti sul framework, in
futuro, senza doverci preoccupare di aver creato nuovi problemi o, ancora peggio, rimosso per errore
delle funzionalità.
Scommetto che adesso cominci a vedere con occhi diversi Laravel, avendo una visione più ampia
del tutto. L’avevo detto che sarebbe servito!
Allacciate le cinture, tra poco si comincia a scrivere codice!
⁷https://github.com/illuminate
⁸https://packagist.org/
I Primi Passi
Laravel è un framework per il linguaggio PHP. Linguaggio che, nonostante non abbia una sintassi
molto simpatica, è facile da usare e in fase di deploy non da particolari problemi.
Molti siti famosi che magari usi anche tutti i giorni (Facebook, ad esempio) sono scritti in PHP.
Laravel non solo ti da dei componenti, una struttura e strumenti con cui lavorare, ma prova (spesso
con successo) a darti tutto quello che PHP non ha.
Nel creare Laravel, infatti, gli sviluppatori hanno cercato una sintassi gradevole, valida dal punto di
vista semantico capace di farlo competere con tutti i framework più importanti in circolazione. Tra
i suoi obiettivi, infatti, Laravel vuole rendere il PHP ancora più divertente da usare.
Nel corso del tempo il framework è stato adottato e utilizzato con successo, senza problemi, anche
in occasione di grandi progetti, sia di livello amatoriale che enterprise.
Che tu sia un programmatore alle prime armi o un esperto artigiano di codice, Laravel è qui per
aiutarti e, soprattutto, questo libro è qui per insegnarti come sfruttare al meglio ogni sua singola
sfaccettatura.
Vediamo innanzitutto cosa serve per utilizzare Laravel.
Requisiti
• Un Computer Leggere è bello, davvero, ma ti assicuro che usare un computer per provare gli
esempi di codice nella pratica ti faciliterà molto le cose.
• Un Server Web Un linguaggio server ha bisogno di un server. Laravel si basa su PHP, che è
un linguaggio server. Ergo, Laravel ha bisogno di un server web. Non importa quale usi, ma
so che buona parte della community si basa su Apache o Nginx. A te la scelta.
• PHP: Hypertext Preprocessor (5.3 o superiore) Laravel richiede almeno la versione 5.3 di
PHP. Questo perché molti dei suoi componenti richiedono features avanzate che sono state
implementate solo recentemente. Per sapere quale versione di PHP stai usando sul tuo server,
usa il comando “php -v” oppure il metodo “phpinfo()”.
• Un Server Database Nonostante non sia un requisito fondamentale, molti degli esempi di
questo libro interagiscono con un database. Per questo motivo ti consiglio di installarne uno
supportato dal connettore PDO. Puoi utilizzare MySQL, gratuito e flessibile, ma anche Oracle,
SQL Server, Postgres ed SQLite.
• Un Editor di Testo Ne avrai bisogno per scrivere un po’ di codice! Ce ne sono tanti: io ti
consiglio Sublime Text 2 che, anche se non è gratuito, è tremendamente utile. In ogni caso, tra
editor e IDE completi, ce ne sono a milioni. A te la scelta.
I Primi Passi 38
Questo è quanto, per ora: prima di cominciare a premere tasti a caso sulla tastiera e fare miracoli,
però, dobbiamo prima scaricare una copia del framework!
Installazione
Sinceramente non saprei se “Installazione” è il titolo davvero adatto per questa sezione. Comunque
sia, non ho trovato niente che si adattasse meglio.
Ti spiego subito perché: Laravel si trova su un repository ospitato da Github. Questo insieme di files
non è altro che una sorta di “template” per la tua applicazione, che dovrai copiare in locale sulla tua
macchina.
Utilizzeremo git per clonare il repository in locale, in una cartella a scelta del nostro webserver. Ecco
il comando:
Adesso avremo un template di applicazione già pronto nella cartella “my_project”. Alcuni, nel
riferirsi a questa directory, parlando di “app package”. Tienilo a mente perché non si sa mai, potrebbe
servirti in futuro.
Alcuni utenti delle vecchie versioni di Laravel, probabilmente, ricorderanno una cartella chiamata
“Laravel”, contenente tutti files “motore” del framework. Normalmente ci si riferisce a questa
parte come ai files del “core”. Tale cartella, oggi, non esiste più: il core infatti esisterà come un
pacchetto separato, come dipendenza (esatto, d’altronde cosa abbiamo parlato a fare di Composer?)
del “template package”.
A proposito di Composer: diamo un’occhiata al file “composer.json” nella cartella “my_project”.
1 {
2 "require": {
3 "laravel/framework": "4.0.*"
4 },
5 "autoload": {
6 "classmap": [
7 "app/commands",
8 "app/controllers",
9 "app/models",
10 "app/database/migrations",
11 "app/database/seeds",
12 "app/tests/TestCase.php"
13 ]
14 },
I Primi Passi 39
15 "scripts": {
16 "post-update-cmd": "php artisan optimize"
17 },
18 "minimum-stability": "dev"
19 }
Nota: le versioni delle varie dipendenze potrebbero cambiare dall’ultima modifica di questa sezione.
Il risultato, comunque, rimane lo stesso.
Come puoi vedere, il pacchetto dipende esclusivamente da “laravel/framework”, che contiene tutti i
componenti necessari a dare vita alla tua applicazione.
Dobbiamo ancora installarlo però! Un template rimane pur sempre solo un template: avviamo il
comando “install” di Composer per preparare tutto il progetto!
Ecco cosa ci apparirà appena digitato ed inviato il comando:
Anche qui potresti vedere valori leggermente diversi per quanto riguarda le versioni. Anche qui,
quindi, ti ripeto: non preoccuparti, è naturale.
Direi che è una bella lista di componenti, eh?
A cosa diamine servono tutti quanti? Come già detto in precedenza Laravel si appoggia sull’open
source e anche sui pacchetti distribuiti con Composer: sono le dipendenze del framework.
Ti do un consiglio, comunque: dai un’occhiata al sito di Packagist⁹. Scoprirai che non ha senso
reinventare la ruota se esiste già dell’ottimo codice testato!
Dato che hai letto il capitolo dedicato a Composer (perché lo hai letto, vero?) saprai anche che i core
files di Laravel adesso saranno installati e pronti nella cartella “vendor”.
Sicuramente non vorrai includere nel versioning anche il framework: abbiamo pensato a tutto e ci
abbiamo messo un “.gitignore” per sicurezza.
Ci siamo quasi: tocca configurare il server prima di partire!
La cartella “public”, inoltre, contiene i vari assets del tuo progetto: Javscript, CSS ed immagini. Adesso
sai dove andranno messi! Fondamentalmente, tutto quello a cui si può accedere pubblicamente dovrà
essere messo qui.
Cosa vuol dire tutto questo? Semplice: che la prima cosa da fare è assicurarsi che il server guardi nel
posto giusto, e guardare nel posto giusto per noi significa che il server deve usare come riferimento
di partenza la cartella “public” e non la root del nostro progetto!
Il nostro compito, quindi, sarà quello di dare al server le istruzioni adeguate.
Ehi, ho comprato un dominio dal nome veramente carino. Non ho già un URL adatto?
No, mi dispiace. Non funziona così. La vita non è tutta rose e fiori, dovresti saperlo. Il file di bootstrap,
“index.php”, si trova infatti nella cartella “public”.
Ecco, quindi, come apparirà una richiesta di default al nostro fantastico guestbook:
http://islifeallcakesandpies.com/index.php/guestbook
Beh… non è che sia proprio uno spettacolo quel segmento “index.php”, vero? Tra l’altro non è un
granchè neanche per quanto riguarda l’ottimizzazione per i motori di ricerca.
Si può fare qualcosa? Certamente.
Vediamo un po’ di configurazioni di esempio su vari webserver: saranno un’ottima traccia di
partenza per trovare quella definitiva e perfetta per la nostra situazione.
Cominciamo da Nginx.
Nginx
Nginx (si pronuncia come “Engine-X”) è un fantastico server web che ho cominciato ad usare da
poco. La scelta è stata semplice: è più veloce di apache e non richiede XML per la configurazione. Ti
assicuro, per molti è già abbastanza.
Su una distribuzione linux basata su Debian, come Ubuntu, puoi installare nginx e php attraverso il
comando:
Il secondo pacchetto è php5-fpm, un modulo FastCGI che permette ad nginx di eseguire codice PHP.
Su Mac questi pacchetti sono disponibili su Macports¹⁰. I pacchetti richiesti possono essere installati
con il comando:
¹⁰http://www.macports.org/
I Primi Passi 42
37 gi_script_name;
38 }
39
40 # We don't need .ht files with nginx.
41 location ~ /\.ht {
42 deny all;
43 }
44
45 }
Ora, non tutti i server sono uguali. Darti qui una configurazione generica adatta ad ogni situazione
per me sarebbe impossibile. Da questo momento dipende tutto da te: trova la configurazione perfetta,
tentativo dopo tentativo!
Se hai bisogno dei files generici di configurazione, comunque, l’ho condivisi su github in modo tale
da renderli disponibili per chiunque possa averne bisogno: daylerees/laravel-website-configs¹¹.
Come ho già detto, nginx è una gran bella scelta. C’è anche altro in circolazione, però. Vediamo
come sistemare quello di cui abbiamo bisogno su Apache.
Apache
Il web server Apache può essere installato su sistemi basati su Debian utilizzando il seguente
comando:
Ecco invece una configurazione VirtualHost che si adatta a buona parte delle possibili situazioni: ci
servirà ad ottenere lo stesso risultato che abbiamo raggiunto poco fa con Nginx.
1 <VirtualHost *:80>
2
3 # Host that will serve this project.
4 ServerName app.dev
5
6 # The location of our projects public directory.
7 DocumentRoot /path/to/our/public
8
9 # Useful logs for debug.
10 CustomLog /path/to/access.log common
11 ErrorLog /path/to/error.log
¹¹http://github.com/laravel-website-configs
I Primi Passi 44
12
13 # Rewrites for pretty URLs, better not to rely on .htaccess.
14 <Directory /path/to/our/public>
15 <IfModule mod_rewrite.c>
16 Options -MultiViews
17 RewriteEngine On
18 RewriteCond %{REQUEST_FILENAME} !-f
19 RewriteRule ^ index.php [L]
20 </IfModule>
21 </Directory>
22
23 </VirtualHost>
• app/
• bootstrap/
• vendor/
• public/
• .gitattributes
• .gitignore
• artisan
• composer.json
• composer.lock
• phpunit.xml
• server.php
app
I Primi Passi 45
Innanzitutto, la cartella “app”. Viene usata come home per qualsiasi progetto ed ospita tutte le classi
relative alle funzionalità del tuo progetto, files di configurazione e così via. Si tratta di una cartella
molto importante: per questo a fine capitolo la analizzeremo più nel dettaglio.
bootstrap
• autoload.php
• paths.php
• start.php
La directory “bootstrap” contiene pochi files, tutti relativi alle procedure di startup e caricamento del
framework. Il file “autoload.php” contiene la maggior parte di queste procedure: non avrai bisogno
di toccarlo e se proprio dovrai farlo, procedi con la massima attenzione!
Il file “paths.php” serve a costruire un array di tutti i percorsi comunemente usati dal framework.
A volte potrà essere necessaria qualche modifica (in caso di alterazione delle directory dei vari
pacchetti).
Il file “start.php” contiene invece tutte le procedure di startup del framework. Non entrerò nel
dettaglio per evitarti una confusione inutile. Ricordati, però, che qui troverai tutte le impostazioni
per specificare l’ambiente in cui ti trovi.
Per farla breve, i contenuti della cartella “bootstrap” dovrebbero essere toccati solo da sviluppatori
più esperti, in una fase avanzata del loro apprendimento. Se sei agli inizi, quindi, non ti curar di loro,
ma guarda e passa.
vendor
La cartella “vendor” contiene tutti i pacchetti di Composer utilizzati dall’applicazione. Di conse-
guenza è proprio qui che si trova il codice di Laravel.
public
• packages/
• .htaccess
• favicon.ico
• index.php
• robots.txt
La cartella “public” è l’unica che, in condizioni ottimali, sarà esposta verso il web. Vediamola nel
dettaglio.
La cartella “packages” conterrà gli assets di eventuali pacchetti di terze parti. In questo modo potrai
sempre tenere ben distinto il codice dalle altre risorse, senza comunque far entrare in conflitto gli
assets esistenti con quelli dei pacchetti, magari inseriti in un secondo momento.
I Primi Passi 46
Laravel 4, inoltre, viene distribuito con un file “.htaccess” standard per Apache. Contiene alcune
configurazioni e direttive di default che nella stragrande maggioranza delle situazioni andranno
bene e non daranno problemi.
Nel caso non dovessi averne bisogno, ovviamente, cancellalo!
Di default, inoltre, molti browser cercheranno nella directory principale un file “favicon.ico”. Si
tratta del file icona che viene mostrato nelle tab del browser quando ti trovi sul tuo sito. Ora, molti
browser creano dei log inutili quando questa icona non viene trovata: anche per questo motivo
abbiamo inserito un file “favicon.ico” vuoto, per evitare qualsiasi problema.
Chiaramente, rimpiazzalo quando vuoi.
C’è poi il file “index.php”. Altri non è se non il file che fa partire tutto, attraverso il quale passa
ogni singola richiesta e successivamente richiama a catena tutto il resto del framework. Insomma:
la magia comincia qui.
Non cancellarlo: non sarebbe un granchè carino.
C’è inoltre un file “robots.txt” che, di default, abilita tutti gli hosts di default. Facci quello che vuoi,
è solo un file inserito di default.
.gitattributes
Laravel viene distribuito con alcuni valori di configurazione per git. Attualmente git è la scelta più
popolare per quanto riguarda il versioning ma non è la sola scelta: nel caso tu non voglia usarlo
basta rimuovere questi files.
.gitignore
Il file “.gitignore” contiene alcune informazioni per git, riguardanti quali cartelle devono essere
esonerate dal controllo. Normalmente questo file contiene gli includes sia per la cartella “vendor”
che per la cartella “storage”.
artisan
Artisan è un eseguibile PHP che viene usato per lavorare con il tool da linea di comando. Contempla
un sacco di comandi utili, scorciatoie e funzionalità legate al framework. Ne discuteremo, comunque,
più avanti, in un capitolo dedicato.
composer.json and composer.lock
I file “composer.json” e “composer.lock” sono file legati a Composer, del quale abbiamo visto un po’
tutto nel capitolo “Le Basi”. Nel caso tu voglia saperne di più, quindi, trovi tutto quello che ti serve
li!
phpunit.xml
Questo file ha il compito di gestire la configurazione per PHP Unit, uno tra i framework più famosi
dedicati al testing di un’applicazione PHP. Parleremo di testing più in la, in un capitolo dedicato
all’argomento!
server.php
I Primi Passi 47
@todo: Ho ancora bisogno di qualche ricerca per spiegare meglio cosa fa questo file. Aggiornerò
quanto prima anche questa parte.
La Directory “Application”
Eccoci qui, come promesso.
Dunque, è qui che la tua applicazione prende vita. Il novantanove percento del lavoro sul tuo progetto
si svolgerà qui: un buon motivo per vedere tutto nel dettaglio.
Innanzitutto vediamo cosa c’è in questa cartella:
• commands/
• config/
• controllers/
• database/
• lang/
• models/
• start/
• storage/
• tests/
• views/
• filters.php
• routes.php
commands
La cartella “commands” contiene tutti i comandi creati ad hoc per Artisan. Come se non fosse già
sufficiente tutto quello che ti permette di fare, infatti, potrai specificare tu stesso nuovi comandi ad
hoc per il tuo progetto!
Non è fantastico?
config
In questa cartella verranno piazzati (anzi, già ci sono) tutti i vari files di configurazione della tua
applicazione. In Laravel ogni singola opzione di un progetto è messa in un array associativo, in cui
ogni chiave corrisponde ad un determinato valore. In più, per maggior comodità, si potranno creare
delle sottocartelle, da usare a scelta in base all’ambiente di lavoro del momento (sviluppo, test, deploy
e così via).
controllers
Qui c’è poco da girarci intorno: questa cartella si occuperà di contenere tutti i tuoi controller. I
controller vengono utilizzati per gestire la logica delle applicazioni, una sorta di “collante” di tutti le
I Primi Passi 48
parti di cui l’applicazione stessa è composta. Questa directory viene aggiunta di default tra le classi
in autoload del file “composer.json”, per comodità.
database
Nel momento in cui deciderai di usare un database come metodo di memorizzazione dei dati della
tua applicazione, questa directory verrà usata per tutti quei file che ne comporranno lo schema.
Partendo dalla struttura a tutti quei metodi di seeding con dati di esempio.
lang
La cartella “lang” si occuperà di contenere tutti i file php legati alle lingue e alla localizzazione
della tua applicazione. Un sistema di sottocartelle in base alla region ti permetterà di tradurre la tua
applicazione, facilmente, in più lingue.
models
La cartella “models” si occupperà di contenere tutti i vari model della tua applicazione.
Sorpresa, eh?
Scherzi a parte, più in la parleremo dei models, a cosa servono e, soprattutto, con cosa interagiscono.
Nota: anche questa cartella è tra quelle in autoload nel file “composer.json”.
start
Se la cartella “bootstrap” contiene tutti i dettagli relativi all’avvio del framework, questa cartella
invece contiene tutti i dettagli relativi all’avvio dell’applicazione.
Come sempre, abbiamo inserito un insieme di valori di default che si adatteranno bene per le prime
prove.
storage
La cartella “storage” è stata creata per memorizzare qualsiasi cosa debba essere scritta su disco. Di
conseguenza, prima di lavorarci, controlla tutti gli eventuali permessi, mi raccomando!
tests
Arriviamo alla cartella “tests”, contenente tutti gli unit ed acceptance test per l’applicazione che stai
creando. Di default PHP Unit cercherà questa cartella.
views
La cartella “views” si occuperà di contenere tutti i componenti e templates visuali dell’applicazione.
Per comodità (ed esempio) abbiamo inserito una view “hello” di default.
filters.php
Il file “filters.php” conterrà tutti i filtri della tua applicazione. Se non hai idea di cosa siano, è normale:
siamo all’inizio! Li vedremo più in la, tranquillo.
routes.php
I Primi Passi 49
Infine, il file “routes.php”. Conterrà tutte le routes della tua applicazione. Sai già cosa sono? Bene.
Non sai cosa sono? Tranquillo: ne parleremo molto presto.
Precisamente, nel prossimo capitolo! Let’s go!
Routing di Base
C’era Una Volta…
… una lunga, lunga strada, nella zona più selvaggia di… ehm, facciamo Australia. Si, dovrebbe
andare. Abbi pazienza, questa sezione ha bisogno di un po’ di immaginazione, quindi seguimi. Allora:
il nostro personaggio ha un nome: Browsy Mc Request.
Browsy camminava lungo questa strada, da qualche parte nel paese dei canguri. Non era del posto,
si era perso senza avere idea di dove fosse e, soprattutto, di dove andasse. In ogni caso lui sapeva
bene dove voleva arrivare: un suo amico gli aveva parlato, qualche tempo prima, del Ricovero di
Jason Lewis per i marsupiali baffuti.
Ad un certo punto, Browsy vide in lontananza quello che sembrava a tutti gli effetti un bivio: tutto
eccitato affrettò il passo, desideroso di trovare qualche indicazione più precisa. Una volta giunto
guardò ovunque, ma non c’era niente di niente. Nessuna indicazione, nessun indizio su cosa fare o
dove andare.
Per fortuna, in quel preciso istante, avvenne la magia: si dice che uno sviluppatore web apparve
all’improvviso, piantò un cartello in quel terreno inospitale e sparì così come arrivò, in un batter
d’occhio.
Browsy era confuso: perchè quello strano essere voleva aiutarlo? Soprattutto, cosa ci diamine ci
faceva uno sviluppatore web nell’arido entroterra australiano?
Ad ogni modo, il nostro eroe cominciò ad esaminare le nuove indicazioni: “A sinistra troverai la
Stalla di Bob, mentre a destra il Ricovero di Jason.”
Che fortuna! Esattamente quello che cercava! Browsy visse felice e contento, soddisfatto di aver
raggiunto il suo obiettivo e altrettanto felice di aver conosciuto quello straniero tanto generoso
da aiutarlo quando più ne aveva bisogno. Si promise che, prima o poi, l’avrebbe adeguatamente
ripagato.
Morale della favola? Senza un buon web developer Browsy non avrebbe combinato un bel niente.
Proprio come nella storia, senza una giusta logica e un adeguato indirizzamento (si, sei tu il web
developer, inutile che guardi altrove), la tua applicazione non funzionerà mai e poi mai.
Routing di Base
Dopo questo atipico preambolo direi che possiamo cominciare a vedere in cosa consiste una qualsiasi
richiesta a Laravel.
Routing di Base 51
1 http://domain.com/my/page
In questo esempio stiamo usando il protocollo http (usato da molti browser) per accedere alla nostra
applicazione ospitata su “domain.com”. Il “my/page” identifica invece la “porzione” di URL che viene
collegata ad una determinata logica.
Come avviene tutto questo? Le “routes” sono definite nel file “app/routes.php”. Aprilo e crea la stessa
route anche tu, come nell’esempio seguente:
1 <?php
2 //app/routes.php
3
4 Route::get('my/page', function() {
5 return 'Hello world!';
6 });
Ora, prova ad inserire l’indirizzo della tua applicazione nel browser (molto probabilmente al posto
di “domain.com” dovrai usare “localhost”) e guarda cosa succede.
Se non hai sbagliato niente dovresti vedere la scritta “Hello world!” in un caldo e meraviglioso Times
New Roman. Non è difficile, vero? Certo che no, ma guardiamo meglio tutta la dichiarazione della
route e come viene strutturata.
Le routes vengono sempre dichiarate utilizzando la classe “Route”: si trova li, poco prima del “::”. Il
nome del metodo, “get()”, identifica invece il verb utilizzato dalla richiesta. In questo caso, infatti,
stiamo usando il metodo get() perchè la richiesta è una HTTP GET, verso un certo indirizzo.
Sicuramente saprai (altrimenti adesso lo sai) che tutte le richieste fatte da un browser sono di un
certo tipo ben definito. Normalmente, nella grande maggioranza dei casi, userai la tipologia “GET”.
Giusto per farti un esempio, ogni volta che digiti un indirizzo nella barra degli indirizzi del browser
e premi invio stai effettuando una richiesta GET.
Tuttavia non esiste solo GET: avrai sentito anche di POST, che viene usato per effettuare una richiesta
insieme ad alcuni dati aggiuntivi. Normalmente questo tipo di richiesta viene effettuata quando
compiliamo un form e premiamo invio.
Ecco, di preciso, tutti i verb HTTP che puoi gestire:
Routing di Base 52
1 <?php
2 //app/routes.php
3
4 Route::get();
5 Route::post();
6 Route::put();
7 Route::delete();
8 Route::any();
Tutti questi metodi accettano gli stessi parametri: sentiti libero di testarli, giocarci e farci pratica.
Adesso rimaniamo nelle basi, ma più in la vedremo come gestire in maniera consona tutti questi
verb, in modo tale da fare quello che viene detto “RESTful Routing”.
Alla fine della lista avrai notato anche il metodo “Route::any()”. Si tratta, però, di una specie di “jolly”,
in quanto può essere usato per gestire una richiesta di qualsiasi tipo ad un determinato URL.
Senza divagare troppo, però, torniamo all’esempio pratico di prima.
Ecco il codice:
1 <?php
2 //app/routes.php
3
4 Route::get('my/page', function() {
5 return 'Hello world!';
6 });
Il primo parametro che abbiamo passato al metodo “get()” è l’URL al quale vogliamo associare una
determinata logica. In questo caso l’URL è “my/page”.
Il secondo parametro, come puoi ben immaginare, è il blocco di istruzioni associate all’URL. Quella
che stiamo usando qui è una cosiddetta “funzione anonima”, o “Closure”. Queste “Closure” sono
semplicemente delle funzioni senza nome (anonime, appunto) che possono essere assegnate ad una
variabile.
Ecco, infatti, un altro possibile modo per avere lo stesso risultato del codice precedente:
Routing di Base 53
1 <?php
2 //app/routes.php
3
4 $logic = function() {
5 return 'Hello world!';
6 }
7
8 Route::get('my/page', $logic);
In questo caso stiamo assegnando alla variabile $logic il valore della closure, per poi passare questa
variabile al metodo “Route::get()”.
In questa precisa istanza, comunque, Laravel eseguirà questa closure solo ed esclusivamente se la
richiesta effettuata sarà di tipo GET. Chiaramente puoi definire quante routes vuoi:
1 <?php
2 //app/routes.php
3
4 Route::get('first/page', function() { return 'First!'; });
5 Route::get('second/page', function() { return 'Second!'; });
6 Route::get('third/page', function() { return 'Potato!'; });
Dopo averle specificate e salvato il file “routes.php” prova a visitare questi indirizzi:
1 http://domain.com/first/page
2
3 http://domain.com/second/page
4
5 http://domain.com/third/page
A questo punto ti starai già chiedendo: “come faccio a mappare la home page della mia applicazio-
ne?”.
Vero, non dovremmo mai lasciare fuori questo URL:
1 http://domain.com
1 <?php
2 //app/routes.php
3
4 Route::get('/', function() {
5 return 'In soviet Russia, function defines you.';
6 });
Ehi, perchè hai usato lo slash? Non ho nessuno slash se scrivo semplicemente “www.domain.com”!
Vero, ma una route contenente solo ed esclusivamente uno slash davanti risponderà ad entrambe
queste richieste:
1 http://domain.com
2
3 http://domain.com/
A volte, infatti, può capitare che davanti al dominio venga messo uno slash (volontariamente o no,
o magari mentre si arriva da un link esterno, non si sa mai).
Dopo questo primo esempio, perchè non proviamo a ricostruire insieme una gerarchia di prova?
Immaginiamo un bel sito di libri e giornali. Vogliamo una home page, una pagina generica per i
libri, una pagina per ogni genere, una pagina generica per i giornali ed una pagina per ogni genere
di giornale.
Qualcosa come:
1 /
2
3 /libri
4
5 /fiction
6
7 /scienze
8
9 /romanzo
10
11 /giornali
12
13 /gossip
14
15 /tecnologia
In realtà è una struttura piuttosto “povera”, ma come primo esempio andrà più che bene!
Vediamo come riprodurre questa struttura con le routes:
Routing di Base 55
1 <?php
2 //app/routes.php
3
4 //home page
5 Route::get('/', function() {});
6
7 //routes for the books section
8 Route::get('/books', function() {});
9 Route::get('/books/fiction', function() {});
10 Route::get('/books/science', function() {});
11 Route::get('/books/romance', function() {});
12
13 //routes for the magazines section
14 Route::get('/magazines', function() {});
15 Route::get('/magazines/celebrity', function() {});
16 Route::get('/magazines/technology', function() {});
Con questa collezione di routes abbiamo riprodotto tranquillamente la gerarchia che ci eravamo
prefissati.
Però, ora che ci penso… non ci sarebbe un modo per semplificare ancora di più le cose?
Certo che c’è! Ora vediamo come usare i parametri per le nostre routes, in modo tale da aderire ai
principi del DRY (Don’t Repeat Yourself).
Routes e Parametri
Definire dei parametri può farci risparmiare un sacco di tempo. In realtà , non appena comincerai
a fare qualcosa di leggermente più complesso dell’esempio visto poco fa, ne avrai tremendamente
bisogno, te l’assicuro.
Per fortuna, con Laravel è facile definire per ogni route uno o più parametri.
Si tratta di una cosa più facile a farsi che a dirsi, per cui passiamo subito ad un esempio:
Routing di Base 56
1 <?php
2 //app/routes.php
3
4 //routes for the books section
5 Route::get('/books', function() {
6 return 'Books index.';
7 });
8
9 Route::get('/books/{genre}', function($genre) {
10 return "Books in the {$genre} category.";
11 });
In questo esempio abbiamo eliminato, con una sola e semplice route, la necessità di crearne una per
ogni genere di libro!
Come abbiamo fatto? Semplicemente usando il segnaposto “{genre}”. Il valore quindi che verrà
inserito verrà automaticamente assegnato a {$genre} che invece si troverà nel codice della closure
da te specificata.
Non ti trattengo oltre.
Prova ad andare all’indirizzo:
1 http://domain.com/libri/thriller
Non ci fermiamo qui: possiamo ottimizzare ancora di più la struttura e ridurre il numero di routes
necessarie. A volte, infatti, un parametro può essere anche opzionale: nelle routes di Laravel per
rendere un parametro opzionale basta inserire il punto interrogativo “?” alla fine del nome del
parametro.
Ad esempio:
Routing di Base 57
1 <?php
2 //app/routes.php
3
4 //routes for the books section
5 Route::get('/books/{genre?}', function($genre = null) {
6
7 if ($genre == null)
8 return 'Books index.';
9
10 return "Books in the {$genre} category.";
11 });
Se non sarà specificato un genere nell’URL alla variabile $genre verrà assegnato NULL. Se questa
variabile, quindi, sarà uguale a NULL (il controllo viene fatto con un semplice if) allora verrà
mostrato un determinato testo, altrimenti un altro.
Un NULL, però, non sempre è benvenuto. Non c’è problema: possiamo specificare anche un valore
di default in caso nell’URL un parametro non venga specificato.
Vale lo stesso discorso del NULL, solo che al suo posto ci andrà un valore definito a priori.
1 <?php
2 //app/routes.php
3
4 //routes for the books section
5
6 Route::get('/books/{genre?}', function($genre = 'Crime') {
7 return "Books in the {$genre} category.";
8 });
1 http://domain.com/libri
Se hai già usato Laravel 3 sono sicuro non avrai difficoltà . In caso contrario ti consiglio di fare
pratica, fare tanti esperimenti per diventare totalmente padrone del meccanismo.
Tra l’altro il routing non è solo questo: c’è ancora molto da vedere e, dopo aver coperto le basi,
torneremo sull’argomento.
Adesso però facciamo un altro piccolo passo avanti, e vediamo cosa offre Laravel per quanto riguarda
l’output di una richiesta.
Output e Response
Quando qualcuno ti fa una domanda, a meno che tu non sia dell’umore adatto o la domanda non
abbia senso, probabilmente rispondi. Giusto?
Le richieste ad un server web funzionano in maniera identica. Il browser richiede qualcosa e si
aspetta qualcos’altro in ritorno.
Nel capitolo precedente abbiamo visto come queste risposte prendono forma nella closures che
andiamo a specificare:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return 'Ciao!'; });
Qui, ad esempio, la stringa “Ciao!” è la risposta al browser. Per una dimostrazione va più che bene,
certo, ma come fare se volessimo mandare in output, al posto di una stringa, un po’ di HTML?
Insomma, succede spesso se costruiamo un’applicazione web.
Magari così?
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return '<!doctype html>
6 <html lang="en">
7 <head>
8 <meta charset="UTF-8">
9 <title>Alright!</title>
10 </head>
11 <body>
12 <p>This is the perfect response!</p>
13 </body>
14 </html>';
15 });
Scherzo. Non vorrai mica mandare in output l’HTML così? Non fare mai una cosa del genere. Se lo
hai fatto, non dirlo in giro.
Per nostra fortuna, Laravel contempla svariati oggetti “Response” che permettono di gestire l’output
della nostra applicazione in modo molto semplice.
In questo capitolo li vedremo nel dettaglio uno alla volta.
Views
Le Views non sono altro che la parte visuale della nostra applicazione. Avrai sicuramente sentito
parlare del pattern MVC (Model View Controller).
Ecco perché si chiamano Views.
Ora, in cosa consiste di preciso una view? Altro non è che un template, un file che contiene del
testo. Questo testo, molto probabilmente, sarà HTML e la view avrà un’estensione “.php”. Le view,
in genere, sono situate all’interno della cartella “app/view”.
Cominciamo subito a fare qualche esempio pratico:
Niente di eccezionale: un po’ di HTML memorizzato nel file “app/views/simple.php”. Ora creiamo
una View.
Si, ok, ma non intendevo “creiamo un’altra view”. In realtà volevo dire “usiamo il metodo “make()”
per creare il giusto oggetto da mostrare all’utente.
Prima di andare in output, infatti, dobbiamo richiamare il template che abbiamo appena creato da
qualche parte, in modo tale da collegarla ad una certa logica.
Guarda qui:
Output e Response 60
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return View::make('simple');
6 });
Esatto! Il metodo “View::make()” crea una nuova istanza di un oggetto response di classe View. Il
parametro che viene passato è il nome della view stessa, senza estensione.
Avrai notato anche che non c’è tutto il path: ho semplicemente specificato “simple” e non
“app/views/simple”. Laravel, infatti, va a cercare la view direttamente nella cartella “app/views”.
Ora, se controlli il codice nella closure noterai che viene ritornato un oggetto di tipo View. È
importante tenere a mente che c’è un “return”, visto che Laravel si servirà di questo valore per
elaborare l’output adeguato.
Adesso prova! Vai all’URL della route e vedi cosa succede!
Più in la vedremo anche come creare tipologie diverse di template che lavorino con oggetti View in
maniera più avanzata, in modo tale da renderti la vita ancora più semplice. Per ora, come già detto
prima, rimaniamo alle basi.
1 <?php
2
3 // app/routes.php
4 Route::get('/{squirrel}', function($squirrel)
5 {
6 $data['squirrel'] = $squirrel;
7 return View::make('simple', $data);
8 });
Nella route qui sopra abbiamo usato il parametro “$squirrel”, aggiungendolo nell’array $data. Subito
dopo abbiamo preso l’array data e lo abbiamo passato, come secondo parametro, al metodo “make()”.
Output e Response 61
In questo modo, tutto quello che c’è nell’array $data potrà essere usato direttamente nella view!
Ti vedo un po’ confuso. Tranquillo, sei qui per imparare!
Dunque, l’array $data (che in realtà puoi chiamare come vuoi, basta passare il nome giusto al metodo
“make()”) è un array associativo le cui chiavi, nella view, corrispondono a nomi di variabili che puoi
richiamare.
Proviamo con questo esempio. Innanzitutto creo un array con due elementi:
1 <?php
2 array('name' => 'Taylor Otwell', 'status' => 'Code Guru');
1 <?php
2 echo $name; // output: 'Taylor Otwell'
3 echo $status; // output: 'Code Guru'
In poche parole, la chiave dell’array “name” diventa, nel template, la variabile “$name”. Chiaramente,
puoi inserire qualsiasi tipo di variabile all’interno dell’array che viene passato, per cui sperimenta
qualsiasi soluzione ti viene in mente, dagli interi, passando per gli array multidimensionali fino ad
arrivare agli oggetti.
Torniamo adesso all’esempio precedente: usiamo la variabile $squirrel nel template di prova che
abbiamo creato.
Ora, se visitiamo l’URL “/gray” riceveremo una pagina che ci accoglierà con la frase: “I wish I were
a gray squirrel!”.
Si tratta di un concetto semplice, ma molto, molto potente.
Poco fa ho detto che ci sono svariati tipi di oggetti di response da poter usare. In certi casi infatti,
più che mostrare qualcosa, potresti avere la necessità di reindirizzare l’utente verso una pagina ben
precisa.
Vediamo come fare, attraverso l’oggetto “Redirect”.
Redirect
Un “Redirect” è un oggetto speciale che ti permette di mandare l’utente da una parte all’altra della
tua applicazione, verso altre routes.
Come fare? Ecco qui un esempio al volo:
Output e Response 62
1 <?php
2 // app/routes.php
3
4 Route::get('first', function() {
5 return 'Prima route.';
6 });
7
8 Route::get('second', function() {
9 return 'Seconda route.';
10 });
Fino a qui tutto familiare. Sono due routes, ognuna legata ad una logica sua.
Ora, supponiamo di voler indirizzare l’utente, nel momento in cui decide di andare sulla prima route,
direttamente sulla seconda. Ecco come fare:
1 <?php
2 //app/routes.php
3
4 Route::get('first', function() {
5 return Redirect::to('second');
6 });
7
8 Route::get('second', function() {
9 return 'Seconda route.';
10 });
Nella prima route abbiamo inserito il richiamo al metodo “to()” dell’oggetto “Redirect”. Come unico
parametro abbiamo passato l’URI per indirizzare la chiamata alla route “second”.
Se ora provi a visitare “/first” verrai indirizzato automaticamente verso “/second”. Questo perché,
ricevendo l’oggetto Redirect (ricorda il return, mai ometterlo), Laravel ha elaborato l’istruzione di
conseguenza e reindirizzato l’utente verso la destinazione specificata.
È una tecnica molto comoda, magari quando una risorsa non viene trovata o c’è qualche problema
e vuoi rimandare l’utente verso una pagina “sicura”.
Vediamone un esempio di utilizzo pratico: un qualsiasi login.
Output e Response 63
1 <?php
2
3 //app/routes.php
4 Route::get('books', function() {
5 if (Auth::guest())
6 return Redirect::to('login');
7
8 // Show books to only logged in users.
9 });
In questo esempio ci stiamo assicurando che tutti gli utenti non autenticati (vedremo l’oggetto Auth
più avanti, anche se questa sintassi è tutto tranne che complicata) vengano rimandati alla pagina di
accesso.
Response e Personalizzazioni
Entrambe le classi “View” e “Redirect” derivano dall’oggetto “Response”. L’oggetto “Response” è
un’istanza di una classe che può essere passata a Laravel come risultato di una closure (in una route)
oppure di una action (nel caso di un controller, che vedremo successivamente). In questo modo ad
ogni chiamata il framework può dare una risposta adeguata.
Gli oggetti Response, normalmente, contengono un corpo, un codice di status e gli header HTTP.
Ad esempio, il body di una qualsiasi View è il codice HTML che vi è contenuto. Lo status code di
un Redirect, invece, è 301, e così via.
Laravel usa tutte queste informazioni per creare un risultato da rimandare, a fine elaborazione, al
browser.
Facile intuire, quindi, che non siamo strettamente legati al concetto di View e Redirect. Laravel è
anche (anzi, soprattutto) flessibilità: possiamo creare un nostro oggetto Response per soddisfare le
nostre necessità.
Vediamo un esempio.
1 <?php
2 //app/routes.php
3
4 Route::get('custom/response', function() {
5 return Response::make('Hello world!', 200);
6 });
In questo esempio abbiamo usato il metodo “Response::make()” per creare un nuovo oggetto. Il primo
parametro consiste nel contenuto (o corpo) della risposta, mentre il secondo parametro altri non è
che il codice di stato HTTP servito.
Se non hai nessuna esperienza con questi codici immagina che ogni singola richiesta che un browser
fa corrisponde ad una risposta ben precisa. Questa risposta ha un codice, numerico, che spiega
velocemente cosa è successo.
Qualche esempio? Il codice 200, che equivale ad un “ok, risorsa trovata, nessun problema”, oppure il
codice 302, il quale indica che il redirect richiesto è stato effettuato. O magari, ancora, il 418: “Sono
una teiera.”. Si, hai letto bene: all’IETF sono dei burloni.
Per non parlare del maledetto errore 404! Quante volte l’avrai incontrato? Beh, ora sai che 404 è
il codice per indicare una risorsa che non è stata trovata nel momento in cui è stata richiesta dal
browser.
Tornando al nostro esempio, comunque, tutto quello che farà sarà servire il contenuto “Hello World”,
con uno codice di stato 200.
Arriviamo quindi alle informazioni dell’header HTTP. In questo caso gli headers sono una collezione
di chiavi-valori che rappresentano informazioni (a volte meno, a volte più) importanti per il browser
che riceve la risposta.
In genere vengono usati per indicare di che formato è il risultato ottenuto, informazioni varie sul
periodo di caching e così via. Ovviamente, con Laravel possiamo tranquillamente definire tutti gli
header che vogliamo.
Largo al codice!
1 <?php
2 // app/routes.php
3
4 Route::get('custom/response', function() {
5 $response = Response::make('Hello world!', 200);
6 $response->headers->set('chiave', 'valore');
7 return $response;
8 });
1 <?php
2
3 var_dump($response->headers);
Il metodo “set()” ci permette quindi di aggiungere un nuovo elemento all’insieme, specificando prima
la chiave e poi il valore desiderato.
A quel punto basta effettuare il return dell’oggetto creato, come abbiamo fatto prima. Il browser
riceverà i nuovi header e potremo usare questa ulteriore informazione.
Detta così, però, sembra un po’ astratta la cosa, vero? Che ne dici di un bell’esempio più “concreto”?
Ti accontento.
Immaginiamo di voler creare un’applicazione che, al posto di restituire un output HTML, restituisca
un output in Markdown. Normalmente un browser non saràcapace di interpretare il Markdown:
supponiamo però di avere un’applicazione sul nostro pc che lo consenta tranquillamente.
Ora, per indicare il contenuto di una response andremo a modificare gli header. Precisamente, il
“Content-Type”. Questa chiave header è usata da tutti i browser ed indica il formato della risposta
che arriva.
1 <?php
2 //app/routes.php
3
4 Route::get('markdown/response', function() {
5 $response = Response::make('***some bold text***', 200);
6 $response->headers->set('Content-Type', 'text/x-markdown');
7 return $response;
8 });
Creiamo l’oggetto, specifichiamo gli header adeguati e, come prima, un semplice return. In
pochissimi secondi abbiamo servito alla nostra applicazione una risposta in formato markdown!
Semplicissimo!
Ora… visto che ormai siamo tutti amici, posso svelarvi un segreto?
Vieni più vicino.
L’oggetto Response, in realtà, non appartiene a Laravel.
Non scaldarti! Stai sereno! Come già detto in precedenza, Laravel usa svariati (e robusti) componenti
del progetto Symfony 2. L’oggetto Response è uno di questi, in quanto eredita molte cose dall’oggetto
omonimo derivato dal componente Symfony HTTPFoundation.
Output e Response 66
Cosa significa? Beh, se vai a dare un’occhiata alla documentazione delle API per questo oggetto
troverai una quantità quasi spaventosa di metodi extra che non sono neanche coperti nella
documentazione di Laravel.
Un mondo in un altro mondo: pensa a quante cose puoi fare.
Se dovessi averne bisogno (o se dovessi essere particolarmente curioso ora che te l’ho detto) ecco un
link alla documentazione dell’oggetto Response¹².
Osservando bene la pagina noterai che questa classe ha un attributo chiamato “$headers”. Esatto! Si
tratta proprio dell’attributo che abbiamo visto poco fa nell’esempio!
Visto che l’oggetto Response di Laravel deriva da questo oggetto, sentiti libero di esplorare in lungo
e in largo (oltre che di utilizzare) tutti i vari metodi offerti dalla documentazione di Symfony.
Prendiamo un metodo a caso: “setTtl()”. Cosa dice?
Bene: questo metodo serve ad impostare il valore di “time-to-live” per la cache condivisa. Ora, non è
che sia un granchè esperto nello specifico, ma mi viene da pensare che sia un metodo particolarmente
utile quando ci sia da decidere per quanto una certa informazione è considerata utile prima di essere
scartata.
Diamogli un valore. Pare che accetti come parametro un intero, che consiste poi nel numero di
secondi del time-to-live. Diamogli 60 secondi di vita.
¹²http://api.symfony.com/2.2/Symfony/Component/HttpFoundation/Response.html“TheSymfonyResponseObjectâ€
Output e Response 67
1 <?php
2 //app/routes.php
3
4 Route::get('our/response', function() {
5 $response = Response::make('Bond, James Bond.', 200);
6 $response->setTtl(60);
7 return $response;
8 });
Servita la nostra risposta, il time-to-live verrà impostato a sessanta secondi. Puoi ben immaginare
quindi quale sia la complessità (e semplicità d’uso) di tutto questo sistema.
In ogni caso però ti do un consigli: non sentirti confuso per questa grandezza. Sarà raro infatti
dover fare operazioni del genere: normalmente non dovrai fare altro che usare View, Redirect e tutte
strutture già pronte.
Perchè, allora, ho deciso di farti vedere anche questa personalizzazione? Perché è importante, almeno
secondo me, avere un’idea completa delle cose.
Scorciatoie
Laravel è tuo amico. Davvero.
No, non è vero… c’è di più. Laravel ti ama.
Giuro!
Ha un sacco di scorciatoie per renderti la vita più facile, e non vede l’ora che tu cominci ad usarle.
1 <?php
2
3 // app/routes.php
4 Route::get('markdown/response', function() {
5 $data = array('iron', 'man', 'rocks');
6 return Response::json($data);
7 });
1 ["iron","man","rocks"]
1 <?php
2 //app/routes.php
3
4 Route::get('file/download', function() {
5 $file = 'path_to_my_file.pdf';
6 return Response::download($file);
7 });
1 <?php
2
3 //app/routes.php
4 Route::get('file/download', function() {
5 $file = 'path_to_my_file.pdf';
6 return Response::download($file, 418, array('iron', 'man'));
7 });
Qui serviremo il nostro file con un codice “418” (Sono una teiera) ed un header contenente la coppia
“iron=man”.
Direi che siamo alla fine del capitolo. Probabilmente è un po’ più lungo di quello che avevo previsto
inizialmente ma la cosa non mi dispiace.
Come sempre, ti consiglio di fare incessantemente pratica e sperimentare varie soluzioni, in modo
tale da capire bene le classi che abbiamo visto insieme e l’importanza dell’oggetto Response, base di
tutto l’output.
Filtri
Ricordo, qualche anno fa, che Jesse O’Brien organizzò un evento privato, dove lui e i suoi amici
avrebbero potuto guardare una partita del team di hockey del posto contro i Laravel Pandas.
Ora, non voglio stare qui a parlare del risultato: se Jesse mi avesse ascoltato i suoi Knights oggi
sarebbero chissà dove.
Comunque, l’evento venne ospitato a Londra, all’Hoster Hut. Un posto tra l’altro famoso per essere
molto accogliente per tutti i canadesi. Solo per loro, però.
Volete sapere cosa successe? Beh, ad intervalli irregolari, un americano alla volta volava dalla
finestra. Non è stata poi così amichevole come partita, a pensarci bene.
Che caratteraccio.
Ad ogni modo, fu allora che Jesse capì che bisognava mettere un po’ di gente alla porta, per fare
selezione e vedere chi passava e chi no.
Quindi? Jesse implementò dei filtri. A certe condizioni si poteva passare, altrimenti no.
Sarebbe interessante fare qualcosa del genere anche con le nostre applicazioni, vero? Bene, vediamo
come proteggere le nostre routes create con Laravel, attraverso i Filtri (Filters).
Filtri di Base
Cos’è un filtro? Semplicemente, un certo set di regole ed azioni applicate ad una route. Istruzioni
che vanno eseguite prima o dopo della route alla quale vengono “agganciate”.
Pensateci bene: usare dei filtri prima di una route può alterare l’intero andamento di un’applicazione,
nel caso alcune condizioni non vengano soddisfatte.
Un esempio su tutti? Pensate alla protezione di un’area riservata di un qualsiasi sito.
In ogni caso, come sempre, un esempio vale più di mille parole. Vediamo un po’ di codice in azione!
Creiamo una semplice view:
1 <?php
2 //app/filters.php
3
4 Route::filter('birthday', function() {
5 if (date('d/m/Y') == '25/01/1990') {
6 return View::make('birthday');
7 }
8 });
Ecco il nostro primo filtro. Laravel fornisce un file “app/filters.php” come location generica per tutti
i nostri filtri. In realtà, comunque, possiamo metterli dove vogliamo.
Tramite il metodo “Route::filter()” andiamo a creare un nuovo filtro. Il primo parametro è un nome,
che verrà utilizzato (lo vedremo a breve) dal sistema come identificativo del filtro stesso. Il secondo
parametro, invece, è una closure.
Questa closure verrà richiamata nel momento in cui il filtro a cui è associata sarà utilizzato. Un po’
come abbiamo fatto con le routes qualche capitolo fa, ricordi?
In questo caso specifico, nel momento in cui la data odierna sarà quella del mio compleanno verrà
mostrata una view di auguri. Verrà mostrata una view al posto di un’altra (se ricordi quello che ho
detto poco fa, infatti, un filtro viene agganciato prima o dopo una route).
Questo dovrebbe già darti un’idea della potenza di uno strumento del genere, vero?
Stai già immaginando un sacco di cose, vero? Dai, fai pure la tua risata da malvagio di turno mentre
ti vengono le prime idee.
Muahahhuahuahuahauhuahuahuahuahuahuahuahah!
1 <?php
2 //app/filters.php
3
4 Route::filter('birthday', function() {
5 if (date('d/m') == '25/01') {
6 return View::make('birthday');
7 }
8 });
Osserviamo meglio la closure: abbiamo una condizione ed una risposta. Nel caso la data sia “25/01”
allora la closure ritornerà in output la view del compleanno. In caso contrario, invece, la logica della
route continuerà normalmente.
Chiaramente, se non agganciamo prima questo filtro a qualche route sarà impossibile eseguirlo!
Ricordi quando ti ho spiegato che puoi specificare come secondo parametro durante la definizione
di una route i filtri collegati?
Non te l’ho detto? Ah.
Ok, scusa.
Vediamo subito un esempio di definizione di una route, appena “diversa” dal solito:
1 <?php
2 //app/routes.php
3
4 Route::get('/', array(function() {
5 return View::make('hello');
6 });
Come puoi notare ho inserito all’interno di un array la definizione della closure. Funziona esatta-
mente come prima, tranquillo. Adesso andremo ad agganciare il filtro fatto poco fa.
1 <?php
2
3 //app/routes.php
4 Route::get('/', array( 'before' => 'birthday', function() {
5 return View::make('hello');
6 });
Non ho fatto altro che creare un altro elemento all’interno dell’array. In questo caso, l’indice “before”
spiega al framework quali filtri eseguire PRIMA della chiamata alla closure.
Precisamente, questa volta, ho detto alla route di eseguire il filtro “birthday” prima della logica nella
closure.
Filtri 73
Nel momento in cui andrò a richiamare quella route, quindi, se dovesse essere il venticinque gennaio
vedrò una schermata di auguri, altrimenti un semplice saluto.
Questo perché, in base alle condizioni logiche del filtro e della route, avrò in base ad una certa
condizione un output diverso.
Perfetto, adesso non dobbiamo fare altro che aspettare il venticinque gennaio e vedere la schermata
di auguri!
Scherzo. Cambiamo al volo la condizione del filtro in modo tale da vedere cosa succede. Basta mettere
al posto della condizione da verificare un caro e vecchio “true”.
1 <?php
2 //app/filters.php
3
4 Route::filter('birthday', function() {
5 if (true) { return View::make('birthday'); }
6 });
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'after' => 'birthday', function() {
5 return View::make('hello');
6 }));
Se sei sveglio l’avrai già notato, ma devo far notare una cosa: una volta che viene richiesto il return
di un oggetto Response non viene mostrato altro.
In questo caso, quindi, richiamare la view di auguri dopo aver comunque mostrato la view “hello” è
senza senso.
In filtri eseguiti dopo le route, però, possono esserci un sacco di cose: operazioni di pulizia post-
esecuzione, logging di sorta e così via.
Filtri Multipli
Laravel non ti vuole porre dei limiti: puoi applicare, infatti, tutti i filtri che vuoi ad una route.
Vediamo subito qualche esempio di codice in azione.
Innanzitutto, agganciamo più filtri prima dell’esecuzione:
Filtri 74
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => 'birthday|christmas', function() {
5 return View::make('hello');
6 });
Abbiamo collegato sia il filtro “birthday” che “christmas”. Non spiegherò nel dettaglio cosa fa il filtro
“christmas”, credo tu abbia un quoziente intelletivo sufficiente per capirlo.
Il simbolo pipe “|” viene usato per separare i vari filtri usati nella stringa collegata all’indice “before”.
Ovviamente, l’ordine di esecuzione sarà quello di comparsa nella stringa: in questo caso, quindi,
verrà richiamato prima il filtro “birthday” e poi “christmas”.
Se dovesse risultarti più comodo, comunque, potrai usare anche un array al posto di una stringa:
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => array('birthday', 'christmas'), function() {
5 return View::make('hello');
6 });
Non so quale sia il tuo stile, ma onestamente preferisco molto di più gli array alla semplice stringa.
Continuando su questa strada, comunque, è facile immaginare che la cosa vale anche per i filtri
successivi alla route:
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => 'birthday', 'after' => 'christmas',
5 function() {
6 return View::make('hello');
7 });
Filtri e Parametri
Come ogni funzione PHP, anche i filtri possono accettare dei parametri. Caratteristica particolar-
mente utile, che permette di evitare ripetizioni inutili.
Via con gli esempi:
Filtri 75
1 <?php
2 //app/filters.php
3
4 //before
5 Route::filter('test', function($route, $request) {});
6
7 //after
8 Route::filter('test', function($route, $request, $response) {});
Sgamato! Beh, si, sono due filtri, tra l’altro con lo stesso nome. Tuttavia, la tua perplessità è ben
fondata. Dunque: Laravel offre diversi parametri, in base alla tipologia di filtro (“before” ed “after”).
Entrambe queste tipologie ricevono “$route” e “$request” come parametri.
Adesso, usando var_dump($route) veniamo a conoscenza del fatto che $route è un’istanza della
classe IlluminateRoutingRoute. Se hai letto i capitoli precedenti ricorderai che “Illuminate” è il nome
in codice del progetto dei componenti di Laravel. La classe “Route” rappresenta il percorso usato dal
layer di routing, appunto.
Come avrai ben immaginato, il parametro successivo è invece l’oggetto relativo alla richiesta effet-
tuata. Precisamente, un’istanza della classe “IlluminateHttpRequest”. Contiene tutte le informazioni
relative ad URL, dati aggiuntivi e così via.
Il filtro “after”, invece, riceve un parametro in più: un’istanza dell’oggetto “Response” ritornato dalla
route stessa. Un parametro, quindi, che cambierà in base al flusso del programma stesso.
So cosa stai pensando: “gran bella idea, avanzata tra l’altro, ma se volessi definire io dei parametri
personalizzati?”.
Nulla di più semplice!
Innanzitutto dobbiamo aggiungere un placeholder nella dichiarazione della closure (dopo i parametri
di default che abbiamo appena visto), come segue:
1 <?php
2 //app/filters.php
3
4 Route::filter('birthday', function($route, $request, $date) {
5 if (date('d/m') == $date) {
6 return View::make('birthday');
7 }
8 });
Filtri 76
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => 'birthday:12/12', function() {
5 return View::make('hello');
6 });
Il parametro passato al nostro filtro è quello subito dopo i due punti (colon, in inglese) “:”. Se vuoi
vedere in azione il sistema provalo! Eventualmente cambia la data in input con quella odierna e
guarda il filtro entrare magicamente in azione.
Chiaramente puoi usare tutti i parametri di cui hai bisogno: nessun limite anche qui:
1 <?php
2 //app/filters.php
3
4 Route::filter('birthday', function($route, $request, $first, $second, $third) {
5 return "{$first} - {$second} - {$third}";
6 });
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => 'birthday:foo,bar,baz', function() {
5 return View::make('hello');
6 });
Inoltre, esattamente come capita per un qualsiasi metodo o funzione, puoi specificare anche dei
valori di default, da prendere in considerazione nel caso non venga passato niente.
Filtri 77
1 <?php
2 //app/filter.php
3
4 Route::filter('example', function($route, $request, $optional = 'Yep!') {
5 return $optional;
6 });
Adesso fermati un attimo. Fai pratica con tutte queste cose, prendi tutto il tempo di cui hai bisogno
per rendere tuoi questi concetti e, quando avrai fatto abbastanza prove, vai avanti.
Filtri (Classi)
Le Closures sono una gran cosa. Fanno risparmiare un sacco di tempo e vanno alla grande. Tuttavia,
si può fare ancora qualcosa di più. Non essendo istanziabili è infatti difficile gestirne i test in modo
efficiente.
Per questo motivo, ogni feature di Laravel che utilizza o richiede delle Closure permette un’alterna-
tiva. Una classe dedicata in PHP, appunto. Ovviamente istanziabile.
Prima di creare la classe, però, abbiamo bisogno di un “posto” dove metterla. Più che posto, ad essere
precisi, una cartella: la chiameremo “filters” e la metteremo nella cartella “/app”.
Dovremo, comunque, modificare il file “composer.json” in modo tale da avere tutto sotto controllo.
1 "autoload": {
2 "classmap": [
3 "app/commands",
4 "app/controllers",
5 "app/models",
6 "app/filters",
7 "app/database/migrations",
8 "app/database/seeds",
9 "app/tests/TestCase.php" ]
10 }
Perfetto.
Ora creiamo la nuova classe/filtro “birthday”.
Filtri 78
1 <?php
2 //app/filters/Birthday.php
3
4 class BirthdayFilter {
5
6 public function filter($route, $request, $date){
7
8 if (date('d/m') == $date) {
9
10 return View::make('birthday');
11
12 }
13
14 }
15
16 }
Ho chiamato questa classe “BirthdayFilter”. Sia chiaro: non c’è bisogno del suffisso “Filter”, ma a me
piace per una questione di ordine mentale.
Se c’è una cosa obbligatoria, però, è il metodo “filter”. Funziona esattamente come una closure, quindi
non ripeterò un’altra volta ciò che il filtro fa.
Più che altro, vediamo come fare in modo che il sistema riconosca questo filtro come quello da usare.
Innanzitutto dobbiamo creare una alias per il nostro filtro. Useremo il metodo “Route::filter()”.
Tuttavia, questa volta, anzichè passargli una closure come secondo parametro, gli daremo in pasto
una semplice stringa.
Così:
1 <?php
2 //app/routes.php
3
4 Route::filter('birthday', 'BirthdayFilter');
Come puoi intuire, il secondo parametro questa volta indica la classe da istanziare ed usare per il
filtro.
Ora che il filtro è stato creato non dobbiamo fare altro che collegarlo alle route che ne hanno bisogno,
esattamente come abbiamo fatto prima.
Filtri 79
1 <?php
2 //app/routes.php
3
4 Route::get('/', array( 'before' => 'birthday', function() {
5 return View::make('hello');
6 }));
Ricorda: prima di fare qualsiasi test dovrai eseguire il comando “composer dump-autoload”, visto che
sei andato a modificare il file composer.json. Una volta richiamato il comando Laravel sarà capace
di cercare i filtri nell’apposita cartella.
Filtri Globali
Se dai un’occhiata all’interno del file /app/filters.php avrai sicuramente notato due filtri piuttosto
strani. Sono i filtri “globali”, che vengono eseguiti prima e dopo TUTTE le richieste dell’applicazione.
1 <?php
2 //app/filters.php
3
4 App::before(function($request) {
5 //
6 });
7
8 App::after(function($request, $response) {
9 //
10 });
Funzionano esattamente come qualsiasi filtro, con l’unica differenza che vengono applicati ad ogni
route, di default. Proprio per questo motivo non dovrai specificare il parametro “$route” visto in
precedenza, oltre a non avere neanche bisogno di collegarli a mano.
Filtri di Default
Sempre nel file app/filters.php ci sono altri filtri: alcuni di questi sono stati creati per te, in modo
tale da evitarti un po’ di lavoro di routine.
Vediamoli:
Filtri 80
1 <?php
2 //app/filters.php
3
4 Route::filter('auth', function() {
5 if (Auth::guest())
6 return Redirect::guest('login');
7 });
8
9 Route::filter('auth.basic', function() {
10 return Auth::basic();
11 });
12
13 Route::filter('guest', function() {
14 if (Auth::check())
15 return Redirect::to('/');
16 });
Tutti questi filtri sono relativi al livello di autenticazione di Laravel. Questo livello, che verrà
analizzato nel dettaglio più avanti, consente di proteggere una o più routes in modo tale da evitare
a chiunque di accedere alla tua applicazione.
Il quarto filtro, invece, è quello relativo al cross site request forgery.
1 <?php
2 //app/filters.php
3
4 Route::filter('csrf', function() {
5 if (Session::token() != Input::get('_token')) {
6 throw new Illuminate\Session\TokenMismatchException;
7 }
8 });
Può essere usato dalle tue route per fare in modo che la provenienza dell’utente sia l’applicazione
stessa. Una garanzia di sicurezza necessaria in molte situazioni.
Filtri e Pattern
Non vuoi attaccare i tuoi filtri di volta in volta, route dopo route? Tranquillo, siamo qui anche per
assecondare la tua pigrizia. Lo strumento di cui hai bisogno è un filtro “pattern”.
Di cosa si tratta? Anzichè specificare il filtro nella singola route, dovrai usare il metodo “Rou-
te::when()” come segue:
Filtri 81
1 <?php
2 //app/routes.php
3
4 Route::when('profile/*', 'birthday');
In questo modo, ad ogni route che comincia con “profile/” verrà agganciato il filtro “birthday”. Un
sistema assolutamente comodo, se si pensa a quanto lavoro ci può risparmiare.
Controller
Creare un Controller
Nel capitolo dedicato al routing abbiamo studiato come collegare un determinato URL ad una
closure. Una sorta di “pezzettino” di logica, da definire insieme ad altri per creare la nostra
applicazione nella sua struttura.
Tuttavia, quando si parla di applicazioni leggermente più complesse, i Controller vengono di gran
lunga preferiti alle più semplici routes.
Cos’è un Controller? Semplicemente, una classe utilizzata per ospitare la logica di un progetto.
Un’applicazione è composta da uno o più controller ed ogni controller è a sua volta formato da
uno o più metodi, detti anche “actions”. Per fare un paragone, le actions possono essere paragonate
alle singole routes.
Per farti capire meglio la differenza, comunque, passerò subito ad un esempio. Costruiamo insieme
il primo controller:
1 <?php
2 //app/controllers/ArticleController.php
3
4 class ArticleController extends BaseController {
5
6 public function showIndex() {
7 return View::make('index');
8 }
9
10 public function showSingle($articleId) {
11 return View::make('single');
12 }
13
14 }
Dato che queste due “logiche” fanno parte dello stesso contesto (gli articoli, appunto), viene naturale
raggrupparli insieme. Il raggruppamento consiste nella classe “ArticleController”.
Ovviamente, puoi chiamare il controller come vuoi: l’importante è che estenda la classe “BaseCon-
troller”. Per il resto non serve altro: penserà a tutto Laravel. In genere questa nomenclatura è quella
che uso più spesso, perciò l’ho riproposta qui.
Il nostro controller verrà creato e poi salvato in una cartella precisa: “app/controllers”. Se ci fai caso,
tra l’altro, questa cartella fa parte di quelle in autoload gestite direttamente da Laravel. Nel caso tu
voglia metterla altrove fai pure, ma ricorda di modificare il file “composer.json”!
Facciamo un passo avanti, adesso, e diamo un’occhiata ai metodi interni al controller. Anche in
questo caso puoi chiamarli come vuoi: è una mia consuetudine far cominciare con “show” tutti i
metodi che mostrano in output una pagina web.
L’importante, comunque, è che questi metodi siano “public”, in modo tale che Laravel sia capace di
instradarli come fossero delle routes. Ricordatelo! Nel momento in cui un metodo viene dichiarato
“private” non sarà possibile instradarlo. Potresti aver bisogno di renderlo privato lo stesso per logiche
“interne”. In quel caso non preoccuparti e procedi pure.
Analizziamo il primo metodo, “showIndex()”. Supponiamo rappresenti, come già detto, una lista di
articoli.
Ha un che di familiare, vero? Vediamo come potremmo strutturare una route dalla stessa funziona-
lità:
1 Route::get('index', function() {
2 return View::make('index');
3 });
L’unica differenza tra le due soluzioni è che, se il metodo del controller ha un nome, la route presenta
una closure (per definizione) anonima.
Tuttavia, non è l’unica differenza: abbiamo imparato, nel capitolo dedicato alle routes, che possiamo
collegare un URL ad una logica ben precisa. Nella route precedente, ad esempio, l’indirizzo “/index”.
Nel controller invece non si accenna ad un URL da collegare, da nessuna parte!
Come si fa ora?
Controller 84
1 <?php
2
3 //app/routes.php
4 Route::get('index', 'ArticleController@showIndex');
Per collegare un controller ad un URI bisogna definire una nuova route, nel file “app/routes.php”.
Esatto, lo stesso metodo “Route::get()” visto precedentemente. Il secondo parametro, però, non è più
una closure.
La stringa immessa infatti consiste in due sezioni, separate dal carattere “@”. Guardiamo ancora un
attimo il codice:
1 <?php
2 //app/controllers/ArticleController.php
3
4 class ArticleController extends BaseController {
5
6 public function showIndex() {
7 return View::make('index');
8 }
9
10 public function showSingle($articleId) {
11 return View::make('single');
12 }
13
14 }
1 ArticleController@showIndex
A questo punto associare un URL ad una action ben precisa è semplice: basta fare altre associazioni.
Controller 85
1 <?php
2 //app/routes.php
3
4 Route::post('article/new', 'ArticleController@newArticle');
So che siete intelligenti e avete già capito cosa ho fatto: stavolta ho collegato la action “newArticle”
all’URL “article/new”, usando però il verb “POST”.
Bene.
Ok, adesso arriva un’altra cosa da sapere. Puoi assegnare anche un namespace al tuo controller.
Laravel continuerà a lavorare tranquillamente senza problemi!
Ecco come cambia il nostro controller:
1 <?php
2 //app/controllers/Article.php
3
4 namespace Blog\Controller;
5
6 use View;
7
8 use BaseController;
9
10 class Article extends BaseController {
11
12 public function showIndex() {
13
14 return View::make('index');
15
16 }
17
18 }
A questo punto, però, dobbiamo aggiornare anche la dichiarazione nella route. Ecco fatto:
1 <?php
2 //app/routes.php
3
4 Route::post('index', 'Blog\Controller\Article@showIndex');
Semplice, vero?
Ad ogni modo sappi che, comunque, puoi mettere i tuoi controller in sottocartelle o organizzarti
come meglio credi. L’importante, comunque, è che Composer sia capace di arrivare ai files.
Controller 86
Controller RESTful
Laravel offre soluzioni: fino a qui l’abbiamo capito. Oltre alle soluzioni, però, offre anche opzioni. I
Controller RESTful sono un esempio lampante di questo concetto.
Abbiamo già visto, in precedenza, che possiamo decidere a qualche verb di richiesta HTTP assegnare
un metodo di routing. A volte, tuttavia, può far comodo “tenere insieme” il verb e la logica ad esso
collegata, senza diversificarla in fase di routing.
Vediamo un po’ cosa offre Laravel: cominciamo a fare qualche modifica al nostro controller “Article”.
1 <?php
2 // app/controllers/Article.php
3
4 namespace Blog\Controller;
5
6 use View;
7 use BaseController;
8
9 class Article extends BaseController {
10
11 public function getCreate() {
12 return View::make('create');
13 }
14
15 public function postCreate() {
16 // Handle form data.
17 }
18
19 }
Stavolta l’intenzione è quella di gestire la creazione di un articolo del blog. Avrai notato, infatti, che
il metodo “Create()” viene preceduto da un “get” e un “post”. Sono i verb HTTP da usare in questo
caso. Praticamente:
1 GET /create
2 POST /create
Ti starai chiedendo, tra le varie cose, come instradare tutte queste richieste. Lo abbiamo già visto
prima, ma date le circostanze non avrebbe molto senso: i verbi da usare li abbiamo già specificati
nella classe!
Ecco un altro metodo che potrebbe piacerti:
Controller 87
1 <?php
2 //app/routes.php
3
4 Route::controller('article', 'Blog\Controller\Article');
Con questo singolo metodo abbiamo creato una route per ogni azione nel controller. Il primo
parametro è l’URL di base: in questo caso “article”. Una sorta di prefisso per le operazioni che
verranno effettuate (ad esempio “article/create”).
Per quanto riguarda il secondo parametro, secondo me, hai già capito. Te lo dico comunque: è la
classe che hai appena creato, “Article”! In questo caso ho specificato il percorso completo, visto che
si trova nel namespace “BlogController”.
Sicuramente è un passo avanti per quanto riguarda l’organizzazione del codice, vero? Distinguere
la logica in questo modo aiuta molto a tenere il codice ordinato, oltre che a facilitarti la vita, visto
che dovrai usare un solo metodo per ogni controller, anzichè uno per ogni action!
Blade
In questo capitolo imparerai ad usare a dovere uno strumento potentissimo: Blade. Ti assicuro,
ne avrai bisogno. Dovrai diventarne un maestro, per poter reclamare il titolo di Artisan PHP,
sconfiggendo Lord Otwell in combattimento. È un po’ come un rito di passaggio: solo allora potrai
prendere il tuo posto nel consiglio, al tavolo di Phil Sturgeon.
Ok, scherzi a parte, vediamo davvero di cosa si tratta Blade. Non è un arma, ovviamente: è il sistema
di Templating di Laravel.
Probabilmente ti starai chiedendo il perché del nome “Blade”. Ho chiesto a Taylor, che mi ha parlato
di Razor, un componente simile per .NET dal quale ha preso ispirazione.
Vediamo come usarlo.
Creare un Template
Abbiamo già visto come creare le view, vero? Particolarmente utili per separare la logica dalla
presentazione. Se proprio vogliamo, però, possiamo ancora migliorarle. Esatto, grazie a Blade!
Il problema con le view PHP standard, infatti, è che nonostante la suddetta separazione c’è sempre
bisogno di inserire i tag PHP. Un minimo, certo, ma comunque c’è bisogno di farlo.
Grazie a Blade abbiamo a disposizione una comoda sintassi che ci permette di evitare tutto ciò.
Vediamone un primo, semplice esempio.
Lo so, sto facendo le cose passo passo. Questo è l’esempio di view che abbiamo visto qualche capitolo
fa. La variabile “$scoiattolo” viene elaborata, visto che ci sono i tag di apertura e chiusura PHP.
Praticamente cambia solo il nome: il file si chiama infatti “esempio.blade.php”. Per cui, da una
qualsiasi route, dovremmo richiamare il metodo “View::make(‘esempio.blade’)”… giusto?
Invece no!
Blade 89
1 <?php
2
3 // app/routes.php
4
5 Route::get('esempio', function() { return View::make('esempio'); });
Ecco come funziona il meccanismo: quando chiediamo a Laravel di usare la view “esempio” viene
cercato anche il “.blade”. Se viene trovato, Laravel userà automaticamente il motore di Blade per
l’interpretazione corretta della view. Altrimenti, invece, procederà normalmente.
Comodo, vero?
Vediamo adesso un po’ di sintassi interna alla view.
Output PHP
Molti dei tuoi template, sicuramente, servono per l’output di uno o più dati. Magari fai spesso
qualcosa del genere:
Ok, il nome può essere migliorato. Comunque, vediamo come possiamo evitare il codice PHP per
ottenere comunque lo stesso identico risultato:
Tutto quello che viene inserito tra le doppie parentesi graffe “{{“ e “}}” viene elaborato e passato ad
un metodo “echo” automaticamente, grazie a Blade.
Converrai con me che è una sintassi molto semplice e molto più pulita della precedente.
Inoltre, visto che queste direttive verranno “tradotte” direttamente in PHP puoi tranquillamente
inserire il codice che già hai usato, o ti può servire.
Magari una funzione come “date”:
Brutto, eh? Senza considerare, appunto, che potrebbe essere poco sicuro, visto che equivale ad
un’iniezione di codice javascript a tutti gli effetti.
Anche qui Laravel ci offre una soluzione semplice. Anzichè usare due parentesi graffe, ne usiamo
tre:
Tutti i caratteri speciali vengono così magicamente trasformati nelle varie entità HTML corrispon-
denti.
Ecco l’output finale del codice sorgente della pagina:
Strutture di Controllo
PHP presenta un certo numero di strutture di controllo: if, while, for e foreach. Se non ne hai mai
sentito parlare, leggi un altro libro.
Sicuramente conosci anche tutte le varie scorciatoie di queste strutture: quelle in cui i due punti “:”
fanno da padrone. Vediamo un semplice if in questo formato.
Blade, grazie alla chiocciola (“@”), sa perfettamente come interpretare tutti i vari blocchi di codice,
e l’output viene gestito esattamente come desiderato.
Adesso concentrati. Respira. Prova ad immaginare un ciclo foreach in Blade. Chiudi gli occhi e
concentrati, ragazzo! Ecco il foreach:
Molto più semplice da scrivere, eh? Come sempre, anche qui per l’output del valore abbiamo usato
le parentesi graffe.
Stessa cosa per un ciclo for. Vediamone un esempio:
Blade 92
Semplice, pulito ed esattamente quello che serve. Anche il while segue la stessa filosofia:
Perfetto: adesso sei il maestro delle strutture condizionali. Nulla può fregarti. Tranne l’ultima
struttura… l’unless.
Hai ragione, ma chi ti ha detto che Blade non può offrirlo comunque?
Unless è l’esatto apposto di un while, se vogliamo. Mentre il while si occupa di eseguire un certo
codice se la condizione è uguale a “true”, nell’unless il codice viene eseguito solo se la condizione è
uguale a “false”.
La struttura di controllo per eccellenza dei pessimisti.
Template
Blade include alcuni metodi ausiliari (helper), in modo tale da permetterti di costruire e gestire
facilmente dei template per la tua applicazione.
Certo, magari non scriverà per te le view che ti servono… per quello ci stiamo lavorando, magari in
Laravel 5. Chissà. Nel frattempo, purtroppo, dovrai farti da solo le tue interfacce.
Blade 93
Come probabilmente già sai, PHP ti permette di includere un file dentro un altro file, attraverso
l’istruzione “include()”. Particolarmente utile in fase di creazione delle view, in modo tale da
organizzare il codice in modo più strutturato.
Blade ovviamente non è da meno: attraverso il metodo “@include” potrai ottenere lo stesso risultato,
elaborando la view inclusa.
Basta chiacchiere! Passiamo subito ad un esempio che è meglio!
Come puoi vedere, tutto quello che il metodo “include()” fa è accettare, come parametro, il nome
della view da caricare in quel preciso punto in cui è scritta l’istruzione stessa.
Il formato del nome della view è lo stesso che viene utilizzato con il metodo “View::make()”.
Ecco il risultato in output!
Blade 94
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Narwhals</title>
6 </head>
7 <body>
8 <h1>When does the Narwhal bacon?</h1>
9 <p>Why, the Narhwal surely bacons at midnight, my good sir!</p>
10 <small>Information provided based on research as of 3rd May '13.</small>
11 </body>
12 </html>
Semplice! In questo modo il nostro template potrà essere scomposto in più pezzi, da riutilizzare e
combinare in base alle necessità: decisamente affine con la filosofia del DRY (Don’t Repeat Yourself).
Cosa chiedere di più?
Non finisce qui, comunque! Vediamo gli altri strumenti che ci vengono messi a disposizione da Blade!
Ereditarietà e Template
Blade e Laravel offrono un nuovo modo di costruire templates: il segreto è basarsi sull’ereditarietà.
Confuso?
Tranquillo: cercherò di semplificare il concetto il più possibile e renderti la vita più facile. Però
proviamoci, ne vale la pena!
Innanzitutto immaginiamo un template. Ci sono alcune “parti” che non cambiano e sono uguali su
tutte le pagine. Altre invece sono variabili e cambiano in base a dove ci si trova.
Pensiamo a tutte queste parti fisse come una “base” da cui partire, nella quale poi inserire tutto il
resto. Vediamone subito un esempio:
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title></title>
6 </head>
7 <body>
8 </body>
9 </html>
Blade 95
Direi che come base è piuttosto generica, vero? Va sicuramente bene per qualsiasi pagina web della
nostra applicazione.
Trasformiamola, adesso, in un layout di base.
Cosa ho fatto? Ho creato un template partendo dal codice HTML che avevo e ci ho inserito due
sezioni.
La prima, quella relativa al corpo della nostra pagina, è piuttosto semplice da capire:
1 @yield('body')
L’istruzione “@yield” indica che qui blade dovrà inserire il corpo di una certa sezione che verrà
specificato più tardi: guarda questa istruzione come un “segnaposto”.
L’altra sezione invece è questa:
Molto simile alla precedente, ad eccezione che si può aggiungere del contenuto di default. In questo
modo, quindi, nel caso non venga specificato nulla in corrispondenza del segnaposto “testata”,
Laravel automaticamente provvederà a mostrare il codice all’interno del blocco.
Adesso, partendo da questa base, creiamo un “template figlio”.
Blade 96
1 @extends('layouts.base')
Questo metodo serve ad avvertire Laravel che questa pagina del template usa, come base, il codice
contenuto in “layout/base”, creato poco fa. Il carattere “.” (il punto) denota la suddivisione delle
cartelle.
Nota per tutti i nostalgici: questa funzionalità in Laravel 3 era implementata da “@layout”.
L’abbiamo rinominata in modo tale da avere un nome più “coerente” con la funzionalità specifica
che volevamo descrivere.
Ora, visto che è stato specificato il “modello” da cui partire, non bisogna fare altro che riempire i
vari segnaposto.
L’istruzione “@section” fa al caso nostro:
Abbiamo specificato il nome (“corpo”) ed un contenuto, delimitandolo con “@stop” alla fine.
A quel punto pensa a tutto Laravel: in fase di rendering prende questo contenuto e lo mette in
corrispondenza dell’istruzione “@yield(‘corpo’)” vista prima.
Creiamo subito una route, in modo tale da testare il tutto.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('home'); });
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title></title>
6 <link rel="stylesheet" href="style.css" />
7 </head>
8 <body>
9 <h1>Hurray!</h1>
10 <p>We have a template!</p>
11 </body>
12 </html>
Ed ecco l’output.
Blade 98
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title/title>
6 <link rel="stylesheet" href="another.css" />
7 </head>
8 <body>
9 <h1>Hurray!</h1>
10 <p>We have
11 a template!
12 </p>
13 </body>
14 </html>
Come fare, invece, se volessimo semplicemente aggiungere del contenuto ad una sezione e non
sostituirlo con uno di default, come fatto finora?
L’istruzione “@parent” arriva in nostro soccorso:
“@parent” dice a Blade cosa fare: al posto di sostituire tutto il contenuto di “@yield”, sostituisci il
contenuto della view in corrispondenza di “@parent”.
Vediamo come cambia il codice sorgente:
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title/title>
6 <link rel="stylesheet" href="style.css" />
7 <link rel="stylesheet"
8 href="another.css" />
9 </head>
10 <body>
Blade 99
11 <h1>Hurray!</h1>
12 <p>We have a template!</p>
13 </body>
14 </html>
Tutto chiaro?
Laravel, come sempre, non ti vuole porre limiti: la creazione di un template figlio derivato da un
padre può coinvolgere anche più di due livelli. Come in questo esempio:
1 <p>First</p>
2 <p>Second</p>
3 <p>Third</p>
4 <p>Fourth</p>
5 <p>Fifth</p>
Volendo descrivere il processo, potremmo dire che: “quarta” estende “terza” che estende “seconda”
che estende “prima” che è il template di base.
Avrai sicuramente notato, tra l’altro, che ad ogni livello puoi aggiungere del contenuto aggiuntivo.
Insomma: Blade è davvero flessibile.
Commenti
Probabilmente già lo sai: HTML permette di inserire dei commenti all’interno dei file:
Blade 100
Normalmente, questo commento viene letto anche dall’utente finale, che da un’occhiata al codice
sorgente. C’è a chi piace e chi, invece, non gradisce la cosa.
Se sei del secondo gruppo potresti usare i commenti classici PHP, che a differenza dei primi non
vengono visti dal codice HTML dato in output.
Se poi sei anche un esteta e non ti ritrovi in nessuno dei due gruppi, perché non usare i commenti
di Blade?
1 <?php
2 // app/routes.php
3
4 Route::get('/', array( 'before' => 'sexyfilter', function() {
5 return View::make('hello');
6 } ));
Abbiamo creato una route e gli abbiamo assegnato un filtro, da eseguire prima delle istruzioni nella
closure. Questo è il nostro nuovo punto di partenza: vediamo cosa possiamo fare ancora e quali
strumenti ci consegna Laravel.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/my/long/calendar/route', array( 'as' => 'calendar', function(){
6 return View::make('calendar');
7 }));
Usando l’indice “as” possiamo specificare un nome identificativo per la route. In questo modo,
da questo momento in avanti, potremo arrivare a questa route semplicemente usando questo
“nickname”. Pensa, ad esempio, alla classe “Redirect”.
Oppure, guarda il metodo seguente:
1 // app/views/example.blade.php
2 {{ route('calendar') }}
Il metodo route(‘calendario’) crea automaticamente un URL. Questo url non è altro che quel-
lo della route specificata come parametro. Quindi, dato che la route “calendario” equivale a
“una/lunga/route/per/il/calendario”, il risultato sarà:
1 http://localhost/una/lunga/route/per/il/calendario
Utile, vero?
Una tecnica del genere può tornare molto utile in svariati casi: immagina di avere un’applicazione
con tante routes. Hai deciso di generare i link tramite questo metodo e, ad un certo punto, devi
cambiare una route.
Non avrai perso neanche un secondo di più: anche se hai cambiato la route, infatti, è il nickname il
punto di riferimento.
Potrai anche aver cambiato “una/lunga/route/per/il/calendario” in “una/lunga/route/per/il/calenda-
rio/fantastico”, ma che differenza fa? Tanto c’è il nickname “calendario” che permette ugualmente
l’indirizzamento già aggiornato con il nuovo URL!
Inoltre, come se non bastasse, puoi usare il metodo “route()” anche sull’oggetto Redirect! Te lo
ricordi?
1 <?php
2 return new Redirect::route('calendar');
Comodo. Poi ci sono anche altre varie utility. Come ad esempio il metodo “currentRouteName()”,
della classe “Route”:
Routing Avanzato 103
1 <?php
2 //app/routes.php
3
4 $current = Route::currentRouteName();
Ricorda: tutte queste features più avanzate sono disponibili sia per le route che per i controller! Ecco
un esempio dell’uso di “as” con una action di un controller:
1 <?php
2
3 //app/routes.php
4
5 Route::get('/my/long/calendar/route', array( 'as' => 'calendar', 'uses' => 'Cal\
6 endarController@showCalendar' ));
Dopo questa rassegna passiamo un attimo alla sicurezza: vediamo come rendere le nostre routes più
sicure.
1 <?php
2 // app/routes.php
3
4 Route::get('secret/content', array( 'https', function () {
5 return 'Secret squirrel!';
6 }));
Parametri e Vincoli
Nel capitolo dedicato al routing base abbiamo visto che è possibile specificare un insieme di
parametri per una certa route specifica. Ad esempio:
Routing Avanzato 104
1 <?php
2 // app/routes.php
3
4 Route::get('save/{princess}', function($princess) {
5 return "Sorry, {$princess} is in another castle. :(";
6 });
Potrebbe essere necessario creare qualche “regola”, in modo tale da limitare ogni possibile danno o
incoerenza. D’altronde, l’hai mai vista una principessa di nome “1337f15h”?
Ma si, dai: aggiungiamo qualche vincolo al parametro. Supponiamo di voler accettare solo delle
stringhe di lettere.
Ecco come fare.
1 <?php
2 // app/routes.php
3
4 Route::get('save/{princess}', function($princess) {
5 return "Sorry, {$princess} is in another castle. :("; })->where('princess', '[A\
6 -Za-z]+');
Cosa è successo? Grazie al metodo concatenato “where()” abbiamo specificato che per il parametro
“principessa” (primo parametro di where()) possono essere specificate solo stringhe di lettere come
valore valido (secondo l’espressione regolare passata come secondo parametro del metodo where()).
Ovviamente non starò qui a coprire l’intero capitolo delle espressioni regolari nel dettaglio: è un
argomento così vasto che richiederebbe un libro a parte! Per cui, per ora, prendi tutto per buono e
vai tranquillo.
Tornando a noi: cosa succede se inserisco un valore non valido come parametro?
Semplice: la route non viene neanche presa in considerazione.
Se dovesse presentarsi la necessità, tra l’altro, puoi usare quante condizioni desideri:
1 <?php
2 // app/routes.php
3
4 Route::get('save/{princess}/{unicorn}', function($princess, $unicorn) {
5 return "{$princess} loves {$unicorn}"; })->where('princess', '[A-Za-z]+')
6 ->where('unicorn', '[0-9]+');
Anche qui andiamo a valutare il nome dell’unicorno come uno o più numeri. D’altronde si sa che
gli unicorni vengono sempre numerati. Lo sa bene anche il mio amico 3240012.
Routing Avanzato 105
Gruppi di Routes
Ricordi, qualche capitolo fa, come era possibile collegare una o più condizioni ad una route tramite
un filtro?
Bene, immagina di avere tante, tantissime routes alle quali dover collegare il filtro in questione. Non
sarebbe poi così comodo, dover collegare route dopo route.
Per fortuna Laravel permette di raggruppare le routes, in modo tale da permettere loro di condividere
attributi comuni, e a te di risparmiare tempo.
Vediamo subito un esempio:
1 <?php
2 // app/routes.php
3
4 Route::group(array('before' => 'onlybrogrammers'), function() {
5
6 //First Route
7 Route::get('/first', function() {
8 return 'Dude!';
9 });
10
11 //Second Route
12 Route::get('/second', function() {
13 return 'Duuuuude!';
14 });
15
16 //Third Route
17 Route::get('/third', function() {
18 return 'Come at me bro.';
19 });
20
21 });
Nell’esempio qui sopra abbiamo usato il metodo “group()” sull’oggetto Route. Il primo parametro è
un array, che lavora esattamente come gli altri visti finora. Accetta filtri, indici di sicurezza e tutto
il resto.
Il secondo parametro, invece, è una closure contenente tutte le altre routes. Definite qua dentro,
infatti, queste routes condividono automaticamente tutte le proprietà del gruppo di cui fanno parte.
Non esistono solo i filtri, però! Vediamo quali altre features ci riserva Laravel.
Routing Avanzato 106
Routes e Prefissi
Se molte delle tue routes condividono una certa struttura dell’URL, potresti creare un gruppo per
evitare un po’ di noiosissime ripetizioni!
Guarda un po’ qui:
1 <?php
2 // app/routes.php
3
4 Route::group(array('prefix' => 'books'), function() {
5
6 //First Route
7 Route::get('/first', function() {
8 return 'The Colour of Magic';
9 });
10
11 //Second Route
12 Route::get('/second', function() {
13 return 'Reaper Man';
14 });
15
16 //Third Route
17 Route::get('/third', function() {
18 return 'Lords and Ladies';
19 });
20
21 });
Questo prefisso varrà per ogni singola route del gruppo. Di conseguenza, le routes “prima”, “seconda”
e “terza” saranno raggiungibili agli indirizzi:
1 /libri/prima
2
3 /libri/seconda
4
5 /libri/terza
Routing di Dominio
A volte non è solo l’URI a caratterizzare una route. L’host, infatti, può cambiare. Considera questi
indirizzi:
Routing Avanzato 107
1 http://myapp.dev/my/route
2 http://another.myapp.dev/my/route
3 http://third.myapp.dev/my/route
La differenza è evidente: i sottodomini. Vediamo come usare il domain routing a nostro vantaggio:
magari per distribuire contenuti differenti in base al sottodominio.
1 <?php
2 // app/routes.php
3
4 Route::group(array('domain' => 'myapp.dev'), function() {
5 Route::get('my/route', function() { return 'Hello from myapp.dev!'; });
6 });
7
8 Route::group(array('domain' => 'another.myapp.dev'), function() {
9 Route::get('my/route', function() { return 'Hello from another.myapp.dev!';});
10 });
11
12 Route::group(array('domain' => 'third.myapp.dev'), function() {
13 Route::get('my/route', function() { return 'Hello from third.myapp.dev!';});
14 });
Usando l’indice “domain” possiamo specificare un hostname di riferimento che deve coincidere con
quello della richiesta attuale. Di conseguenza, in base alla richiesta sarà servito un determinato
risultato.
Chiaramente è anche possibile usare dei parametri, in modo tale da gestire a piacimento il valore in
arrivo:
1 <?php
2 //app/routes.php
3
4 Route::group(array('domain' => '{user}.myapp.dev'), function() {
5 Route::get('profile/{page}', function($user, $page) {
6 // ...
7 });
8 });
http://taylor.myapp.dev/profilo/avatar
Verrebbe preso il valore in $utente (“taylor”) e passato alla route richiedente. Un esempio come
quello appena visto potrebbe essere utile per gestire le pagine del profilo dei vari utenti della nostra
applicazione. Anche se, comunque, gli usi che se ne possono fare sono tantissimi.
Buon lavoro!
Generazione di URL
La tua applicazione, nel caso tu non lo sappia ancora, ruota intorno a routes ed indirizzi. Dopotutto,
è proprio li che vanno a finire i tuoi utenti. D’altronde è quello il principio alla base: servire pagine
all’utente finale.
Ora, se la tua applicazione, per quanto meravigliosa possa essere, serve una singola pagina, dubito
fortemente che i tuoi utenti possano esserci interessati a lungo.
Quale potrebbe mai essere la soluzione?
Ma certo! Più pagine! Link ipertestuali! Grande Giove!
Per creare dei link ipertestuali abbiamo bisogno di capire come costruire i link della nostra
applicazione. Certo, potremmo farlo a mano, ma perché mai usare un framework, a quel punto?
Vediamo come Laravel ci aiuta con la generazione di link.
L’URL Attuale
Partiamo dalle basi. Ottenere il link della pagina corrente, con Laravel, è semplicissimo: basta usare
il metodo URL::current(), il quale ritorna il valore di cui abbiamo bisogno.
Costruiamoci una route al volo:
1 <?php
2 // app/routes.php
3
4 Route::get('/url/attuale', function() { return URL::current(); });
1 http://myapp.dev/url/attuale
Facile, no?
Passiamo avanti: “URL::full()” consente invece di ottenere l’URL della pagina attuale.
Ehm, mi sa che stai sbagliando. Due metodi che fanno la stessa identica cosa?
No, identica no. Lasciami spiegare. Supponiamo di passare in GET dei parametri alla pagina. Ad
esempio:
Generazione di URL 110
1 http://myapp.dev/url/attuale?foo=bar
Mentre URL::current() “pulisce” l’URL da tutti i parametri aggiuntivi, URL::full() invece ti restituisce
l’URL completo.
Testiamolo subito:
1 <?php
2 // app/routes.php
3
4 Route::get('/current/url', function() { return URL::full(); });
1 http://myapp.dev/url/attuale?foo=bar
Chiaro, no?
Adesso passiamo ad un altro metodo, che permette di ottenere l’URL precedente dall’header,
precisamente in corrispondenza della voce “referer”.
Vediamo subito un esempio:
1 <?php
2 // app/routes.php
3
4 Route::get('prima', function() {
5 // Redirect alla seconda route.
6 return Redirect::to('seconda');
7 });
8
9 Route::get('seconda', function() {
10 return URL::previous();
11 });
Nella prima route ho creato un redirect verso la seconda route. In corrispondenza di questa, quindi,
ho effettuato l’output di “URL::previous()”, ottenendo quindi l’URL della prima (da dove provengo,
per intenderci).
1 <?php
2 // app/routes.php
3
4 Route::get('esempio', function() { return URL::to('altra/route'); });
1 http://demo.dev/another/route
Come puoi vedere, Laravel ha costruito automaticamente un link alla route da te richiesta. Ora,
“altra/route” in questo momento non esiste, ma puoi creare comunque un link. Sta a te poi decidere
cosa generare.
Ci sono anche altri parametri che puoi specificare, sottoforma di array. Vediamone alcuni:
1 <?php
2 // app/routes.php
3
4 Route::get('esempio', function() { return URL::to('altra/route', array('foo', 'b\
5 ar')); });
1 http://myapp.dev/altra/route/foo/bar
Se, inoltre, vuoi assicurarti che l’URL generato usi il protocollo HTTPS, allora devi specificare “true”
(senza virgolette) come terzo parametro del metodo:
Altrimenti, un’alternativa più che valida e anche più veloce può essere il metodo “URL::secure()”:
1 URL::secure('altra/route');
Il risultato è lo stesso.
Ancora una volta, anche qui puoi specificare altri parametri aggiuntivi, come nell’esempio di prima:
1 <?php
2 // app/routes.php
3
4 Route::get('il/migliore/avenger', array('as' => 'ironman', function() {
5 return 'Tony Stark';
6 }));
7
8 Route::get('esempio', function() { return URL::route('ironman'); });
1 http://myapp.dev/il/migliore/avenger
1 <?php
2 // app/routes.php
3
4 Route::get('il/{primo}/avenger/{secondo}', array( 'as' => 'ironman', function($p\
5 rimo, $secondo) {
6 return "Tony Stark, il {$primo} avenger {$secondo}.";
7 }));
8
9 Route::get('esempio', function() { return URL::route('ironman', array('migliore'\
10 , 'sempre')); });
1 http://myapp.dev/esempio
Laravel compilerà automaticamente l’URL risultante usando i parametri nell’esatto ordine di input.
Il risultato, quindi, sarà il seguente:
1 http://myapp.dev/il/miglior/avenger/sempre
C’è ancora un altro metodo di generazione URL da conoscere. Niente di speciale, tranquillo, ma
ricordati che non ci sono solo le routes: possiamo mai lasciare indietro i controller?
Stiamo parlando di “URL::action()”:
Generazione di URL 113
1 <?php
2 // app/routes.php
3
4 class Stark extends BaseController {
5
6 public function tony() {
7 return "Se c'è una cosa che ho imparato è che bisogna contare su di me per app\
8 agare me stesso.";
9 }
10 }
11
12 Route::get('io/sono/iron/man', 'Stark@tony');
13
14 Route::get('esempio', function() { return URL::action('Stark@tony'); });
Abbiamo creato velocemente un controller, Stark, fornendogli anche una action, “tony()”. Abbiamo
quindi specificato la route corrispondente (prima chiamata di “Route::get()”), per poi specificare il
return dell’URL in “esempio”.
Andando proprio a questo indirizzo, ecco cosa appare:
1 http://myapp.dev/io/sono/iron/man
Laravel ha automaticamente collegato l’URL della action “tony()” con la route “i/sono/iron/man”.
Indirizzo che è stato poi mandato in output.
Anche in questo caso è, ovviamente, possibile passare uno o più parametri. Stesso discorso per quanto
riguarda l’ordinamento. Ecco un ulteriore esempio: evito altre spiegazioni.
1 <?php
2 //app/routes.php
3
4 class Stark extends BaseController
5 {
6 public function tony($whatIsTony)
7 {
8 ...
9 }
10 }
11
12 Route::get('tony/il/{first}/genio', 'Stark@tony');
13
14 Route::get('esempio', function() { return URL::action('Stark@tony', array('narc\
15 isista')); });
Generazione di URL 114
1 http://myapp.dev/tony/il/narcisista/genio
1 <?php
2 // app/routes.php
3
4 Route::get('esempio', function() { return URL::asset('img/logo.png'); });
Il primo parametro è, ovviamente, il path relativo della risorsa per la quale generare l’URL.
Visitando l’indirizzo “esempio” otterremo qualcosa come:
1 http://myapp.dev/img/logo.png
1 <?php
2 // app/routes.php
3
4 Route::get('esempio', function() { return URL::asset('img/logo.png', true); });
Et voilà!
1 https://demo.dev/img/logo.png
Non ti va di usare il “true”? Beh dai, capita. Allora puoi usare “URL::secureAsset()”, se vuoi buttarla
più sul descrittivo.
1 <?php
2 // app/routes.php
3
4 Route::get('esempio', function() { return URL::secureAsset('img/logo.png'); });
1 https://demo.dev/img/logo.png
Scorciatoie
Tutti i metodi che abbiamo visto finora sono ugualmente utilizzabili anche nelle view della tua
applicazione. Per questioni di comodità, però, potresti avere la necessità di usare dei metodi più…
corti.
Anche in questo caso Laravel non ti lascia solo e ti offre una scorciatoia per ogni singolo metodo
visto finora.
Vediamole, partendo dal metodo “url()”:
Anche per creare URL sicuri con il protocollo HTTPS c’è la scorciatoia adeguata:
Generazione di URL 116
La sintassi è esattamente la stessa della controparte “completa”. Anche per quanto riguarda parametri
ed extra:
Infine ecco la shortcut per il metodo “URL::asset()”. Indovina come si chiama? “asset()”. Ovviamente.
Ci siamo. È un bel po’ di roba, ma da oggi in poi puoi creare URL di qualsiasi genere come se non ci
fosse un domani.
Buon divertimento.
Dati e Richieste
Tutte le applicazioni si basano sulla manipolazione di uno o più dati, per risolvere un problema. I
dati vengono cambiati, memorizzati, creati, elaborati e così via.
Nel mondo del web questi dati, a volte, non sono cose che durano per sempre. Anzi, magari durano
giusto il minimo indispensabile: si pensi ai dati di un form, che “vivono” solo per il tempo della
richiesta stessa.
Prima di tutto, però, dobbiamo capire come prelevare questi dati dalla richiesta. Non ti preoccupare,
come sempre Laravel ha dei metodi veloci adatti allo scopo.
Ecco un semplice esempio:
1 <?php
2 // app/providers/input/data/request.php
3
4 namespace Laravel\Input\Request\Access;
5
6 use Laravel\Input\Request\Access\DataProvider;
7 use Laravel\Input\Request\Access\DataProvider\DogBreed;
8
9 class Data extends DataProvider {
10 public function getDataFromRequest($requestDataIndicator) {
11 $secureRequestDataToken = sin(2754) - cos(23 + 52 - pi() / 2);
12 $retriever = $this->getContainer()->get('retriever');
13 $goldenRetriever = $retriever->decorate(DogBreed::GOLDEN);
14 $request = $goldenRetriever->retrieveCurrentRequestByImaginaryFigure();
15 return $request->data->input->getDataByKey($requestDataIndicator);
16 }
17 }
18
19 // app/routes.php
20
21 $myDataProvider = new Laravel\Input\Request\Access\Data; $data =
22 $myDataProvider->getDataFromRequest('example');
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { $data = Input::all(); var_dump($data); });
Il metodo “Input::all()” recupera TUTTI i dati contenuti in una richiesta, sia in POST che in GET.
Una volta recuperati i dati vengono messi in un array associativo.
Prendiamo ad esempio l’URL
1 http://myapp.dev/?foo=bar&baz=boo
Questo è un classico esempio di recupero dei dati in una richiesta GET, ma come già detto puoi fare
la stessa cosa anche con i dati mandati in POST (da un form, ad esempio).
Cominciamo dal codice del form:
1 <?php
2 // app/routes.php
3
4 Route::post('/', function() { $data = Input::all(); var_dump($data); });
5 Route::get('post-form', function() { return View::make('form'); });
Abbiamo aggiunto una route per mostrare il form e fatto un’altra piccola modifica. L’hai notata?
Ho alterato il metodo “Route::get()” in “Route::post()” per quanto riguarda l’URL “/”. In questo modo
non viene letto nessun dato di richiesta fin quando il verb HTTP non è POST.
Visita quindi “/post-form” e clicca sul pulsante “Send” per inviare la richiesta. Questo è l’output:
Niente di così complicato, no? I dati sono stati recuperati correttamente e possiamo lavorarci come
meglio crediamo.
Ora, una piccola osservazione: anche se siamo in POST, c’è il modo di leggere eventuali altri
parametri passati all’URL?
C’è solo un modo per saperlo: modifichiamo il form.
A quanto pare, i dati in get vengono letti e hanno anche rimpiazzato quelli contenuti in POST.
In definitiva: i dati in GET hanno una priorità più alta di quelli in POST. Nel caso vengano utilizzati
entrambi, quindi, sai cosa succede.
A volte è necessario recuperare un singolo dato, tra i tanti immessi nella richiesta. Esiste il metodo
“Input::get()” per questo scopo, e si usa così:
Dati e Richieste 120
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { $data = Input::get('foo'); var_dump($data); });
Come primo parametro ho specificato il nome identificativo del dato da prelevare et voilà! Ecco cosa
succede se eseguo la route qui sopra:
1 string(3) "bar"
Altra domanda: se questo dato non esistesse? Visitiamo l’URL “/” senza ulteriori parametri.
1 NULL
Fai bene attenzione: nel momento in cui in Laravel qualcosa non viene trovato, il sistema ritorna
“NULL” (senza virgolette).
Aggiungendo un secondo parametro al metodo Input::get(), inoltre, possiamo specificare un even-
tuale valore di default in caso non venga trovato niente.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { $data = Input::get('foo', 'bar');
5 var_dump($data); });
Ed ecco il risultato:
1 string(3) "bar"
In poche parole, abbiamo visitato l’URL senza specificare nessun dato aggiuntivo. Secondo le
istruzioni abbiamo richiesto “foo”, ma non avendo trovato niente abbiamo restituito il valore “bar”,
indicato come default in casi come questi.
Laravel però permette anche un controllo “alla base”. Hai bisogno di sapere se un certo dato esiste?
Allora usa il metodo “Input::has()”!
Dati e Richieste 121
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { $result = Input::has('foo'); var_dump($result); });
Pfff…
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() {
6 $result = Input::only(array('foo', 'baz'));
7 var_dump($result);
8 });
A questo metodo vanno passati, in un semplice array, tutti gli indici dei dati di cui abbiamo bisogno.
Se non ti piace l’array puoi sempre usare una semplice lista:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 $result = Input::only('foo', 'baz');
6 var_dump($result);
7 });
1 http://myapp.dev/?foo=one&bar=two&baz=three
Et voilà:
Dati e Richieste 122
Laravel, come richiesto, ha recuperato solo un determinato subset dei dati della richiesta. Ovvia-
mente, così come esiste il metodo “only()” esiste il metodo “except()”.
Come puoi facilmente immaginare, in questo caso verranno ritornati TUTTI i dati eccetto quelli
specificati come parametri.
Anche in questo caso puoi scriverlo così:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() {
6 $result = Input::except(array('foo', 'baz'));
7 var_dump($result);
8 });
Oppure così:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 $result = Input::except('foo', 'baz');
6 var_dump($result);
7 });
1 http://demo.dev/?foo=one&bar=two&baz=three
“Old” Input
Come già specificato prima, i dati disponibili in richieste “POST” e “GET” rimangono tali solo per
la durata della richiesta corrente. Questo vuol dire che al giro successivo vengono persi.
Tuttavia, nel caso dovessimo averne bisogno ancora per un po’, Laravel mette a disposizione un
comodo meccanismo di gestione del “vecchio” input, per un altro ciclo ancora.
Osserviamo questo esempio per capire meglio il tutto:
Dati e Richieste 123
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return Redirect::to('new/request'); });
5 Route::get('new/request', function() { var_dump(Input::all()); });
Abbiamo la prima route che effettua un redirect verso la seconda, “new/request”. Passiamo alla
prima route un po’ di dati in GET e vediamo che succede:
1 http://myapp.dev/?foo=one&bar=two
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flash();
6 return Redirect::to('new/request');
7 });
8
9 Route::get('new/request', function() { var_dump(Input::all()); });
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flash();
6 return Redirect::to('new/request');
7 });
8
9 Route::get('new/request', function() { var_dump(Input::old()); });
Il metodo “Input::old()” chiede a Laravel l’intero array di dati della richiesta precedente (quindi, non
quella attuale). Ecco quale sarà l’output della richiesta adesso, visitando l’URL “/?foo=one&bar=two”.
Perfetto! Esattamente quello di cui avevamo bisogno! Adesso abbiamo una marcia in più, potendo
lavorare non solo con i dati della richiesta attuale, ma anche con quelli della precedente.
Esattamente come negli altri casi, tra l’altro, possiamo anche recuperare i singoli elementi di questo
array.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flash();
6 return Redirect::to('new/request');
7 });
8
9 Route::get('new/request', function() { var_dump(Input::old('bar')); });
Qui ci è bastato inserire un parametro, una semplice stringa, per indicare l’indice del dato da
recuperare.
Cosa fare se, invece, volessimo recuperare solo alcuni elementi? Un subset, esattamente come prima.
Beh, basta dire a Laravel cosa… flashare, e cosa no!
Dati e Richieste 125
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flashOnly('foo');
6 return Redirect::to('new/request');
7 });
8
9 Route::get('new/request', function() { var_dump(Input::old()); });
Grazie al metodo “flashOnly” decidiamo cosa tenere in memoria per la successiva richiesta e cosa
no. Semplice!
Visitiamo nuovamente “/?foo=one&bar=two”. L’output, stavolta, sarà il seguente:
Così come esiste “flashOnly” esiste anche “flashExcept”. Le modalità di funzionamento sono le stesse
di “only()” ed “except()”, per cui non mi dilungherò in ripetizioni inutili.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flashExcept('foo');
6 return Redirect::to('new/request');
7 });
8
9 Route::get('new/request', function() { var_dump(Input::old()); });
Ecco il risultato:
Direi che la libertà è massima, non trovi? L’uso di un array per questo genere di cose è particolar-
mente utile quando hai già memorizzato altrove gli indici di cui hai bisogno, e non vuoi inserirli a
mano (o magari cambiano di volta in volta). Un ottima prassi per tenere più pulito il codice!
Adesso ti faccio vedere qualcosa di più. Facciamo un passo avanti. Quante volte ti è capitato di
compilare un form e ricevere, in output, un errore?
Un sacco di volte: password troppo corte, troppo lunghe, indirizzo email non valido, e così via.
L’utente è umano ed errare è umano, cosa vuoi farci.
A volte, però, ci sono dei form veramente grandi. Una volta mi è capitato un form con 34 campi da
compilare. Non me lo scorderò mai e potrebbe capitare anche a te, anche se non te lo auguro.
Per questo motivo Taylor ha creato il metodo “withInput()”.
Vediamolo:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return Redirect::to('new/request')->withInput();
6 });
Concatenando il metodo “withInput” al redirect, Laravel esegue il flash di tutti i contenuti della
richiesta attuale, portandoli di fatto nel ciclo successivo.
Un modo elegante e più pulito di fare quello che abbiamo fatto poco fa. Si sa, con Laravel non ci si
accontenta mai.
Dati e Richieste 127
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Input::flash();
6 return Redirect::to('new/request');
7 });
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return Redirect::to('new/request')->withInput(Input::only('foo'));
6 });
Qui abbiamo usato “Input::only()” per decidere quale subset di dati passare alla richiesta successiva.
Stessa cosa per “Input::except()”:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return Redirect::to('new/request')->withInput(Input::except('foo'));
6 });
È evidente, ormai, l’enorme livello di miglioramento rispetto al PHP classico. Le cose sono cambiate
un bel po’. Tuttavia, l’input non è solo questo!
C’è ancora da vedere come gestire i files in upload.
Upload di Files
Le nostre applicazioni riceveranno dei dati: ma non è detto che saranno esclusivamente in formato
testuale. Una qualsiasi applicazione web in PHP può ricevere files da un form una volta configurato
adeguatamente.
Prima di vedere la procedura, però, dobbiamo preparare il terreno di gioco. Non posso, ovviamente,
allegare un file ad un qualsiasi URL con GET. Per questo motivo dovrò creare un form dal quale
caricare il file:
Dati e Richieste 128
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
Ehm, credo tu abbia saltato qualche capitolo. Torna indietro al capitolo dedicato al routing (di base,
non quello avanzato).
Se invece non ti sei posto questa domanda, continuiamo.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5 Route::post('handle-form', function() { var_dump(Input::all()); });
Direi che ci siamo. Visitiamo l’URL “/” e scegliamo un file da caricare per il nostro form. Magari un
bellissimo libro che ho scritto un annetto fa?
(Ricorda di non farti prendere dalla foga dell’upload e caricarlo ovunque, già ho mandato cinque
email di avviso per infrazione del copyright.)
Comunque, clicchiamo sul pulsante ed aspettiamo che il nostro file venga caricato.
Ed ecco la magia! Il nostro output sarà…
Dati e Richieste 129
1 array(0) { }
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5 Route::post('handle-form', function() { var_dump(Input::file('book')); });
Abbiamo fatto qualche modifica nella seconda route, quella di handling dell’upload. Anzichè usare
“Input::get()” ho deciso di usare “Input::file(‘book’)”.
Ecco il risultato.
1 object(Symfony\Component\HttpFoundation\File\UploadedFile)#9 (7) {
2 ["test":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
3 bool(false)
4 ["originalName":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
5 string(14) "codebright.pdf"
6 ["mimeType":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
7 string(15) "application/pdf"
8 ["size":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
9 int(2370413)
10 ["error":"Symfony\Component\HttpFoundation\File\UploadedFile":private]=>
11 int(0) ["pathName":"SplFileInfo":private]=> string(36)
12 "/Applications/MAMP/tmp/php/phpPOb0vX"
13 ["fileName":"SplFileInfo":private]=> string(9) "phpPOb0vX" }
Ci siamo! Un oggetto che rappresenta il nostro file appena caricato nell’applicazione. Meraviglioso!
Beh, in realtà non è un granchè da vedere. Sicuramente ben costruito, però. C’è da dirlo. Anche
perché presenta una marea di metodi utilissimi, perfetti per gestire il nostro file in tutti i suoi aspetti.
Vediamone subito qualcuno.
Dati e Richieste 130
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->getFileName();
8 });
Abbiamo concatenato all’istruzione che già c’era un “getFileName()”. Come puoi ben immaginare
restituisce il nome del file. Proviamolo subito.
phpaL1eZS
Tecnicamente si, è il tuo file, solo che questo è il suo nome “temporeaneo”, visto che ogni file caricato
viene messo, innanzitutto, nella cartella dei files temporanei. Nel momento in cui viene spostato
allora il nome cambia. Se non viene messo da nessuna parte fino alla fine della richiesta, il file viene
automaticamente rimosso.
Ok, vediamo se riusciamo a trovare in qualche modo il nome originale, sul nostro computer. Questo
mi sembra adatto!
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->getClientOriginalName();
8 });
“getClientOriginalName()” ritorna esattamente quello che cerchiamo: il nome (vero) del file.
Proviamolo:
1 codebright.pdf
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->getClientSize();
8 });
1 2370413
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->getMimeType();
8 });
Ed ecco il risultato:
1 application/pdf
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->guessExtension();
8 });
1 pdf
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 return Input::file('book')->getRealPath();
8 });
Ecco il limbo!
1 /tmp/php/phpLfBUaq
Ora che sappiamo dove si trova il file potremmo effettuare un “copy()” o “rename()”.
Ehi, ma noi usiamo Laravel, vogliamo una cosa fatta bene.
Dobbiamo spostare il file: quindi serve il metodo “move()”.
Basta specificare, come stringa, il path di destinazione e via!
Missione completata.
Dati e Richieste 133
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 Input::file('book')->move('/storage/directory');
8 return 'File was moved.';
9 });
Ricorda che avrai comunque bisogno dei permessi di scrittura e lettura delle cartelle dove vai a
salvare i files, altrimenti non avrai altro che errori, errori ed ancora errori, al posto di un bellissimo
libro al posto giusto.
Una volta caricato il file, andiamo a dare un’occhiata alla cartella di destinazione: il nome del file è
ancora quello della cartella temporanea!
No, non va proprio bene.
Dobbiamo muoverlo nella cartella di destinazione, certo, ma con il nome giusto!
Praticamente, così:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('handle-form', function() {
7 $name = Input::file('book')->getClientOriginalName();
8 Input::file('book')->move('/storage/directory', $name);
9 return 'File was moved.';
10 });
Cosa ho fatto?
Semplicemente, prima di tutto ho preso il nome vero del file, grazie a “getClientOriginalName()”.
Una volta presa questa informazione (e messa nella variabile $name) ho spostato il file grazie al
metodo “move()”.
Il gioco è fatto.
Per quanto riguarda i files per ora è tutto. Se vuoi approfondire la conoscenza della classe
“UploadedFile” di Symfony, comunque, dai un’occhiata alla Symfony API Documentation per la
classe UploadedFile¹³.
¹³http://api.symfony.com/2.0/Symfony/Component/HttpFoundation/File/UploadedFile.html
Dati e Richieste 134
Cookies
Teoricamente non dovrei parlare di cookies. Insomma, sono degli ottimi biscotti, ma ultimamente
sto facendo una dieta low-carb e, insomma, sai com’è…
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 $cookie = Cookie::make('low-carb', 'un gustoso biscotto alle mandorle', 30);
6 return Response::make('Nom nom.')->withCookie($cookie);
7 });
8
9 Route::get('/nom-nom', function() {
10 $cookie = Cookie::get('low-carb');
11 var_dump($cookie);
12 });
Nella prima route, “/”, abbiamo creato il cookie. Esattamente come prima, niente di più, niente di
meno. Abbiamo solo aggiunto il return di un oggetto response con un testo ed il cookie in questione.
Il metodo “withCookie()” può essere usato per allegare un cookie ad un oggetto response. Nel
momento in cui l’oggetto viene restituito viene creato anche il cookie.
La seconda route, “/nom-nom” si occuperà invece di recuperare il cookie in questione, usando il
nome specificato prima come parametro del metodo “Cookie::get()”.
Facciamo qualche test e visitiamo “/”.
1 Nom nom.
Ora, aspettando trenta minuti e riprovando ad effettuare quest’ultima visita all’URL “/nom-nom”,
otterremmo un “null” come risposta.
Proprio come “Input::get()”, anche “Cookie::get()” permette di specificare un secondo parametro, in
cui inserire un valore di default in caso di null.
Almeno adesso avremo un po’ di pollo, se proprio non avremo i benedetti biscotti.
Sempre come succedeva nell’Input, anche Cookie ha un suo metodo “Cookie::has()”, per verificare
la presenza o meno di un cookie nel sistema. Il metodo ritorna, ovviamente, true o false a seconda
dell’esito del controllo.
Dati e Richieste 136
Come già detto prima, i cookies hanno un certo periodo di vita, dopo il quale vengono cancellati.
Se dovessi aver bisogno di un cookie “eterno”, comunque, c’è sempre il metodo “Cookie::forever()”.
Eccolo:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 $cookie = Cookie::forever('low-carb', 'almond cookie');
6 return Response::make('Nom nom.')->withCookie($cookie);
7 });
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Cookie::forget('low-carb');
6 return 'Credo passerò al pollo.';
7 });
Cookie e Sicurezza
La sicurezza è importante, e Laravel lo sa bene.
A volte potresti decidere di mettere nei cookie un po’ di informazioni, magari importanti. Soprattutto,
potresti non aver voglia di lasciare all’utente finale la possibilità di prendere e modificare i dati del
cookie.
Fortunatamente Laravel “firma” ogni cookie con un codice di autenticazione, e ne cripta i contenuti.
Nessuno, se non la tua applicazione, potrà mai leggerlo.
Se, per qualche motivo, dovesse essere manomesso, semplicemente verrà ignorato.
Intelligente, vero?
I Form
Era una notte fredda e buia in quel di Los Angeles, ma le luci del Dolby Theatre erano così luminose
che per un secondo potevi scordarti che era non era più giorno. Gli Academy Awards erano andati
alla grande: Iron Man aveva vinto tutto quello che si poteva vincere. Meraviglioso.
In effetti, ora che ci penso, se questo libro dovesse vendere un sacco di copie potrei comprare anche
io una casa in California. Magari con un laboratorio sotterraneo e cominciare a lavorare ad un
esoscheletro tutto mio.
Mi chiamerei “La Volpe di Fuoco” e potrei usare i miei poteri per sistemare tutti i vari problemi nel
mondo PHP. Certo, dovrei lasciar perdere le modelle, Emma non ne sarebbe molto contenta. Poi, tra
l’altro, non credo che guiderei un Audi… ok, basta, torniamo a noi.
Eravamo al Dolby Theatre.
Dicevo, erano tutti li: gli sviluppatori salvati da Laravel 3 che non vedevano l’ora di assistere alla
prima di Laravel 4.
Dopo tanta attesa una limousine nera arriva. Le porte si aprono e una figura alta e slanciata. La
folla impazzisce: le donne si strappano i reggiseni, gli uomini si strappano i reggiseni, chiunque è
in topless ormai (so che non ha molto senso, ma è la mia storia e io dico che tutti sono in topless).
Comunque, la figura alta e slanciata altri non è che Taylor Otwell, il creatore di Laravel.
Cominciano subito le prime domande:
“Per circa un anno”. Capisce che deve fermarsi un attimo a rispondere, mentre altre reporter in
topless continuano ad arrivare. Così come le altre domande.
Ancora la stessa domanda? Taylor però si mostra paziente, per cui risponde: “Più o meno per un
anno”.
Taylor a quel punto non ce la fa più. Partono i cinque minuti, caccia dalla sua tasca una tastiera (non
si sa come, ma riesce a farlo) e la sfonda in faccia al reporter.
“Avete letto o no la documentazione?” urla Taylor infervorato alla folla. “Odio le ripetizioni! Sarebbe
servito un form per raccogliere tutte le domande prima di porle!”
Esatto. Form.
I Form 138
Beh dai, non te la prendere. Non sono uno scrittore vero e proprio, faccio quello che posso. Non
guardarmi così.
Torniamo sui binari.
Questo capitolo, ad essere sincero, mi ha reso molto dubbioso. Ci sono infatti svariati argomenti che
si intersecano tra loro:
Sono tutti e tre capitoli molto grandi. In più, come se non bastasse, ognuno di loro dipende
pesantemente dall’altro!
Non puoi testare la risposta di un form senza sapere come prelevare ed elaborare i dati di una
richiesta, e non puoi ripopolare un form senza sapere cosa va bene e cosa no.
Per cui, ecco come ho strutturato la cosa.
In primis, ho parlato dell’elaborazione dei dati nel capitolo precedente. Insomma, te ne sarai accorto.
Qui, invece, parlerò dei form: come si costruiscono e come aggiungere svariati tipi di campi. Infine,
nel capitolo successivo, parleremo di validazione e ripopolamento.
Direi di cominciare, che ne dici?
Apertura di un Form
Prima di mandare qualsiasi tipo di dato, abbiamo bisogno di sapere dove questi dati andranno a
finire. Abbiamo visto come creare dinamicamente l’URL di una determinata route.
Facciamo due più due e, il risultato, potrebbe essere questo:
Una semplice view, usando il metodo “url()” per generare l’URL della route di destinazione dei dati.
Semplice e veloce.
Ecco, invece, la route in cui viene mostrato il form.
I Form 139
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
Il form è pronto per essere usato. Ora, questo è il metodo che uso normalmente e che preferisco.
Tuttavia, Laravel offre sempre un’alternativa. Ecco un altro metodo di costruzione di form, in questo
caso basato su Blade:
A diffrenza di prima, per aprire e chiudere il form abbiamo usato i metodi “Form::open()” e
“Form::close()”. Il metodo di apertura accetta, come parametro, un array contenente i vari parametri
da specificare.
Ecco il codice HTML generato.
Fantastico, vero?
Sicuramente avrai notato il campo “_token”. Per ora ignoralo: è un campo di sicurezza e più tardi ti
parlerò della sua enorme importanza. Adesso, però, non c’è bisogno neanche di considerarlo, non ti
preoccupare.
I Form 140
Dunque: Laravel sa bene che per quanto riguarda i form il metodo POST è il più usato. Per questo
motivo metterà, di default, questo valore in corrispondenza di “method”.
L’attributo “accept-charset”, inserito automaticamente, serve per il character-encoding ed è impor-
tante in molte situazioni. Ovviamente non mi dilungo, non voglio andare fuori tema più di quanto
non abbia già fatto.
Vediamo come modificare questi valori: ecco un altro esempio.
Abbiamo usato il metodo “GET” e non “POST” e cambiato il charset accettato con “ISO-8859-1”.
L’output, quindi, cambierà di conseguenza:
(Ho sistemato un po’ la formattazione, ma il codice è esattamente quello di prima, con le dovute
modifiche).
Parlando invece del target del form, la sua destinazione, facciamo un piccolo passo indietro. Nel
capitolo dedicato alla generazione degli URL abbiamo visto che la creazione di questi indirizzi può
avvenire grazie a svariati metodi.
Possiamo usarli, tramite alcuni indici speciali, come parametri in apertura del form. Ecco un primo
esempio:
Anzichè usare il classico indice “url”, qui abbiamo usato “route” per specificare la destinazione. In
questo caso, appunto, una route ben definita.
Facciamo un altro esempio, stavolta con una action specifica di un controller.
Il primo parametro, “first_name”, indica l’attributo “name” del campo che questa etichetta sta
descrivendo. Il secondo valore, invece, è il testo della label vero e proprio.
Vediamo subito il codice sorgente generato:
Ottimo! Teoricamente, dovremmo sempre usare le etichette, in modo tale da descrivere al meglio il
form che andiamo a creare.
Non è solo l’attributo “for”, comunque, che possiamo specificare. Un array associativo come terzo
parametro è quello di cui abbiamo bisogno per aggiungere attributi alla nostra label.
I Form 144
In questo caso abbiamo semplicemente aggiunto un attributo “id” ed il valore corrispondente, “first_-
name”.
Ecco il codice generato:
Ora che sappiamo come generare le label possiamo passare ai campi veri e propri.
Campi di Testo
I campi di testo sono quelli più utilizzati in un form. Vediamo subito, quindi, come generarli grazie
al metodo “Form::text()”.
Il primo parametro, come puoi ben immaginare, è il valore dell’attributo “name”, che identifica la
casella stessa. Il secondo, opzionale, è un valore di default da inserire automaticamente all’interno.
Salviamo e controlliamo il codice sorgente.
I Form 145
Esattamente come già avvenuto per “Form::label()”, il terzo parametro, sempre opzionale, è un array
nel quale inserire gli altri attributi del campo.
Ricorda: ogni singolo generatore di questi campi di input ha questa “sintassi”: il primo parametro è
l’attributo “name”, il secondo è il valore di default, il terzo è un array con gli altri attributi, a tuo
piacimento.
Da questo momento in poi evito di ripetermi, altrimenti la cosa diventa troppo noiosa.
Aree di Testo
Le aree di testo sono molto simili ai campi di testo semplici, ma con un’eccezione: permettono di
inserire del testo su più linee, anzichè una sola.
Per generarle abbiamo bisogno del metodo “Form::textarea()”.
Nel caso delle aree di testo, Laravel genera automaticamente alcuni valori di default, come il numero
di colonne e di righe. Puoi cambiarli a piacimento agendo sui parametri aggiuntivi.
Campi Password
Alcune cose, si sa, è meglio se rimangono segrete: quale migliore esempio di una password? Il metodo
“Form::password()” è quello di cui abbiamo bisogno.
Qui il secondo parametro (valore di default) non ha motivo di esistere… sarebbe leggermente assurdo.
Rimane però uguale la regola dell’ultimo parametro.
Checkbox
Le Checkbox ti permettono di ottenere dei valori booleani dai tuoi form. Si possono creare facilmente
con “Form::checkbox()”.
I Form 147
Il primo parametro, come sempre, è il nome. Il secondo è il valore, contenuto nell’attributo “value”,
appunto. Il terzo, infine, è lo status (selezionato/deselezionato).
Di default una checkbox è deselezionata.
Ecco il codice HTML finale:
Pulsanti Radio
I pulsanti radio sono identici, per molti aspetti, alle checkbox. L’unica differenza è che, normalmente,
questi pulsanti si usano per effettuare una scelta in un insieme ristretto di valori.
Vediamone subito un esempio.
Abbiamo usato il metodo “Form::radio()” per creare i vari pulsanti. Da notare il primo parametro,
uguale per tutti: serve a fare in modo che l’utente possa scegliere solo un colore.
Vediamo il codice HTML generato:
I Form 148
Select
Le Select, o drop down, sono un altro modo di far scegliere all’utente un valore specifico in un
determinato insieme. Il metodo da usare, stavolta, è “Form::select()”.
Se sai come è fatto un select avrai già capito a cosa serve il secondo parametro: è l’insieme dei
valori da cui scegliere. L’indice è il valore letto successivamente, mentre il valore corrispondente è
l’etichetta da mostrare all’utente.
Il terzo parametro, invece, indica il valore selezionato di default in fase di creazione del campo.
Et voilà!
I Form 149
Volendo, se necessario, puoi anche riorganizzare i valori nella select secondo una o più categorie. Per
abilitare questa opzione non dovrai fare altro che specificare un array multidimensionale al posto
di quello classico, visto poco fa.
Campo Email
Il tipo di campo “email” ha la stessa sintassi del classico “Form::text()” visto poco fa. La differenza
sostanziale rispetto all’altro è infatti il risultato: viene creato un campo di tipo “email” compatibile
con il nuovo standard HTML5, che provvederà a validare il valore immesso autonomamente.
Ecco subito un esempio.
Il primo parametro, l’unico immesso, è l’attributo “name” da usare per il campo. Ovviamente, per
fare in modo che il campo funzioni, dobbiamo specificare nel tag di apertura del form il multipart
encoding.
A lei il codice, Sir.
Campi Nascosti
A volte alcuni campi non devono essere necessariamente mostrati. Usare i campi nascosti è un buon
modo per gestire dati aggiuntivi nei form che creiamo.
Il metodo “Form::hidden()” serve a questo scopo.
I Form 152
La stringa passata come primo parametro, in questo caso, è il testo da mostrare sul pulsante.
I Form 153
Perfetto! Adesso possiamo anche inviare i nostri form! Vediamo, però, qualche altro tipo di pulsante.
Pulsante Normale
A volte potremmo aver bisogno di un pulsante semplice, non collegato all’azione di invio del form.
In tal caso serve “Form::button()”.
I parametri da usare sono esattamente gli stessi, non cambia praticamente niente.
Pulsante “Image”
Al posto di un pulsante nativo, inoltre, potresti avere la necessità di usare un image button HTML5
per il tuo form.
Di seguito un esempio:
I Form 154
Il primo parametro specificato è l’url dell’immagine da usare, mentre il secondo è il testo (value) da
mostrare.
Ed ecco quindi il codice HTML generato.
Pulsante Reset
I pulsanti di reset possono essere usati per “pulire” un form dai suoi contenuti. Molto spesso può
essere usato dall’utente nel momento in cui effettua più di un errore e vuole ricominciare.
Macro
Nella sottosezione precedente abbiamo scoperto svariate tipologie di input e relativi metodi di
generazione. Possono essere usati per risparmiare un sacco di tempo.
Laravel, però, può fare di più: perché non definire dei blocchi di input personalizzati, in modo tale da
averli sempre pronti? Esatto, stiamo parlando di quelle che nel gergo di Laravel chiamiamo “Macro”.
Innanzitutto: dove metterle? Creiamo un file “app/macros.php”, in modo tale poi da includerlo dal
file delle routes.
Eccone i contenuti:
1 <?php
2 // app/macros.php
3
4 Form::macro('fullName', function(){
5 return '<p>Full name: <input type="text" name="full_name"></p>';
6 });
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 return Form::fullName();
6 });
La nostra route ritorna automaticamente il valore di “fullName()”. Il metodo “fullName”, però, non
esiste! Con un po’ di sana magia, infatti, Laravel elabora automaticamente questo nome in base
anche alle macro presenti!
Non è fantastico?
Ecco cosa è stato creato:
I Form 156
Cosa fare, invece, se volessimo specificare dei parametri aggiuntivi alle nostre macro? Detto fatto:
1 <?php
2 // app/macros.php
3
4 Form::macro('fullName', function($name){
5 return '<p>Full name: <input type="text" name="'.$name.'"></p>';
6 });
Attraverso i placeholders è assolutamente semplice fare una cosa del genere. Nell’esempio appena
visto, per capirci, ho dato la possibilità di specificare l’attributo “name”.
Adesso, invece, aggiorniamo il codice nella route:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function(){
5 return Form::fullName('my_field');
6 });
Quello di cui abbiamo bisogno è, semplicemente, un modo di evitare che i dati elaborati dalla nostra
applicazione provengano esattamente dalla nostra applicazione! In caso contrario, infatti, entra in
gioco quello che è conosciuto come “Cross Site Request Forgery”. CSRF, per gli amici.
Insomma: evitare ogni possibile trasmissione dall’esterno che non sia quella dell’utente.
A questo punto ti chiedo: ricordi il “_token” automaticamente inserito nel form visto poco tempo
fa?
Si? Bene!
Vedilo come una sorta di “chiave” che rende valida la trasmissione dei dati del form. In fase di
elaborazione dei dati inseriti, se questa chiave non esiste o è sbagliata allora l’elaborazione stessa
viene bloccata, per sicurezza.
Come fare questo controllo? Semplice.
Bisogna comparare il valore di “_token” con quello di “Session::token()”. Se il valore è uguale allora
è tutto ok.
1 <?php
2 // app/routes.php
3
4 Route::post('/handle-form', array('before' => 'csrf', function() {
5 // Handle our posted form data.
6 }));
Agganciando questo filtro, infatti, tutti i controlli di validità vengono svolti automaticamente e non
c’è bisogno di scriverli di volta in volta.
Più in la, in un capitolo apposito, vedremo anche come gestire tutti gli errori ottenuti in caso di
problemi di questo genere (e non solo).
Usando “Form::open()” il token viene generato automaticamente. Se invece stai scrivendo il codice
a mano e hai bisogno del token di controllo, basta usare il metodo “Form::token()”:
I Form 158
Ricorda: fin quando non vuoi abilitare altre applicazioni all’accesso nella tua, fai in modo di usare
sempre questo tipo di protezione nei form.
Validazione
Qualche mese fa la situazione era davvero stressante. Lavorare a Laravel 4 non si limitava al
solo sviluppo del framework: dovevo stare attento alla costruzione del nuovo sito, modificare la
documentazione, senza poi considerare il mio ruolo di supporto che un buon creatore di framework
deve sempre assumere, in queste circostanze. Un periodo piuttosto impegnativo. Come se non fosse
abbastanza, inoltre, ero impegnato anche in parallelo con la stesura di Code Bright: volevo farlo
bene, in modo tale da attirare più sviluppatori possibile verso il framework.
Avevo bisogno di staccare un po’. Stavo pensando ad uno dei Phill’s Parks. I Phill’s Parks sono dei
parchi di divertimento che puoi trovare un po’ in tutto il mondo. Sono stati creati dal CEO di Phill’s
Parks, Phill Sparks, nei primi anni duemila. Un posto magico dove gli sviluppatori possono andare
per riprendersi dallo stress della vita quotidiana.
A saperlo prima sarebbe stato più semplice rilassarsi e godersi al meglio quel paio di settimane prima
del lancio. Ah, tra l’altro, sapevi che come lettore di Code Bright c’è anche uno sconto del 90%?
Beh, ad essere sincero, quando ho cominciato a scrivere il capitolo avevo un bel piano in mente. Poi
però mi sono lasciato distrarre da tutti quei divertimenti e… maledizione.
No, scherzo. Continua a seguire la storia.
Dicevo, i Phill’s Parks sono un posto davvero speciale per qualsiasi sviluppatore. Solo per gli
sviluppatori.
C’è un problema, però, con gli sviluppatori: se non sei attento, a volte, è difficile capire chi è davvero
uno sviluppatore e chi no. Insomma, nessuno vuole dei designer nei Phill’s Parks. Immaginali
blaterare qualcosa sul Comic Sans dei menu del bar.
C’è bisogno di… esatto, validare all’ingresso!
Quando validiamo qualcosa decidiamo una o più regole per uno o più attributi. Proviamo a fare una
lista di questi attributi:
• Bevanda preferita - Browser preferito - Tipo di barba - Capacità di conversazione con una
donna
Come set di attributi direi che ci siamo. Ora dobbiamo capire quali sono le regole per riconoscere
subito un buon sviluppatore da ammettere liberamente.
Validazione 160
Disclaimer: ehi, si scherza. Conosco un sacco di ragazze che sviluppano e sono anche sicuro che
sappiano parlare con disinvoltura alle altre ragazze. Un conto è scherzare, ma evitiamo i luoghi
comuni. Grazie.
Tornando a noi, ecco che arriva qualcuno ai cancelli. Vediamo se può entrare o no:
Validazione Semplice
Non fidarti mai dei tuoi utenti. Se c’è una cosa che ho imparato lavorando nell’industria IT è proprio
questa. Se hai una qualsiasi falla nella tua applicazione, un utente prima o poi la troverà. Anche la
più nascosta, la più remota. Quella che non ti aspetti mai.
La validazione è sicuramente un buon modo per tappare una buona parte di questi buchi, e l’obiettivo
della validazione è quello di ottenere, sempre, un buon input.
Tuttavia, cosa è effettivamente un “buon input”?
Vediamo un esempio per capire meglio.
Supponiamo di avere un form HTML per collezionare dei dati riguardo la registrazione di nuovi
utenti alla nostra applicazione. Fa sempre bene rivedere un po’ di cose già fatte, per cui ecco il form
fatto con Blade.
Validazione 161
Come puoi vedere, inoltre, il form punta alla route “/registration”. Tutti i dati in POST, quindi,
finiranno li ed è proprio li che saranno elaborati.
Creiamo al volo un paio di route da usare.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('/registration', function() { $data = Input::all();
7 // data handling...
8 });
Con “/registration”, quindi, elaboriamo i dati. Tuttavia, anche se ora si trovano li, in $data, non
possiamo ancora usarli. Perché? Ora lo vedi.
Vai all’indirizzo “/”. NON compilare il form e premi sul pulsante di invio.
Cosa è successo? Schermata bianca, eh?
Validazione 162
Si!
Beh, te lo dico io cosa è successo: anche senza nessun dato (tutti i campi sono rimasti vuoti) il form
è stato inviato ugualmente, senza nessun controllo.
Direi proprio che non va bene, anche perché è un problema non indifferente per quanto riguarda la
sicurezza.
Ragion per cui, adesso, andiamo ad aggiungere una validazione di base al nostro form.
Innanzitutto:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('/registration', function() {
7 $data = Input::all();
8 $rules = array( 'username' => 'alpha_num' );
9 });
Cominciamo dalle cose semplici. L’array associativo $rules contiene un certo numero di indici (in
questo caso un indice solo, ‘username’).
Qui stiamo dicendo che, per essere valido, il campo “username” del form deve avere un contenuto
OBBLIGATORIAMENTE alfanumerico.
Ora configuriamo l’oggetto necessario a validare il form, in modo tale che $rules non rimanga un
array senza nessuno scopo.
Abbiamo bisogno dell’oggetto Validation.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
Validazione 163
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'alpha_num'
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18 });
Il metodo Validator::make() crea una nuova istanza del Validatore, prendendo in input i dati da
validare e le regole da noi specificate.
Nota importante: in questo caso stiamo validando i dati del form, ma il validatore può essere usato
per qualsiasi array!
Ora che abbiamo creato un’istanza del validatore possiamo usarlo per fare tutti i controlli.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'alpha_num'
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18
19 if ($validator->passes()) {
20 // Normally we would do something with the data.
21 return 'Data was saved.';
22 }
23
24 return Redirect::to('/'); });
25 });
Validazione 164
Per testare le varie regole non dobbiamo fare altro che ricorrere al metodo “passes()”. Questo metodo
ritornerà un booleano che indicherà l’esito dell’operazione: “true” se sarà andato tutto bene, “false”
in caso di errori.
Torniamo al nostro form. Stavolta proviamo ad inserire, come username, la stringa “johnzoidberg”.
Clicchiamo quindi sul pulsante di invio e…
Data was saved.
Perfetto! Adesso proviamo a far fallire la validazione. Torniamo nuovamente al form e, come
username, inseriamo il valore “!!!”.
Premi sul pulsante di invio nuovamente e…
Esatto! Hai inserito un valore non valido e il form non ha processato i tuoi dati.
Ora, il metodo “passes()” è ottimo. Forse un po’ troppo ottimista. Si sa, il mondo non è perfetto: non
siamo tutti Robert Downey Jr.
Anche per questo motivo abbiamo creato il metodo opposto: “fails()”. Eccolo in azione.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'alpha_num'
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18
19 if ($validator->fails()) { return Redirect::to('/'); }
20
21 return 'Data was saved.';
22 });
Validazione 165
Il metodo “fails()” ritorna una booleana opposta a quella ritornata da “passes()”. Ovvio, no?
Adesso proviamo a cambiare un po’ le regole di validazione. Togliamo la regola “alpha_num” e
mettiamo un controllo sulla lunghzza minima: è la volta di “min”.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'min:3'
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18
19 if ($validator->passes()) {
20 // Normally we would do something with the data.
21 return 'Data was saved.';
22 }
23
24 return Redirect::to('/'); });
25 });
La regola “min” serve ad assicurarsi che il campo “username” sia lungo almeno 3 caratteri. Questo
parametro, il 3, è specificato subito dopo i due punti “:”.
Questa regola, in particolare, è speciale: in base al tipo di dato passato, infatti, si comporterà
diversamente e di conseguenza! Se gli passiamo una stringa ne controllerà la lunghezza. Passandogli
un numero intero, invece, farà il confronto matematico sul valore. Passandogli un campo di tipo file,
invece, farà il controllo sulla grandezza del file! In questo caso, ad esempio, si assicurerà di avere un
file grande almeno 3 bytes.
Meraviglioso, non è vero?
Proviamo subito questa nuova regola: vai nuovamente al form “/” ed inserisci nel campo username il
valore “Jo”. Ancora una volta verrai reindirizzato alla pagina di registrazione. Nessuna elaborazione:
Jo è più corto di 3 caratteri.
Validazione 166
Ora che le cose cominciano ad essere più chiare, perché non provare più regole insieme, contempo-
raneamente? Laravel permette tranquillamente di farlo, quindi approfittiamone!
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'alpha_num|min:3'
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18
19 if ($validator->passes()) {
20 // Normally we would do something with the data.
21 return 'Data was saved.';
22 }
23
24 return Redirect::to('/'); });
25 });
Le varie regole per uno stesso campo possono essere separate dal pipe (“|”, senza virgolette). Ad
essere sincero il libro sarebbe uscito molto prima se non avessi impiegato 30 secondi a cercare ogni
benedetta volta il pipe.
Comunque non temere: usare il pipe non è il solo modo di specificare le regole di validazione.
Insomma, potresti anche non essere un nonno degli anni 60. L’abbiamo messo in cantiere.
So già cosa stai pensando e ti confermo: array multidimensionali!
OH, SI!
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => array('alpha_num', 'min:3')
14 );
15
16 // Create a new validator instance.
17 $validator = Validator::make($data, $rules);
18
19 if ($validator->passes()) {
20 // Normally we would do something with the data.
21 return 'Data was saved.';
22 }
23
24 return Redirect::to('/'); });
25 });
Regole di Validazione
Bene. Ci siamo.
Innanzitutto ricorda: ci sono tante, tante, troppe regole di validazione che puoi usare. Per cui massima
attenzione, visto che andremo il più a fondo possibile per ognuna di queste.
Se stai leggendo il libro in un ritaglio di tempo, magari prima di andare a dormire, metti tutto via e
addormentati. Ci rivediamo quando sei totalmente sveglio.
Queste di seguito sono le regole di validazione, in ordine rigorosamente alfabetico.
Validazione 168
accepted
Questa regola può essere usata per assicurarsi che sia stata data, dall’utente, una conferma “positiva”.
Cosa vuol dire di preciso? Semplice, che il campo in oggetto deve avere un valore a scelta tra i
seguenti: “yes”, “on” oppure 1.
Potrebbe essere utile, magari, per l’accettazione di un qualsiasi disclaimer o liberatoria.
Esempio:
active_url
La regola di validazione “active_url” si assicura, prima di approvare il campo specificato, che il
suo contenuto corrisponda ad un indirizzo internet attivo. Il controllo viene effettuato tramite
“checkdnsrr()”, che non solo valida la struttura dell’URL, ma anche la sua reperibilità.
Esempio:
after
La regola “after” accetta un singolo parametro: una rappresentazione (sottoforma di stringa) di una
data. In questo modo il sistema controlla che il campo vada a combaciare con una data successiva a
quella specificata.
Esempio:
alpha
La regola “alpha” si assicura che il campo in oggetto contenga solo caratteri alfabetici.
Esempio:
Validazione 169
alpha_dash
La regola “alpha_dash” si assicura che il campo in oggetto contenga esclusivamente caratteri
alfabetici, dash “-“ e underscore “_”.
Esempio:
alpha_num
La regola “alpha_num” si assicura che il campo in oggetto contenga solo ed esclusivamente caratteri
alfanumerici. Utile per la validazione di username.
Esempio:
before
La regola “before” accetta, come “after”, un singolo parametro. In questo caso, il valore inserito del
campo deve essere cronologicamente collocato prima della data usata come parametro. I valori sono
convertiti in timestamp utilizzando il metodo PHP “strtotime()”.
Esempio:
between
La regola “between” accetta due parametri: il valore inserito nel campo è quello che deve rientrare
nel range delimitato dai due valori specificati.
Ovviamente, il tipo di confronto cambia in base al tipo di variabile: in caso di campi numerici viene
fatto un confronto puramente matematico, in caso di stringhe ad essere controllata è la lunghezza.
In caso di file, invece, la grandezza in bytes.
Esempio:
Validazione 170
confirmed
La regola “confirmed” può essere usata come prova dell’esistenza e, appunto, conferma di un altro
campo. Un esempio classico è la conferma della password.
Il campo di conferma deve avere lo stesso nome dell’originale, con l’aggiunta di “_confirmation”.
La regola va applicata al campo originale.
Un esempio quindi è: “password” e “password_confirmation”.
Esempio:
date
La regola “date” si assicura che il campo inserito sia una data valida. Il controllo viene effettuato
tramite il metodo “strtotime()” di PHP.
Esempio:
date_format
La regola “date_format” ha il compito di assicurarsi che un determinato campo presenti una data
nel formato specificato come parametro.
Esempio:
different
La regola “different” è quasi il contrario di “confirmed”, visto poco fa. Se con “confirmed” si cerca
un valore uguale, con “different” il valore inserito deve essere diverso rispetto a quello di un altro
campo.
L’altro campo da controllare viene specificato come parametro.
Esempio:
Validazione 171
email
La regola “email” controlla se il testo inserito nel campo in oggetto è un indirizzo email valido.
Esempio:
exists
La regola di validazione “exists” si assicura che il valore inserito sia presente nel database. Come
parametri vengono specificati il nome della tabella e il campo da usare per effettuare il check.
Può essere usato, ad esempio, per controllare un utente di riferimento durante una registrazione (il
classico “se sei arrivato grazie ad un altro utente, inserisci il suo username in questo campo”).
Esempio:
Nell’esempio abbiamo cercato l’username specificato solo tra gli utenti amministratori.
image
La regola “image” si assicura che il file caricato sia un’immagine valida. Viene controllata l’estensione
in modo tale che sia “.bmp”, “.gif”, “.jpeg” o “.png”.
Esempio:
in
La regola “in” controlla che il valore inserito nel campo sia in una lista di valori specificata come
parametro.
Esempio:
Validazione 172
integer
La regola “integer” controlla che il valore inserito nel campo sia un numero intero. Difficile, lo so.
Esempio:
ip
La regola “ip” si assicura che il campo contenga un indirizzo IP valido.
Esempio:
max
La regola di validazione “max” è l’esatto opposto di “min” vista precedentemente negli esempi. Si
assicura che il campo contenga un valore minore del parametro specificato.
Il controllo, come prima, varia in base al tipo: matematico in caso di valori numerici, sulla lunghezza
in caso di stringhe o sulla grandezza in caso di files.
Esempio:
mimes
La regola “mimes” ha il compito di assicurarsi che un file caricato sia di un tipo compreso tra quelli
inseriti come parametri nella specifica.
Esempio:
Validazione 173
min
Già vista prima negli esempi, la regola “min” funziona esattamente come “max”.
Esempio:
not_in
La regola “not_in” dichiara valido un determinato campo solo se il valore non è tra quelli inseriti
come parametri.
Esempio:
numeric
La regola “numeric” si assicura che il campo sia, appunto, numerico. In tal caso viene dichiarato
valido.
Esempio:
regex
La regola “regex” è sicuramente una delle più flessibili dell’intero sistema: controlla infatti che il
campo inserito risponda positivamente al test di controllo di un espressione regolare specificata
come parametro.
Eviterò di addentrarmi nel mondo delle espressioni regolari: dovrei scrivere un libro a parte.
Nota: dato che il carattere pipe “|” può essere usato all’interno di un’espressione regolare, dovrai
usare necessariamente un array multidimensionale per specificare le varie regole di un campo che
fa uso anche di questa regola.
Esempio:
Validazione 174
required
La regola “required” determina semplicemente l’obbligatorietà di un campo.
Esempio:
required_if
La regola “required_if” è simile a “required”, con la differenza che il campo in oggetto è obbligatorio
solo se il campo specificato come primo parametro presenta un valore uguale a quello specificato
nel secondo campo.
Esempio:
required_with
La regola “required_with” dichiara obbligatorio il campo in oggetto solo se sono presenti anche gli
altri campi inseriti tra i parametri.
Esempio:
required_without
Al contrario di “required_with”, “required_without” determina come obbligatorio il campo in
oggetto solo in assenza dei campi specificati tra i parametri.
Esempio:
Validazione 175
same
La regola “same” viene usata per assicurarsi che il valore del campo in oggetto sia uguale a quello
del campo definito come parametro.
size
La regola di validazione “size” viene usata per assicurarsi che il campo in oggetto abbia una certa
grandezza. Il controllo varia in base al tipo di dato: sulla lunghezza in caso di stringa, sulla grandezza
in caso di file, matematicamente in caso di campo numerico.
Esempio:
unique
La regola “unique” si assicura che il campo in oggetto contenga un valore unico nel contesto del
database dell’applicazione. Come parametri vengono specificati il nome della tabella ed il campo da
controllare.
Un esempio concreto di questa applicazione è il check, in fase di iscrizione, dell’esistenza di un
username. Se già esiste si blocca la procedura.
Esempio:
Come ulteriore aggiunta si può specificare anche un insieme di ID (come parametri aggiuntivi) da
ignorare nel controllo.
url
La regola “url” ha il compito di assicurarsi che il valore presente nel campo in oggetto sia un URL
valido. A differenza di “active_url” non vengono eseguiti check DNS.
Esempio:
Validazione 176
Wow… che lista, vero? Ce l’abbiamo fatta: abbiamo visto tutte le regole. Non è finita qui però, dato
che dobbiamo vedere come gestire gli eventuali messaggi di errore, nel caso le regole non vengano
rispettate.
Messaggi di Errore
All’inizio del capitolo abbiamo visto come effettuare la validazione e come, eventualmente, effettuare
un redirect in caso di errore.
C’è da dire, però, che come feedback non è un granchè: l’utente sbaglia, certo, ma bisogna fargli
capire dove!
Qui entra in gioco Laravel: è possibile, infatti, presentare dei messaggi di errore per ogni “infrazione”
da parte dell’utente.
In questa parte del capitolo dedicato alla validazione vediamo insieme come mostrare questi errori.
Vediamo subito un primo esempio:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('/registration', function() {
7 // Fetch all request data.
8 $data = Input::all();
9
10 // Build the validation constraint set.
11 $rules = array(
12 'username' => 'alpha_num'
13 );
14
15 // Create a new validator instance.
16 $validator = Validator::make($data, $rules);
17
18 if ($validator->passes()) {
19 // Normally we would do something with the data. return 'Data was saved.';
20 }
Validazione 177
21
22 // Collect the validation error messages object.
23 $errors = $validator->messages();
24
25 return Redirect::to('/');
26 });
Nell’esempio qui sopra abbiamo “preso” i vari messaggi di errore usando il metodo “messages()”,
dall’istanza del validatore. Ora, visto che dobbiamo reindirizzare l’utente verso la pagina del form,
dobbiamo anche passare al form gli errori da visualizzare.
Beh, potremmo usare il metodo “withErrors()”, no?
Giuro. Guarda:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('/registration', function() {
7 // Fetch all request data. $data = Input::all();
8
9 // Build the validation constraint set.
10 $rules = array(
11 'username' => 'alpha_num'
12 );
13
14 // Create a new validator instance.
15 $validator = Validator::make($data, $rules);
16
17 if ($validator->passes()) {
18 // Normally we would do something with the data.
19 return 'Data was saved.';
20 }
21
22 return Redirect::to('/')->withErrors($validator);
23 });
Da notare che ho passato l’istanza del validatore al metodo “withErrors()” e non solo i messaggi. Ci
pensa direttamente il metodo a prelevare i dati che servono.
Ancora un esempio:
Validazione 178
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() { return View::make('form'); });
5
6 Route::post('/registration', function() {
7 // Fetch all request data.
8 $data = Input::all();
9
10 // Build the validation constraint set.
11 $rules = array(
12 'username' => 'required|alpha_num|min:3|max:32',
13 'email' => 'required|email',
14 'password' => 'required|confirm|min:3'
15 );
16
17 // Create a new validator instance.
18 $validator = Validator::make($data, $rules);
19
20 if ($validator->passes()) {
21 // Normally we would do something with the data.
22 return 'Data was saved.';
23 }
24
25 return Redirect::to('/')->withErrors($validator);
26 });
Sistemate le regole e i test, vediamo come visualizzare i messaggi di errori nella schermata del form.
Quando la view viene caricata, la variabile $errors viene aggiunta tra i dati della view stessa. È
sempre li: in questo modo non dovrai curarti di controllare la sua esistenza.
Grazie al metodo “withErrors()” infatti questa procedura di aggiunta avviene automaticamente.
A quel punto, usando il metodo “all()” di $errors possiamo accedere ai vari errori, mostrandoli uno
alla volta in un elenco puntato.
Adesso proviamo! Visitiamo “/” ed inseriamo qualche valore, per ottenere degli errori. Premiamo sul
pulsante di invio e…
Ecco cosa succede:
• The username field is required. - The email field is required. - The password field is required.
Fantastico! La nostra applicazione, adesso, dice all’utente cosa ha sbagliato, in modo tale da
velocizzare la correzione.
Sembra un aspetto stupido ma in realtà è importantissimo: ricorda, un’interfaccia gestita male
allontana l’utente.
Facciamo qualche altra prova modificando la view:
Validazione 180
Grazie al metodo “get()” possiamo ottenere i vari messaggi di errore in base al campo specificato
come parametro.
Proviamo nuovamente: inseriamo, nel campo username, un semplice punto esclamativo.
Ecco il risultato:
• The username may only contain letters and numbers. - The username must be at least 3
characters.
Basta usare il metodo “first()”, specificando come paramtro il nome del campo, per ottenere il primo
messaggio di errore.
In questo modo, replicando l’esprimento di prima, ecco quale sarà il risultato:
Di default, “messages” del validatore ritorna un array vuoto, o null, nel caso non ci siano messaggi
da mostrare all’utente. Questo vuol dire che, come già detto prima, non saranno necessari controlli
vari ed eventuali sull’esistenza di $errors.
Il che si traduce in codice più pulito.
Tuttavia, se proprio dovessi aver bisogno di eseguire un controllo, non ti preoccupare: c’è sempre il
metodo “has()”:
Validazione 182
Negli esempi precedenti (quelli con i metodi “all()” e “first()”, per capirci) avrai notato che i messaggi
di errore sono stati inseriti in un codice HTML. Anche senza nessun errore, un po’ di questo codice
(i tag di apertura e chiusura dell’item) sono stati mostrati lo stesso.
Possiamo facilmente evitare questo problema, passando come secondo parametro dei metodi “all()”
e “first()” l’HTML necessario al ripetitore.
Ecco un esempio:
1 <?php
2 // app/routes.php
3
4 Validator::extend('awesome', function($field, $value, $params) {
5 return $value == 'awesome';
6 });
7
8 Route::get('/', function() { return View::make('form'); });
9
10 Route::post('/registration', function() {
11 // Fetch all request data.
12 $data = Input::all();
13
14 // Build the validation constraint set.
15 $rules = array(
16 'username' => 'awesome',
17 );
18
19 // Create a new validator instance.
20 $validator = Validator::make($data, $rules);
21
22 if ($validator->passes()) {
23 // Normally we would do something with the data.
24 return 'Data was saved.';
25 }
26
27 return Redirect::to('/')->withErrors($validator);
28 });
Nota: non c’è una location predefinita in cui inserire le regole di validazione. Puoi tranquillamente
creare un file “validators.php” ed includerlo, se ne hai bisogno.
Cosa abbiamo fatto? Semplicemente, esteso il validatore creando la regola “awesome”. Scendiamo
nel dettaglio e vediamo com’è strutturata questa estensione.
1 <?php
2 // app/routes.php
3
4 Validator::extend('awesome', function($field, $value, $params) {
5 return $value == 'awesome';
6 });
La closure dovrà obbligatoriamente ritornare true, nel caso la regola venga rispettata, oppure false
in caso di problemi.
I parametri passati alla closure, invece, sono: una stringa contenente il nome del campo da validare,
il valore del campo in questione e un array da personalizzare per specificare eventuali parametri
aggiuntivi.
Sempre nell’ottica della scelta, inoltre, Laravel da la possibilità di creare una classe a se stante per
ogni nuova regola di validazione.
Ecco un esempio pratico:
1 <?php
2 // app/validators/CustomValidation.php
3
4 class CustomValidation {
5 public function awesome($field, $value, $params) {
6 return $value == 'awesome';
7 }
8 }
Come puoi vedere, la classe contiene il metodo “awesome()” che corrisponderà poi alla regola
“awesome”. Questo vuol dire che una classe che estende il validatore potrà avere tutte le regole
aggiuntive che desideri.
Ancora una volta non c’è un posto predefinito da usare per depositare tutte queste classi. Puoi
decidere tu dove metterle: a me piace creare una cartella “app/validators” ed aggiungerla via
composer.
Comunque, direi che ci siamo!
Diamine, stavo quasi per scordarmi. Come sempre, bisogna usare il metodo “Validator::extend()”.
Serve per fare “l’aggancio” delle nuove regole.
1 <?php
2 // app/routes.php
3
4 Validator::extend('awesome', 'CustomValidation@awesome');
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function() { return View::make('form'); });
6
7 Route::post('/registration', function() {
8 // Fetch all request data.
9 $data = Input::all();
10
11 // Build the validation constraint set.
12 $rules = array(
13 'username' => 'min:3',
14 );
15
16 // Build the custom messages array.
17 $messages = array(
18 'min' => 'Yo dawg, this field aint long enough.'
19 );
20
21 // Create a new validator instance.
22 $validator = Validator::make($data, $rules, $messages);
23
24 if ($validator->passes()) {
25 // Normally we would do something with the data.
26 return 'Data was saved.';
27 }
28
Validazione 187
29 return Redirect::to('/')->withErrors($validator);
30 });
1 <?php
2 // Build the custom messages array.
3 $messages = array(
4 'min' => 'Yo dawg, this field aint long enough.'
5 );
6
7 // Create a new validator instance.
8 $validator = Validator::make($data, $rules, $messages);
I messaggi personalizzati vengono specificati nell’array $messages. Ogni indice dell’array identifica
la regola interessata dal messaggio. In questo caso stiamo specificando un messaggio di errore
alternativo per la regola “min”.
Stessa cosa vale per le regole di validazione personalizzate: non cambia nulla.
Ecco un esempio:
1 <?php
2
3 // Build the custom messages array.
4 $messages = array(
5 'awesome' => 'Please enter a value that is awesome enough.'
6 );
7
8 // Create a new validator instance.
9 $validator = Validator::make($data, $rules, $messages);
Inoltre, se necessario, possiamo decidere di mostrare un messaggio di errore specifico, oltre che per
una singola regola, anche per una regola applicata al singolo campo.
Basta separare con un punto (“.”) il nome del campo dalla regola. Come nell’esempio seguente:
Validazione 188
1 <?php
2
3 // Build the custom messages array.
4 $messages = array(
5 'username.min' => 'Hmm, that looks a little small.'
6 );
7
8 // Create a new validator instance.
9 $validator = Validator::make($data, $rules, $messages);
Il messaggio qui specificato viene mostrato SOLO nel caso in cui la regola “min” non viene rispettata
per il campo “username”.
Bene: anche per quanto riguarda la validazione è, temporaneamente, tutto.
Adesso riposati. Il capitolo è stato lungo e stiamo per cambiare totalmente argomento: si parte con
il mondo dei Database in Laravel.
Database
Bene, è arrivato il momento di fare una confessione: non sono un grande fan dei database. Da piccolo
sono stato morso da uno di loro, mentre un altro ha anche ammazzato mio fratello.
No dai, non è vero, non ho fratelli.
Comunque, a parte gli scherzi, non mi fanno impazzire. Sarà perché preferisco le cose più “visuali”,
mentre i database sono una raccolta di tabelle. L’anti-divertimento.
Per fortuna, da quando uso Laravel, ho molto poco a che fare con database, SQL e così via. Anzi,
in realtà non è una cosa che riguarda solo Laravel: gli strumenti che noi conosciamo come ORM ci
semplificano la vita di tutti i giorni enormemente.
Tra un po’, quindi, parleremo di Eloquent, l’ORM usato da Laravel: a cosa serve, cosa fa, come si
usa. Ti assicuro, sarà un’esperienza assolutamente piacevole.
Prima di cominciare a scrivere codice, però, vorrei parlare un po’ del concetto di database in relazione
a Laravel.
Perché ne abbiamo bisogno? Beh, perché non dovremmo?
La tua applicazione non dovrà forse memorizzare dei dati da mostrare ed usare in futuro?
Ok, allora non avrai bisogno di un database. In caso contrario, però, avrai bisogno di un modo per
memorizzare, prelevare, cancellare e modificare dei dati.
Astrazione
Innanzitutto: quali database possiamo usare con Laravel 4?
Eccoli:
• SQL Server¹⁷
Come puoi vedere c’è una gran bella scelta. Nella stesura di questo libro, in ogni caso, ho usato per
le mie prove MySQL Community Server Edition. È gratuito ed è un ottima scelta, oltre ad essere
una delle distribuzioni più usate nello sviluppo.
Se, però, dovessi decidere di cambiare server, ad un certo punto, non dovrai preoccuparti di niente:
Laravel infatti presenta un livello di astrazione.
Partendo dalla classica query SQL diretta, fino ad arrivare ai componenti più sofisticati, tutto
viene astratto in modo tale da offrirti dei metodi che, con una minima configurazione, funzionano
ugualmente con tutti i tipi di database che abbiamo appena visto.
Un altro vantaggio nell’usare Laravel per l’interazione con un database è sicuramente la sicurezza.
In tutte le situazioni (a meno che non ti dica espressamente il contrario) non dovrai neanche
preoccuparti di fare l’escape della query.
E non hai ancora visto niente: aspetta di vedere in azione l’intero sistema e poi voglio vedere se dici
“no, grazie, voglio solo mostrare pagine statiche”!
Pfui.
Dopo questa introduzione passiamo alla configurazione.
Configurazione
Tutto quello che ti serve per configurare il livello di astrazione database di Laravel si trova nel file
“app/config/database.php”. Alla fine è facile da ricordare, no?
Vediamo da cosa è composto questo file:
1 /*
2 |--------------------------------------------------------------------------
3 | PDO Fetch Style
4 |--------------------------------------------------------------------------|
5 | By default, database results will be returned as instances of the PHP |
6 | stdClass object; however, you may desire to retrieve records in an array |
7 | format for simplicity. Here you can tweak the fetch style. |
8 */
9
10 'fetch' => PDO::FETCH_CLASS,
Il Fetch Style: quando effettuiamo una query, PHP di default ritornerà una classe (o un array di
classi). Cosa significa? Che sarai in grado di accedere ai dati in questo modo.
¹⁷http://www.microsoft.com/en-us/sqlserver/default.aspx
Database 191
1 <?php
2
3 echo $book->name; echo $book->author;
A volte, però, potresti aver bisogno di ricevere un array al posto di un oggetto. Non devi fare altro
che cambiare il valore di questa opzione, da PDO::FETCH_CLASS in PDO::FETCH_ASSOC.
Le modalità di accesso diventeranno:
1 <?php
2
3 echo $book['name']; echo $book['author'];
Se vuoi una lista completa delle varie modalità di accesso, dai un’occhiata alla Documentazione PHP
PDO¹⁸ e cerca le varie costanti che cominciano con FETCH_.
Ora passiamo all’array delle connessioni: il punto chiave per l’accesso ad un qualsiasi database.
1 <?php
2
3 'connections' => array(
4
5 'sqlite' => array(
6 'driver' => 'sqlite',
7 'database' => __DIR__.'/../database/production.sqlite',
8 'prefix' => '',
9 ),
10
11 'mysql' => array(
12 'driver' => 'mysql',
13 'host' => 'localhost',
14 'database' => 'database',
15 'username' => 'root',
16 'password' => '',
17 'charset' => 'utf8',
18 'collation' => 'utf8_unicode_ci',
19 'prefix' => '',
20 ),
21
22 'pgsql' => array(
23 'driver' => 'pgsql',
¹⁸http://www.php.net/manual/en/pdo.constants.php
Database 192
Sono un bel po’ di connessioni, vero? Lo so, ma ne abbiamo preparate un po’ con dei valori di default
in modo tale da renderti più facile la vita all’inizio.
Ad una prima occhiata starai pensando che ad ogni indice corrisponde una connessione ad un
determinato database. Invece no: hai fatto caso a cosa c’è in corrispondenza dell’indice “driver”
in ogni elemento?
La modalità di connessione viene indicata proprio qui, in “driver”. Il secondo elemento, ad esempio,
usa il driver “mysql”.
La scelta di questa struttura non è casuale: in base alle necessità, infatti, potremmo avere la necessità
di dialogare con due database diversi.
Come in questo esempio:
1 <?php
2
3 'connections' => array(
4
5 'mysql' => array(
6 'driver' => 'mysql',
7 'host' => 'localhost',
8 'database' => 'database',
9 'username' => 'root',
10 'password' => '',
11 'charset' => 'utf8',
Database 193
Insomma, il primo indice non è altro che un “nickname” per la connessione che stiamo specificando.
A quel punto non servirà altro che specificare questo nickname, quando necessario, in base
all’operazione da fare e alla necessità.
Diamo uno sguardo nel dettaglio, adesso, ad un singolo elemento dell’array delle connessioni.
L’opzione “driver”, come già detto, indica il tipo di database al quale ci vogliamo connettere.
• mysql - MySQL
• sqlite - SQLite
• pgsql - PostgreSQL
• sqlsrv - SQL Server
Abbiamo poi l’indice “host” che identifica l’indirizzo del database server al quale ci stiamo
connettendo.
Puoi inserire sia un indirizzo IP che un host name. Normalmente, in locale, l’indirizzo di default è
“localhost” o “127.0.0.1”.
Nota per SQLite: essendo residenti sul disco, per loro non ci sarà nessuna configurazione dell’host.
Andiamo avnati e passiamo a “database”.
Qui stiamo identificando il nome del database da usare nel contesto di questa connessione.
Nel caso di SQLite dovrà essere specificato il path del file.
Ad esempio:
Abbiamo poi “username” e “password” che hanno bisogno di poche presentazioni, direi.
Per SQLite le cose sono leggermente diverse anche qui: non hanno credenziali di accesso. Quindi
puoi omettere questi indici senza nessun problema.
Arriviamo quindi a “charset”, che indica il character set usato dalla connessione. Di default in questo
caso è UTF8.
Database 195
Come prima, SQLite non supporta questa opzione. Elimina pure questo indice nel caso è con questo
driver che stai lavorando.
Tramite “collation” puoi specificare la collation usata di default dal database.
Preparazione
Dato che molto probabilmente seguirai gli esempi nei prossimi capitoli (perché lo farai, vero?) adesso
vediamo insieme come preparare tutto l’ambiente di lavoro.
Normalmente, la procedura sarà la seguente.
Innanzitutto, scarica il database di cui hai bisogno. Come già detto, nelle prove che faremo useremo
MySQL.
Una volta scaricato e installato tutto sul tuo server dovremo andare a sistemare un po’ di parametri
visti poco fa nel file di configurazione. Diamo, però, un’altra occhiata al file “app/config/databa-
se.php”.
1 /*
2 |--------------------------------------------------------------------------|
3 | Default Database Connection Name |
4 |--------------------------------------------------------------------------|
5 | Here you may specify which of the database connections below you wish to |
6 | use as your default connection for all database work. Of course you may |
7 | use many connections at once using the Database library. |
8 */
9
10 'default' => 'mysql',
Database 196
C’eravamo scordati qualcosa! L’indice “default” di questo file di configurazione indica quale delle
connessioni specificate nell’array utilizzare per l’applicazione.
In questo modo, per fortuna, non avremo bisogno ogni volta di specificare singolarmente la
connessione da usare per ogni operazione: ci pensa Laravel.
Bene: ora, dopo aver preparato tutto, dobbiamo cominciare a salvare i dati.
Qui ti volevo! Nel capitolo seguente vedremo come costruire il database, tramite lo Schema Builder.
Schema
Perfetto: hai deciso di cominciare ad usare un database. Come forse già sai, un database non è
semplicemente un array associativo. È qualcosa di più: ha una struttura ben precisa, tipi di dati
diversi, relazioni, indici e così via.
Questo vuol dire che, prima ancora di memorizzare i dati, dobbiamo definire una struttura in cui
lavorare.
In questo capitolo daremo un’occhiata alla classe Schema, che permette di costruire la struttura di
cui abbiamo bisogno. Non andremo a memorizzare dei dati, ma semplicemente a creare la base per
il futuro lavoro che ci attende.
In un secondo momento, tra l’altro, vedremo anche quale, di preciso, è il posto più adatto dove
inserire tutte le istruzioni di “costruzione”. Tuttavia, fino ad allora lavoreremo con routes e closure
come siamo abituati.
Cominciamo dalle basi: come creare una tabella?
Creazione di Tabelle
Per creare una tabella dobbiamo usare il metodo “create()” della classe “Schema”. Ecco un esempio:
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Schema::create('users', function($table) {});
6 });
Questo metodo accetta due parametri. Il primo, una semplice stringa, è il nome della tabella da creare.
In questo caso, appunto, “users”. Normalmente, l’ideale è chiamare la tabella con lo stesso nome
dell’entità che rappresenta, al plurale (in inglese, chiaramente). È infatti lo snake-casing (caratteri
lowercase e “_”) il metodo di nomenclatura più usato per tabelle e campi.
Il secondo, invece, è una closure con un singolo parametro (in questo caso $table, ma puoi dargli il
nome che vuoi). Useremo questo oggetto per definire i vari campi e gli indici da inserire nella tabella.
Cominciamo subito dal primo campo per eccellenza: una chiave univoca autoincrementante.
Schema 198
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Schema::create('users', function($table) {
6 $table->increments('id');
7 });
8 });
Il metodo “increments()” crea un campo intero autoincrementante, il cui nome viene specificato
come parametro. In questo caso abbiamo scelto “id”.
Aggiungiamo, adesso, qualche altra colonna.
1 <?php
2 // app/routes.php
3
4 Route::get('/', function() {
5 Schema::create('users', function($table) {
6 $table->increments('id');
7 $table->string('username', 32);
8 $table->string('email', 320);
9 $table->string('password', 60);
10 $table->timestamps();
11 });
12 });
A breve spiegherò nel dettaglio tutte le varie istruzioni, però osserva il codice: non è molto difficile
da capire!
Visitiamo l’URL corrispondente (“/”). Quindi, una volta eseguite le varie istruzioni, andiamo a
controllare la tabella creata. In questo caso ho usato MySQL.
Vediamo:
Schema 199
Laravel, con pochissimi metodi, ha creato la tabella, i campi necessari e sistemato le varie lunghezze
ed indici. Niente male, come inizio. Il tutto, ricordo, usando l’oggetto $table.
Ora che abbiamo visto come creare una tabella, scendiamo nel dettaglio e vediamo quali metodi
possiamo usare per la creazioni di nuovi campi.
Tipi di Campo
Qui di seguito puoi trovare tutti i metodi utilizzabili per la creazione di nuovi campi su una qualsiasi
tabella creata con lo Schema Builder. Ho evitato di ripetere il codice della route per risparmiare
tempo: adesso sai comunque dove mettere il codice.
increments
Il metodo “increments()” aggiunge un campo intero autoincrementante alla tabella $table.
1 <?php
2 Schema::create('example', function($table) {
3 $table->increments('id');
4 });
Il primo ed unico parametro accettato è una stringa con il nome della colonna da creare. Ecco il
risultato:
Schema 200
1 +-------+------------------+-----+----------------+
2 | Field | Type | Key | Extra |
3 +-------+------------------+-----+----------------+
4 | id | int(10) unsigned | PRI | auto_increment |
5 +-------+------------------+-----+----------------+
bigIncrements
Se “increments()” non è abbastanza per te, “bigIncrements()” è quello di cui hai bisogno. Funziona
esattamente come “increments()”, con la differenza che al posto di un INT verrà creato un BIGINT.
1 <?php
2 Schema::create('example', function($table) {
3 $table->bigIncrements('id');
4 });
1 +-------+---------------------+-----+----------------+
2 | Field | Type | Key | Extra |
3 +-------+---------------------+-----+----------------+
4 | id | bigint(20) unsigned | PRI | auto_increment |
5 +-------+---------------------+-----+----------------+
string
Il metodo “string()” crea dei campi di tipo VARCHAR, ottimi per memorizzare una grande varietà
di valori. Nomi, cognomi, username, indirizzi email e così via. Le basi!
1 <?php
2 Schema::create('example', function($table) {
3 $table->string('nickname', 128);
4 });
Il primo parametro da specificare è il nome del campo, mentre il secondo è la lunghezza in caratteri.
Di default, nel caso non venga specificato, il valore viene impostato in maniera automatica a 255.
Schema 201
1 +----------+--------------+
2 | Field | Type |
3 +----------+--------------+
4 | nickname | varchar(255) |
5 +----------+--------------+
text
Il metodo “text()” entra in gioco quando “string()” non basta. Creerà, infatti, un campo di tipo TEXT,
più grande rispetto al classico VARCHAR.
Ideale, ad esempio, per un post di un blog.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->text('body');
6 });
1 +-------+------+
2 | Field | Type |
3 +-------+------+
4 | body | text |
5 +-------+------+
integer
Il nome parla da solo: il metodo “integer()” crea sulla tabella in oggetto un nuovo campo di tipo INT.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('shoe_size');
6 });
Il primo parametro è il nome scelto per il campo, mentre il secondo, opzionale, è un booleano che
permette di specificare se la colonna dovrà essere autoincrementante oppure no. Il terzo parametro,
sempre opzionale, consente di decidere se il campo dovrà essere privo di segno oppure no.
Chiaramente cambia il range di valori possibili: nel caso di un INT, ad esempio, il valore di un intero
con segno può andare da -2,147,483,648 a 2,147,483,647, mentre un intero senza segno parte da 0 e
arriva a 4,294,967,295.
Schema 202
1 +-----------+---------+
2 | Field | Type |
3 +-----------+---------+
4 | shoe_size | int(11) |
5 +-----------+---------+
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->bigInteger('waist_size');
6 });
Stessi identici parametri di “integer()”. Non c’è bisogno che li ripeta, vero?
1 +------------+------------+
2 | Field | Type |
3 +------------+------------+
4 | waist_size | bigint(20) |
5 +------------+------------+
mediumInteger
Ecco un altro tipo di campo numerico: stessa sintassi, stessi parametri di prima, con la differenza che
i possibili valori vanno da -8388608 a 8388607. In caso di assenza di segno, invece, da 0 a 16777215.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->mediumInteger('size');
6 });
7
8 +-------+--------------+
9 | Field | Type |
10 +-------+--------------+
11 | size | mediumint(9) |
12 +-------+--------------+
tinyInteger
Ancora un altro tipo di intero: in caso di presenza del segno si va da -128 a 127, altrimenti da 0 a 255.
Schema 203
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->tinyInteger('size');
6 });
7
8 +-------+------------+
9 | Field | Type |
10 +-------+------------+
11 | size | tinyint(1) |
12 +-------+------------+
smallInteger
Integer a non finire: stessa storia di prima, a differenza che i possibili valori vanno da -32768 a 32767,
oppure da 0 a 65535.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->smallInteger('size');
6 });
7
8 +-------+-------------+
9 | Field | Type |
10 +-------+-------------+
11 | size | smallint(6) |
12 +-------+-------------+
float
Finalmente qualcosa di diverso. Il metodo “float()” viene usato per memorizzare numeri a virgola
mobile. Ecco come usarlo:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->float('size');
6 });
Schema 204
Il primo parametro è, come al solito, il nome del campo. Il secondo ed il terzo, opzionali, specificano
la lunghezza del campo ed il numero di cifre decimali da usare nella rappresentazione del dato (di
default 8 e 2).
1 +-------+------------+
2 | Field | Type |
3 +-------+------------+
4 | size | float(8,2) |
5 +-------+------------+
decimal
Il metodo “decimal()” è usato per memorizzare numeri… decimali, esatto. Difficile, eh?
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->decimal('size');
6 });
Come sempre, il primo parametro è il nome del campo. Secondo e terzo, invece, lunghezza e numero
di cifre decimali, un po’ come già visto con float.
Di default i due parametri opzionali assumono i valori 8 e 2.
1 +-------+--------------+
2 | Field | Type |
3 +-------+--------------+
4 | size | decimal(8,2) |
5 +-------+--------------+
boolean
Non sempre c’è bisogno di memorizzare chissà cosa: a volte basta un si o un no, un vero o falso, un
1 o uno 0. Il boolean è il tipo di dato perfetto in questi casi.
Schema 205
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->boolean('hot');
6 });
1 +-------+------------+
2 | Field | Type |
3 +-------+------------+
4 | hot | tinyint(1) |
5 +-------+------------+
Nota: se sei attento avrai letto, qui sopra, “tinyint”. Non è un errore: per rappresentare un booleano
è questo il tipo di dato usato normalmente.
enum
Una lista di elementi ben definita, normalmente, viene inserita in un campo di tipo “enum”. Il metodo
da usare, appunto, è “enum()”:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $allow = array('Walt', 'Jesse', 'Saul');
6 $table->enum('who', $allow);
7 });
Il primo parametro è il nome del campo, mentre il secondo è un array con i valori possibili assegnabili
al campo in questione.
1 +-------+-----------------------------+------+
2 | Field | Type | Null |
3 +-------+-----------------------------+------+
4 | who | enum('Walt','Jesse','Saul') | NO |
5 +-------+-----------------------------+------+
date
Il metodo “date()” crea un nuovo campo utilizzabile per memorizzare delle date.
Schema 206
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->date('when');
6 });
1 +-------+------+
2 | Field | Type |
3 +-------+------+
4 | when | date |
5 +-------+------+
dateTime
Il metodo “dateTime()”, sempre legato al tempo, permette di memorizzare non solo una data, ma
anche un’orario. La sintassi, comunque, è esattamente quella di “date()”.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->dateTime('when');
6 });
7
8 +-------+----------+
9 | Field | Type |
10 +-------+----------+
11 | when | datetime |
12 +-------+----------+
time
Nel caso non dovessi aver bisogno di una data, ma solo di un orario, allora conviene usare “time()”.
Schema 207
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->time('when');
6 });
1 +-------+------+
2 | Field | Type |
3 +-------+------+
4 | when | time |
5 +-------+------+
So bene che questo capitolo può sembrare monotono, ma vorrei tu avessi una reference completa di
tutte le opzioni che Laravel ti mette a disposizione.
timestamp
Il metodo “timestamp()” serve a creare un campo dove una data e un orario verranno memorizzati
in formato TIMESTAMP.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->timestamp('when');
6 });
Anche qui il primo ed unico parametro da specificare è il nome del campo da creare.
1 +-------+-----------+---------------------+
2 | Field | Type | Default |
3 +-------+-----------+---------------------+
4 | when | timestamp | 0000-00-00 00:00:00 |
5 +-------+-----------+---------------------+
binary
Il metodo “binary()” crea un campo che può essere usato per memorizzare dati in formato binario.
Particolarmente utile nel caso si voglia memorizzare un file, ad esempio un immagine.
Schema 208
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->binary('image');
6 });
1 +-------+------+
2 | Field | Type |
3 +-------+------+
4 | image | blob |
5 +-------+------+
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->timestamps();
6 });
7
8 +------------+-----------+---------------------+
9 | Field | Type | Default |
10 +------------+-----------+---------------------+
11 | created_at | timestamp | 0000-00-00 00:00:00 |
12 | updated_at | timestamp | 0000-00-00 00:00:00 |
13 +------------+-----------+---------------------+
Passiamo, adesso, al metodo “softDeletes()”. Può capitare, a volte, di voler segnare un certo
record come cancellato, anche se non si vuole perdere il dato. Magari per poterlo recuperare più
agevolmente in futuro.
Con “softDeletes()” viene aggiunto un campo “deleted_at”, che memorizza la data di “cancellazione”
del record. Eloquent, l’ORM di Laravel, terrà conto di questo dato automaticamente durante le
elaborazioni da effettuare.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->softDeletes();
6 });
7
8 +------------+-----------+------+
9 | Field | Type | Null |
10 +------------+-----------+------+
11 | deleted_at | timestamp | YES |
12 +------------+-----------+------+
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('username')->unique();
6 });
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('username')->unique();
6 $table->primary('username');
7 });
1 +----------+--------------+------+-----+---------+-------+
2 | Field | Type | Null | Key | Default | Extra |
3 +----------+--------------+------+-----+---------+-------+
4 | username | varchar(255) | NO | PRI | NULL | |
5 +----------+--------------+------+-----+---------+-------+
Se invece vuoi concatenare tutto insieme per levarti il pensiero, puoi farlo senza problemi! Sia per
“unique()” che per “primary()”.
Ecco un altro esempio valido per arrivare allo stesso risultato:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('username')->unique()->primary();
6 });
Oppure, così:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('username');
6 $table->unique('username');
7 $table->primary('username');
8 });
A volte potresti aver bisogno di una chiave composta da più campi: in quel caso puoi specificare un
array con i nomi dei campi da dare, poi, in pasto a “primary()”.
Esattamente così:
Schema 211
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('id');
6 $table->string('username');
7 $table->string('email');
8 $keys = array('id', 'username', 'email');
9 $table->primary($keys);
10 });
1 +----------+--------------+------+-----+---------+-------+
2 | Field | Type | Null | Key | Default | Extra |
3 +----------+--------------+------+-----+---------+-------+
4 | id | int(11) | NO | PRI | NULL | |
5 | username | varchar(255) | NO | PRI | NULL | |
6 | email | varchar(255) | NO | PRI | NULL | |
7 +----------+--------------+------+-----+---------+-------+
Fatto!
Adesso parliamo di indici: creare degli indici significa velocizzare le query di ricerca. Per segnare
un determinato campo come indice il metodo giusto da usare è “index()”.
Eccolo in azione:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('age')->index();
6 });
Come detto già prima, può essere usato anche come isolato:
Schema 212
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('age');
6 $table->index('age');
7 });
1 +-------+---------+------+-----+---------+-------+
2 | Field | Type | Null | Key | Default | Extra |
3 +-------+---------+------+-----+---------+-------+
4 | age | int(11) | NO | MUL | NULL | |
5 +-------+---------+------+-----+---------+-------+
Esattamente come già successo con “primary()”, inoltre, è possibile specificare un array di campi da
rendere indici.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('age');
6 $table->integer('weight');
7 $table->index(array('age', 'weight'));
8 });
Ed ecco il risultato:
1 +--------+---------+------+-----+---------+-------+
2 | Field | Type | Null | Key | Default | Extra |
3 +--------+---------+------+-----+---------+-------+
4 | age | int(11) | NO | MUL | NULL | |
5 | weight | int(11) | NO | | NULL | |
6 +--------+---------+------+-----+---------+-------+
Altre volte potresti aver bisogno di permettere ad un campo di assumere, in una qualsiasi situazione,
il valore NULL. Il metodo giusto, stavolta, è “nullable()”:
Schema 213
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->nullable();
6 });
Usa il “describe” e…
1 +-------+--------------+------+
2 | Field | Type | Null |
3 +-------+--------------+------+
4 | name | varchar(255) | YES |
5 +-------+--------------+------+
Da questo momento, come puoi vedere, il campo può contenere il valore NULL.
Nel momento in cui, invece, il campo NON deve poter assumere il valore NULL, basta usare lo stesso
metodo, ma con “un false in più”. Esattamente:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->nullable(false);
6 });
1 +-------+--------------+------+
2 | Field | Type | Null |
3 +-------+--------------+------+
4 | name | varchar(255) | NO |
5 +-------+--------------+------+
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->default('John Doe');
6 });
Il primo ed unico parametro di “default()” è, ovviamente, il valore da usare come predefinito per il
campo in oggetto.
1 +-------+--------------+------+-----+----------+
2 | Field | Type | Null | Key | Default |
3 +-------+--------------+------+-----+----------+
4 | name | varchar(255) | NO | | John Doe |
5 +-------+--------------+------+-----+----------+
Adesso passiamo all’ultimo modificatore. In realtà non è molto utile, ma per molti può essere una
scorciatoia niente male.
Ricordi il metodo “integer()”, per creare un campo numerico intero? Bene: immagino che ricordi
anche la possibilità di specificare la presenza o l’assenza di un segno.
Se non ti dovesse piacere la booleana da passare come ulteriore parametro, puoi usare “unsigned()”:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->integer('age')->unsigned();
6 });
Richiamiamo “describe”…
1 +-------+------------------+
2 | Field | Type |
3 +-------+------------------+
4 | age | int(10) unsigned |
5 +-------+------------------+
Schema 215
Ma, Dayle…
Ed io che volevo riposarmi un po’. Ok, va bene, vediamo tutto quello che serve sapere per modificare
le tabelle con la nostra meravigliosa classe Schema.
Innanzitutto, l’operazione di base per eccellenza: come rinominare una tabella?
Con “Schema::rename()”:
1 <?php
2
3 // Create the users table.
4 Schema::create('users', function($table)
5 {
6 $table->increments('id');
7 });
8
9 // Rename the users table to idiots.
10 Schema::rename('users', 'idiots');
Il primo parametro è il nome della tabella a cui vogliamo cambiare nome: il secondo, invece, è il
nuovo nome da assegnare.
Lineare!
Per andare più in fondo alla questione e cominciare a modificare i campi dovrai fare uso del metodo
“Schema::table()”.
Vediamolo insieme:
Schema 216
1 <?php
2
3 Schema::table('example', function($table)
4 {
5 // Modify the $table...
6 });
Il metodo “table()” è praticamente identico a “create()”, usato poco fa per creare una tabella. La sola
differenza è che “table()” agisce su una tabella già esistente, il cui nome viene specificato come primo
parametro.
Il secondo parametro, invece, è una closure in cui inserire tutte le operazioni di modifica della tabella
stessa.
Vediamo subito un primo esempio:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->increments('id');
6 });
7
8 Schema::table('example', function($table)
9 {
10 $table->string('name');
11 });
Grazie al codice qui sopra abbiamo creato una tabella “example”, inserendoci un campo primario
autoincrementante “id”.
In un secondo momento, con il metodo “table”, abbiamo ripreso la tabella e abbiamo usato il metodo
“string()” per aggiungerci un altro campo. In questo caso, un VARCHAR di nome “name”.
Ecco il risultato di “describe example” dopo le operazioni:
1 +-------+------------------+-----+----------------+
2 | Field | Type | Key | Extra |
3 +-------+------------------+-----+----------------+
4 | id | int(10) unsigned | PRI | auto_increment |
5 | name | varchar(255) | | |
6 +-------+------------------+-----+----------------+
Schema 217
All’interno di “table()” puoi usare tutti i metodi già visti per creare nuovi campi: eviterò quindi di
ripeterli nuovamente.
Potresti aver bisogno, però, di cancellare un campo che non ti serve più: usa “dropColumn()”
nell’eventualità:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->increments('id');
6 $table->string('name');
7 });
8
9 Schema::table('example', function($table)
10 {
11 $table->dropColumn('name');
12 });
Basta specificare come primo parametro il nome del campo da cancellare e niente di più.
Ecco il risultato del “describe” dopo l’operazione:
1 +-------+------------------+------+-----+----------------+
2 | Field | Type | Null | Key | Extra |
3 +-------+------------------+------+-----+----------------+
4 | id | int(10) unsigned | NO | PRI | auto_increment |
5 +-------+------------------+------+-----+----------------+
1 <?php
2
3 Schema::table('example', function($table)
4 {
5 $table->dropColumn(array('name', 'age'));
6 });
… o ancora, se non dovesse piacerti l’array, un insieme di stringhe una dopo l’altra.
Schema 218
1 <?php
2
3 Schema::table('example', function($table)
4 {
5 $table->dropColumn('name', 'age');
6 });
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name');
6 });
7
8 Schema::table('example', function($table)
9 {
10 $table->renameColumn('name', 'nickname');
11 });
Il primo parametro specifica il nome della colonna da rinominare, mentre il secondo parametro è il
nuovo nome scelto.
Ecco il risultato del descibe per l’esempio appena visto.
1 +----------+--------------+
2 | Field | Type |
3 +----------+--------------+
4 | nickname | varchar(255) |
5 +----------+--------------+
Ora, ricordi le chiavi primarie specificate qualche riga fa? Magari non c’è più bisogno di loro, non
credi?
Togliamole di mezzo:
Schema 219
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->primary();
6 });
7
8 Schema::table('example', function($table)
9 {
10 $table->dropPrimary('name');
11 });
Grazie al metodo “dropPrimary()”, al quale passiamo il nome del campo per il quale effettuare
l’operazione, è tutto molto facile.
Usando “describe” il risultato sarà…
1 +-------+--------------+------+-----+---------+-------+
2 | Field | Type | Null | Key | Default | Extra |
3 +-------+--------------+------+-----+---------+-------+
4 | name | varchar(255) | NO | | NULL | |
5 +-------+--------------+------+-----+---------+-------+
Come prima, anche in questo caso puoi scegliere se usare un array, in caso di più campi:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name');
6 $table->string('email');
7 $table->primary(array('name', 'email'));
8 });
Oppure…
1 Schema::table('example', function($table)
2 {
3 $table->dropPrimary(array('name', 'email'));
4 });
Ancora una volta possiamo anche scegliere di usare un array o una lista, nello stesso formato di
“dropPrimary()”.
Schema 220
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->unique();
6 $table->string('email')->unique();
7 });
8
9 Schema::table('example', function($table)
10 {
11 $columns = array('example_name_unique', 'example_email_unique');
12 $table->dropUnique($columns);
13 });
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name')->index();
6 });
7
8 Schema::table('example', function($table)
9 {
10 $table->dropIndex('example_name_index');
11 });
Bisogna specificare, infatti, il nome della tabella, il nome del campo e infine “index”, separati da
underscore (“_”).
Chiederò a Taylor di implementare le stesse procedure e alternative viste per “dropPrimary()” e
“dropUnique()”.
Eliminare le Tabelle
Tagliare le gambe ad una tabella e farla finita è semplice, con “Schema::drop()”:
Schema 221
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name');
6 });
7
8 Schema::drop('example');
Basta specificare come parametro il nome della tabella stessa. Verifichiamo l’esito dell’operazione
con “describe”:
Chiaramente, nel momento in cui la tabella non esiste già, e l’operazione di cancellazione diventa
impossibile, viene notificato un errore.
Nel caso volessi evitare il problema, comunque, c’è sempre il metodo “dropIfExists()”, che elimina
la tabella solo se esiste. Altrimenti tutto continua liscio e senza problemi, come prima.
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->string('name');
6 });
7
8 Schema::dropIfExists('example');
Tutto il Resto
Non sapevo dove mettere questi altri metodi, che non si ricollegano a nessuna delle altre sezioni,
così ho creato questa ultima ulteriore sezione.
Cominciamo dal primo, “Schema::connection()”, che permette di effettuare tutte le operazioni viste
finora ma in un altra connessione tra quelle che possiamo specificare nel file di configurazione, visto
nel capitolo precedente.
Ecco un esempio:
Schema 222
1 <?php
2
3 Schema::connection('mysql')->create('example', function($table)
4 {
5 $table->increments('id');
6 });
7
8 Schema::connection('mysql')->table('example', function($table)
9 {
10 $table->string('name');
11 });
Il metodo “connection()” può essere piazzato prima di ogni altro metodo della classe Schema, tramite
concatenamento. Il primo parametro del metodo, ovviamente, è il nome della connessione da usare
per le operazioni da effettuare.
Un metodo molto utile in caso di applicazioni con più database.
Adesso invece un metodo da usare per controllare l’esistenza di tabelle e campi. Cominciamo da
“hasTable()”:
1 <?php
2
3 if (Schema::hasTable('author')) {
4 Schema::create('books', function($table)
5 {
6 $table->increments('id');
7 });
8 }
Specificando come parametro il nome della tabella, il metodo ritorna un booleano che spiega se la
tabella cercata esiste o meno. Ottimo da usare in procedure di sicurezza, nel caso ci siano dei dubbi
sulla struttura del database.
Allo stesso modo c’è un metodo per controllare l’esistenza di un campo specifico: “hasColumn()”.
Schema 223
1 <?php
2
3 if (Schema::hasColumn('example', 'id')) {
4 Schema::table('example', function($table)
5 {
6 $table->string('name');
7 });
8 }
Il primo parametro è il nome della tabella, mentre il secondo è il campo da cercare. Nel momento in
cui il campo esiste, il metodo ritorna “true”.
Altre volte potresti invece aver bisogno di modificare al volo il motore di storage usato da una tabella:
basta usare il campo “engine” dell’oggetto table.
Come in questo esempio:
1 <?php
2
3 Schema::create('example', function($table)
4 {
5 $table->engine = 'InnoDB';
6 $table->increments('id');
7 });
Ecco alcuni dei motori disponibili, da usare come valori per l’attributo “engine”:
• MyISAM
• InnoDB
• IBMDM2I
• MERGE
• MEMORY
• EXAMPLE
• FEDERATED
• ARCHIVE
• CSV
• BLACKHOLE
Per maggiori informazioni sui vari motori, puoi trovare altre informazioni sulla documentazione
MySQL¹⁹.
Su database MySQL, inoltre, puoi riordinare i campi di una tabella usando il metodo “after()”. Ecco
un esempio:
¹⁹http://dev.mysql.com/doc/refman/5.1/en/storage-engines.html
Schema 224
Basta concatenare il metodo “after()” ad una qualsiasi istruzioni di creazione o modifica, in modo
tale da riposizionare il campo dove desiderato.
Con questo ultimo metodo abbiamo visto come creare il nostro database e la nostra struttura.
Facciamo un altro passo avanti, verso le Migrations.
Migrations
Devi sapere che a casa mia sono riuscito a sviluppare un sistema comodissimo: un’armata di panda
fa tutte le cose più noiose senza la minima fatica (per me).
Ecco un esempio di quella che è la loro giornata:
• 9:00 AM - Lavare e vestire Dayle. - 10:00 AM - Cucinare tanta carne dall’ottimo sapore e
dall’esotica provenienza. - 12:00 PM - (Pausa Pranzo) Riposare sull’albero. - 02:00 PM - Pulire
la collezione di hardware Apple. - 04:00 PM - Preparare il trono dal quale Dayle scriverà il
prossimo capitolo di Code Bright. - 09:00 PM - Prelevare Dayle dal trono e portarlo a letto.
Una bella giornata impegnata: non saprei come fare senza di loro! La lista, inoltre, prevede un ordine
ben definito: immagina che casino se gli elementi cominciassero a scambiarsi di posto. Ora, si sa che
i panda sono decisamente intelligenti, quindi per loro non c’è problema.
Anzi, per essere ancora più sicuri i panda hanno scritto una seconda lista: per ogni cosa da fare, una
volta fatta, ci scrivono vicino l’orario preciso, in modo tale da sapere sempre cosa hanno fatto prima
e cosa dopo.
Per fortuna, in Laravel 4 c’è qualcosa di simile per i Database, che ti aiuterà tantissimo nel sistemare
la tua applicazione in fase di preparazione.
Parliamo delle Migrations.
Concetto di Base
Quando costruisci un database puoi creare la sua struttura a mano: scegli i tipi, valori di default, con-
straint vari ed eventuali, e così via. Sappiamo tutti la storia. Cosa succede, però, se improvvisamente
cancelli il database?
Oppure, magari, stai lavorando in un team e non vuoi avere ventimila file di dump diversi in giro.
Proprio qui, in questo frangente, entrano in gioco le Migrations, che sono essenzialmente un insieme
di script da usare per cambiare la struttura (o il contenuto) del tuo database. Hanno anche un
timestamp di riferimento: potrai quindi definire più di un’operazione ed eseguire un insieme di
task preparatori nel giusto ordine.
Laravel, inoltre, tiene traccia di tutte le Migrations già effettuate: in questo modo effettuerà SOLO
quelle che rimangono (immagina una feature del genere in un sistema da aggiornare e non da
installare ex-novo).
Ora che il concetto dovrebbe esserti più chiaro, vediamo come crearne una.
Migrations 226
Il comando usato è “migrate:make”, al quale passiamo anche un nome da dare alla Migration. Il file,
normalmente, viene inserito in “app/database/migrations”. Verrà anche agganciato un timestamp al
file, in modo da tenerne traccia successivamente per l’ordine di esecuzione.
Ecco un esempio di nome del file generato:
app/database/migrations/2013_06_30_124846_create_users.php
Apriamolo per vederne i contenuti:
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 class CreateUsers extends Migration {
6
7 /**
8 * Run the migrations.
9 *
10 * @return void
11 */
12 public function up()
13 {
14 //
15 }
16
17 /**
18 * Reverse the migrations.
19 *
20 * @return void
21 */
22 public function down()
Migrations 227
23 {
24 //
25 }
26
27 }
1 <?php
2
3 /**
4 * Run the migrations.
5 *
6 * @return void
7 */
8 public function up()
9 {
10 Schema::create('users', function($table)
11 {
12 $table->increments('id');
13 $table->string('name', 128);
14 $table->string('email');
15 $table->string('password', 60);
16 $table->timestamps();
17 });
18 }
Conosciamo già queste istruzioni: stiamo creando una tabella di nome “users” e ci stiamo inserendo
alcuni campi. D’altronde cosa ti aspettavi da una Migration che si chiama “create_users”, scusa?
Adesso, tenendo a mente la regola enunciata poco fa, sistemiamo anche “down()”…
Migrations 228
1 <?php
2
3 /**
4 * Reverse the migrations.
5 *
6 * @return void
7 */
8 public function down()
9 {
10 Schema::drop('users');
11 }
In “up()” abbiamo creato la tabella, in “down()” l’abbiamo eliminata. Direi che ci siamo!
Prima di continuare con le successive sezioni voglio farti vedere un paio di trucchetti. Il primo
riguarda proprio la creazione di una tabella.
Supponiamo, infatti, di sapere già il nome della tabella che vogliamo creare. Nell’istruzione data in
pasto ad artisan prima aggiungiamo “–create” e “–table”, come segue:
1 <?php
2
3 use Illuminate\Database\Schema\Blueprint;
4 use Illuminate\Database\Migrations\Migration;
5
6 class CreateUsers extends Migration {
7
8 /**
9 * Run the migrations.
10 *
11 * @return void
12 */
13 public function up()
14 {
15 Schema::create('users', function(Blueprint $table)
16 {
17 $table->increments('id');
18 $table->timestamps();
19 });
Migrations 229
20 }
21
22 /**
23 * Reverse the migrations.
24 *
25 * @return void
26 */
27 public function down()
28 {
29 Schema::drop('users');
30 }
31
32 }
Fantastico! Abbiamo risparmiato un altro po’ di tempo. Viene tutto aggiunto automaticamente: dal
metodo in “up()” a quello in “down()”, arrivando ai timestamps e alla chiave primaria.
L’altro trucco invece è una “scorciatoia” per definire il path di destinazione del file. Lo switch da
usare, questa volta, è “–path”:
La nostra Migration, stavolta, verrà messa in “app/migs” e non in quella predefinita, come da regola.
Ora che abbiamo visto come creare una Migration, analizziamo insieme il metodo per eseguirla.
1 /*
2 |--------------------------------------------------------------------------
3 | Migration Repository Table
4 |--------------------------------------------------------------------------
5 |
6 | This table keeps track of all the migrations that have already run for
7 | your application. Using this information, we can determine which of
8 | the migrations on disk have not actually be run in the databases.
9 |
10 */
11
12 'migrations' => 'migrations',
A quel punto, dopo aver specificato il nome, apri il terminale ed inserisci il comando
Una tabella semplice, con un paio di campi. Non preoccuparti per loro, stanno bene li e non hai
bisogno di niente, se non di sapere che il sistema di Migrations ora è pronto.
A questo punto non bisogna fare altro che usare il comando “migrate” di Artisan per eseguire la
nostra Migration!
L’output del comando, come puoi vedere, non è altro che una lista di tutte le Migrations che sono
state eseguite. Diamo uno sguardo al database per vedere se la tabella è stata creata come desiderato.
Migrations 231
Ho accorciato un po’ la cosa per stare dentro la formattazione del libro, ma come puoi vedere la
tabella è stata creata!
Adesso vediamo un esempio di aggiornamento. Abbiamo bisogno di aggiungere una colonna “title”
alla tabella “users”. So già che sarai tentato dall’aprire il file già creato e aggiungere la definizione
di “title”.
Per piacere, NON farlo.
Se lavorassi in squadra con altre persone e, da qualche parte, dovesse mancare il file che tu stai
modificando, sarebbe un casino e lavorereste con versioni diverse del file.
Facciamo una cosa più ordinata, e creiamo una nuova migration.
Avrai notato che ho usato una nomenclatura fortemente descrittiva: consiglio anche a te di usarla,
rimane molto utile soprattutto quando non lavori da solo.
Andiamo a modificare il codice:
Migrations 232
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 class AddTitleToUsers extends Migration {
6
7 /**
8 * Run the migrations.
9 *
10 * @return void
11 */
12 public function up()
13 {
14 Schema::table('users', function($table)
15 {
16 $table->string('title');
17 });
18 }
19
20 /**
21 * Reverse the migrations.
22 *
23 * @return void
24 */
25 public function down()
26 {
27 //
28 }
29
30 }
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 class AddTitleToUsers extends Migration {
6
7 /**
8 * Run the migrations.
9 *
Migrations 233
10 * @return void
11 */
12 public function up()
13 {
14 Schema::table('users', function($table)
15 {
16 $table->string('title');
17 });
18 }
19
20 /**
21 * Reverse the migrations.
22 *
23 * @return void
24 */
25 public function down()
26 {
27 Schema::table('users', function($table)
28 {
29 $table->dropColumn('title');
30 });
31 }
32
33 }
Ora che Laravel è capace di eseguire la Migration non dobbiamo fare altro che richiamare il comando
“migrate” di Artisan.
Visto? Laravel sa bene quali Migrations hai già eseguito e quali no: l’aggiornamento verrà effettuato
rimanendo in linea con i dati già presenti.
Il risultato? Eccolo…
Migrations 234
La colonna è stata aggiunta correttamente alla tabella che avevamo già creato. Il file di migration
può essere passato al resto del team e chiunque sarà in linea con l’ultimo aggiornamento al database:
basterà eseguire “migrate”.
In ogni caso, se per una qualsiasi ragione ci dovesse essere bisogno di modificare uno dei file già
presenti, basterà usare il comando “migrate:refresh”, che effettuerà il rollback di tutte le migrazioni
per poi eseguirle nuovamente.
Ecco un esempio:
Usando il metodo “down()” di ogni file è stato effettuato il rollback allo stato originale. Quindi, grazie
ai metodi “up()” la tabella è stata nuovamente ricostruita.
Adesso, ricordi il “–path” visto poco fa? Credo proprio di si: serve a specificare un path diverso da
quello di default. Dobbiamo capire, adesso, come fare la stessa cosa per cercare le Migrations in un
path diverso da quello di default.
Il metodo “migrate”, infatti, cerca le varie Migrations nella cartella di default. Anche in questo caso,
però, è proprio “–path” a risolvere il problema.
Facile, no? Grazie a “–path” abbiamo specificato un path diverso da quello di default e Laravel,
automaticamente, va a cercare li tutti i files.
Adesso, prima di concludere il capitolo, concentriamoci sul rollback. Forza!
Migrations 235
Rollback
Dunque: abbiamo visto come è possibile eseguire le nostre migrations. Cosa fare, però, per tornare
indietro allo stato originale?
Semplice: il rollback!
Come eseguirlo? Altrettanto semplice: con “migrate:rollback”. Ovvio, no?
Nota: con “rollback” Laravel torna indietro allo stato precedente, non effettua un reset totale. Per
tornare all’origine del nostro database, infatti, dobbiamo usare il comando “migrate:reset”.
Nota: Chiaramente il comando di “reset” non rimuove i dati sulla tabella delle migrations.
Trucchi Vari
Ah, così vuoi saperne di più, eh? Vediamo insieme qualche altro piccolo extra per soddisfare la tua
voglia di conoscere.
Descrivendo il funzionamento del sistema di database ti avevo già parlato della possibilità di
agganciarsi ad altre connessioni, vero?
Certo che l’ho fatto: con Artisan possiamo effettuare le operazioni viste finora, se vogliamo, su un
altro database grazie all’array delle connessioni!
Qualcosa del genere, grazie allo switch “–database”.
Adesso tutte le varie operazioni verranno effettuate con la connessione dal nickname “mysql”.
Mmh… non ti vedo molto impressionato. Ok, vediamo cosa posso trovare ancora di interessante.
Ok! Senti questa.
Possiamo eseguire le Migrations SENZA alterare il database. Può essere molto utile, se hai bisogno
di testare il risultato delle query effettuate senza mettere a rischio il database.
Lo switch da usare, questa volta, è “–pretend”.
Migrations 236
Eh eh eh.
Anche per le migrations è tutto.
Nei prossimi capitoli le cose si faranno interessanti: analizzeremo Eloquent, l’ORM che Laravel ci
mette a disposizione per effettuare le operazioni di cui abbiamo bisogno sul database della nostra
applicazione.
Eloquent ORM
Abbiamo visto come configurare il nostro database. Abbiamo visto, con Schema, come creare la
struttura per i nostri dati. Adesso, però, è arrivato il momento di mettersi sotto e sporcarsi le mani:
dobbiamo imparare ad elaborare, inserire, modificare e cancellare i dati nel nostro database.
Se hai avuto già a che fare con Laravel 3, ti starai sicuramente chiedendo: “Perché comincia con
l’ORM? Dove sono le query SQL? Il query builder?”.
Ti rispondo subito: facciamo un passo indietro e pensiamo al perché siamo qui, oggi. Sei uno
sviluppatore, anzi, uno sviluppatore PHP! Dato che sei qui, a leggere questo libro, suppongo che
avrai già una discreta conoscenza della programmazione orientata agli oggetti.
Giusto? Bene.
Se descriviamo le entità in gioco nella nostra applicazione come oggetti allora ha più senso
memorizzarli come oggetti, recuperarli come oggetti e così via.
Immaginiamo, adesso, di voler sviluppare un negozio di libri online.
La programmazione orientata agli oggetti ci insegna che dobbiamo innanzitutto identificare le entità
in gioco nel sistema. Ora, un negozio di libri senza libri non ha molte possibilità di successo, giusto?
Bene, per cui diciamo che c’è un’alta probabilità che l’entità principale del sistema che stiamo
sviluppando è il Libro (Book). Un’entità che verrà usata per rappresentare i singoli libri della libreria
in questione. Normalmente ci riferiamo a questi oggetti come “Models”.
1 <?php
2
3 class Book
4 {
5 /**
6 * The name of our book.
7 *
8 * @var string
9 */
10 public $name;
11
12 /**
13 * A description for our book.
14 *
15 * @var string
16 */
Eloquent ORM 238
17 public $description;
18 }
19
20 $book = new Book;
21 $book->name = 'The Colour of Magic';
22 $book->description = 'Rincewind and Twoflower in trouble!';
Perfetto, direi!
Abbiamo creato un oggetto di tipo “Book” e gli abbiamo dato un titolo ed una descrizione. A questo
punto il passo successivo: dobbiamo memorizzarlo sul database. Supponiamo di aver creato la tabella
necessaria, “books”.
Quello che dobbiamo fare, quindi, è creare la query SQL adeguata all’inserimento. Proviamo:
1 <?php
2
3 $query = "
4 INSERT INTO
5 books
6 VALUES (
7 '{$book->name}',
8 '{$book->description}'
9 );
10 ";
1 <?php
2
3 $book = new Book;
4 $book->name = 'The Colour of Magic';
5 $book->description = 'Rincewind and Twoflower in trouble!';
6 $book->save();
Il metodo “save()” farebbe tutto il necessario per gestire le cose lato database: tutte le operazioni di
“basso livello” non spettano più a te.
Bello, vero?
Eloquent ORM 239
Beh sappi che, normalmente, gli Object Relational Mappers (ORM, da adesso) servono proprio a
questo. Creano una connessione (mapping, per essere precisi) tra oggetti del sistema e tabelle, in
modo tale da astrarre tutte le operazioni normalmente svolte “a mano” dallo sviluppatore.
Inserimento, modifica, cancellazione, selezione. Tutto gestito senza dover scrivere neanche una
singola linea di SQL. Il che è stupendo, considerando che l’SQL è una cosa orripilante e noiosa.
Gli oggetti sono più divertenti, te l’assicuro.
Molti ORM, inoltre, offrono anche la possibilità di gestire agevolmente le relazioni tra più oggetti
differenti. Un esempio qualsiasi può essere Libri ed Autori.
Ovviamente, anche Laravel ha un proprio ORM. Il suo nome è “Eloquent”: ti assicuro che il nome
rende decisamente bene l’idea di ciò che fa e come lo fa. Ha una sintassi stupenda, leggibile ed è
facilissimo da usare.
Quando penso ad un layer di storage come questo, la prima cosa che mi viene in mente è la parola
CRUD. No, non è una manifestazione di disprezzo verso l’SQL, ma una sigla. Che, precisamente,
indica:
Vediamo insieme come fare queste quattro cose, in questo preciso ordine.
Creare i models
Prima di creare il nostro primo model per Eloquent dobbiamo trovare un esempio di dato da
rappresentare. Mmh… ho appena costruito un PC da gioco, per cui il nostro primo oggetto sarà
un videogioco!
Partiamo da Schema: creiamo la tabella “games”.
1 <?php
2
3 // app/database/migrations/2013_07_10_213946_create_games.php
4
5 use Illuminate\Database\Migrations\Migration;
6
7 class CreateGames extends Migration {
8
9 /**
10 * Run the migrations.
Eloquent ORM 240
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('games', function($table)
17 {
18 $table->increments('id');
19 $table->string('name', 128);
20 $table->text('description');
21 });
22 }
23
24 /**
25 * Reverse the migrations.
26 *
27 * @return void
28 */
29 public function down()
30 {
31 Schema::drop('games');
32 }
33
34 }
Credo tu sappia già cosa fa questo codice, per cui non mi dilungo su eventuali spiegazioni. Avrai
notato che ho chiamato la tabella “games”: questo perché ho chiamato il model “Game”.
Beh… Laravel è intelligente. Di default, infatti, cerca automaticamente il plurale del nome usato per
il model come nome della tabella usata per memorizzare le istanze! Chiaramente puoi modificare
questo “comportamento” se ne dovessi avere la necessità ma… non è fantastico?
Ad ogni modo, qualsiasi model Eloquent ha alcuni requisiti ben precisi. In primis, deve avere un
campo intero autoincrementante di nome “id”: una chiave primaria univoca da usare per identificare
un singolo record della tabella. Come già visto in precedenza, puoi aggiungere agevolmente un
campo del genere con il metodo “increments()”. Avviamo la migration per aggiornare il database.
Adesso possiamo cominciare. Creiamo un nuovo model Eloquent da usare per rappresentare un
gioco.
Eloquent ORM 241
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7
8 }
Esatto: qui sopra puoi notare, in tutta la sua semplicità, un model più che completo. Cosa? Sei
sorpreso? Si, in effetti è un po’ “vuoto”, ma ti assicuro che non ha bisogno d’altro. Molti ORM
normalmente ti chiedono una mappa XML dello schema, o magari delle note per ogni colonna da
creare. Con Eloquent tutto questo non esiste: puoi tirare un sospiro di sollievo.
Bene. Adesso aggiungiamo un nuovo gioco.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $game = new Game;
8 $game->name = 'Assassins Creed';
9 $game->description = 'Assassins VS templars.';
10 $game->save();
11 });
Ha un che di familiare, vero? In modo semplice e pulito creiamo una nuova istanza del model “Game”
ed impostiamo i suoi attributi. Attributi che, automaticamente, vengono mappati con i rispettivi
campi della tabella corrispondente. Una volta sistemato tutto non dobbiamo fare altro che chiamare
il metodo “save()” e… voilà! Adesso l’oggetto è stato memorizzato correttamente su database.
Visitiamo adesso “/”. Non ci aspettiamo risposte di nessun genere, dato che la query viene eseguita
e non ritorna niente. Tuttavia l’output è leggermente diverso da quello sperato:
1 SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'field li\
2 st' (SQL: insert into `games` (`name`, `description`, `updated_at`, `created_at`\
3 ) values (?, ?, ?, ?)) (Bindings: array ( 0 => 'Assassins Creed', 1 => 'Assassin\
4 s VS templars.', 2 => '2013-07-14 16:30:55', 3 => '2013-07-14 16:30:55', ))
Nel momento in cui Eloquent crea un nuovo model, infatti, cerca automaticamente di popolare i
campi “updated_at” e “created_at” della tabella. Questo perché il sistema si aspetta che tu abbia
Eloquent ORM 242
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7 public $timestamps = false;
8 }
Perfetto: tutto è andato come desiderato e abbiamo il primo gioco in elenco. Il tutto senza scrivere
una sola riga di codice SQL.
Avrai notato, sicuramente, che non abbiamo avuto bisogno di definire l’id del nostro oggetto.
Essendo autoincrementante, infatti, il layer dedicato al database penserà automaticamente ad
assegnarne uno all’oggetto appena creato. Generalmente è sempre meglio di modificare il campo
“id” di un model Eloquent. Evita di metterci mano, dunque, a meno che tu non sappia esattamente
quello che stai facendo.
Poco fa abbiamo disabilitato i timestamps tramite l’apposito attributo. Adesso, invece, vediamo che
succede quando sono abilitati. Per prima cosa dobbiamo modificare lo schema del nostro database.
Useremo una migration apposita, dato che andare a fare la modifica manualmente non è una buona
idea.
Eloquent ORM 243
La migrazione è stata creata correttamente e, come sempre, il nome usato è fortemente descrittivo.
Andiamo ora ad aggiungere i vari timestamps.
1 <?php
2
3 // app/database/migrations/2013_07_14_165416_add_timestamps_to_games.php
4
5 use Illuminate\Database\Migrations\Migration;
6
7 class AddTimestampsToGames extends Migration {
8
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::table('games', function($table)
17 {
18 $table->timestamps();
19 });
20 }
21
22 /**
23 * Reverse the migrations.
24 *
25 * @return void
26 */
27 public function down()
28 {
29 //
30 }
31
32 }
Abbiamo usato il metodo “Schema::table()” per alterare la tabella “games” ed aggiungere i time-
stamps, attraverso “timestamps()”.
Adesso sai cosa fare…
Eloquent ORM 244
1 <?php
2
3 // app/database/migrations/2013_07_14_165416_add_timestamps_to_games.php
4
5 use Illuminate\Database\Migrations\Migration;
6
7 class AddTimestampsToGames extends Migration {
8
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::table('games', function($table)
17 {
18 $table->timestamps();
19 });
20 }
21
22 /**
23 * Reverse the migrations.
24 *
25 * @return void
26 */
27 public function down()
28 {
29 Schema::table('games', function($table)
30 {
31 $table->dropColumn('updated_at', 'created_at');
32 });
33 }
34
35 }
Ho usato il metodo “dropColumn()” in modo tale da disfarmi dei campi “updated_at” e “created_at”
presenti.
Eseguiamo adesso la nostra migration, in modo tale da aggiungere i campi alla tabella.
Eloquent ORM 245
Adesso ci troviamo di fronte ad un’ardua, ardua scelta. Potremmo impostare su true il valore di
$timestamps:
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7 public $timestamps = true;
8 }
Oppure… potremmo semplicemente rimuoverlo. Anche perché, come avrai intuito, il valore di
$timestamps di default è true.
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7
8 }
Bene: adesso eseguiamo nuovamente “/” e vediamo cosa succede. Fatto? Bene. Esaminiamo la tabella
“games” e…
12 +----+-----------------+------------------------+---------------------+---------\
13 ------------+
14 1 row in set (0.00 sec)
Come puoi vedere, i campi “created_at” e “updated_at” vengono automaticamente popolati con i
valori adatti. Un bel modo di risparmiare tempo, a pensarci bene: potresti voler ricordare ai tuoi
utenti quando fanno un anno di membership sul tuo sito e… così hai un valore da usare come
riferimento.
Prima di andare avanti verso la prossima sezione vorrei ancora condividere un paio di “trucchi” con
te. Ricordi prima, quando abbiamo definito “games” come nome per la tabella da usare? Tutta la
storia della “nomenclatura automatica” e così via? Bene. Potresti voler usare un nome diverso per la
tua tabella… come dirlo al model adesso? Chi glielo dice?
Tranquillo. Usa l’attributo “$table” e passa la paura.
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7 public $table = 'gamezilla_roar';
8 }
Ah, un’altra cosa molto importante: nel momento in cui i tuoi model fanno parte di un namespace
considera che la nomenclatura sarà diversa. Immagina ad esempio un model MyApp\Models\Game. Il
nome di tabella corrispondente che Eloquent cercherà sul database sarà my_app_models_games.
Ora che abbiamo visto come creare nuovi record su database vediamo come leggerli.
Ti interessa? Direi di si.
Passiamo alla prossima sezione.
Leggere i dati
Eloquent offre alcuni metodi da usare per effettuare query e cercare dati tra quelli già memorizzati.
Li vedremo nel dettaglio, tutti, nel prossimo capitolo. Per ora, giusto per una prima “infarinatura”,
useremo il metodo “find()”, che viene richiamato quando c’è bisogno di una particolare riga, scelta
in base all’id corrispondente su database.
Ecco un esempio:
Eloquent ORM 247
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $game = Game::find(1);
8 return $game->name;
9 });
Abbiamo usato il metodo statico “find()” passandogli come parametro il numero “1”, ovvero l’id del
gioco memorizzato poco fa.
Dato che possiamo accedere alle proprietà del singolo oggetto (collegate direttamente con i vari
campi della tabella), ecco il risultato della route:
1 Assassins Creed
Perfetto! Il gioco è stato “recuperato” correttamente dal database. Il metodo “find()” fa parte della
classe Eloquent, parent dei vari model che vai a creare: di conseguenza non avrai mai bisogno di
ridefinirlo di volta in volta.
Più in la, come già detto, guarderemo nel dettaglio il retrieving dei dati in tutte le sue molteplici
forme: per ora, invece, vediamo come modificare un dato esistente.
1 <?php
2
3 // app/routes.php
4 Route::get('/', function()
5 {
6 $game = new Game;
7 $game->name = 'Assassins Creed';
8 $game->description = 'Assassins VS templars.';
9 $game->save();
10 });
Abbiamo salvato il model: questo non vuol dire, però, che non possiamo più modificarlo. Possiamo
modificare nuovamente i suoi valori per poi richiamare di nuovo “save()”, aggiornando così il record
già esistente. La prima volta che usi “save()” su un nuovo oggetto, infatti, l’oggetto viene salvato.
Tutte le chiamate successive, invece, hanno lo scopo di modificare il dato già esistente!
Ecco un esempio:
1 <?php
2
3 // app/routes.php
4 Route::get('/', function()
5 {
6 $game = new Game;
7 $game->name = 'Assassins Creed';
8 $game->description = 'Show them what for, Altair.';
9 $game->save();
10
11 $game->name = 'Assassins Creed 2';
12 $game->description = 'Requiescat in pace, Ezio.';
13 $game->save();
14
15 $game->name = 'Assassins Creed 3';
16 $game->description = 'Break some faces, Connor.';
17 $game->save();
18 });
Forse immagini che questo esempio porti alla creazione di ben tre giochi diversi… e invece no.
Facciamo un esempio completo: innanzitutto facciamo un truncate sulla tabella.
Eloquent ORM 249
Visitiamo ora “/”, dopodichè osserviamo il risultato con una semplice query di select.
Come puoi vedere, Assassin’s Creed 3 è l’ultimo valore specificato durante le varie modifiche.
Essendo l’ultimo, è quello che rimane dopo i vari salvataggi.
Una tecnica utile, senza dubbio. Ma che succede se dobbiamo andare a modificare un dato esistente
salvato qualche tempo fa?
Semplice: recuperiamo il dato, lo modifichiamo e lo salviamo. Così:
1 <?php
2
3 // app/routes.php
4 Route::get('/', function()
5 {
6 $game = Game::find(1);
7 $game->name = 'Assassins Creed 4';
8 $game->description = 'Shiver me timbers, Edward.';
9 $game->save();
10 });
Sapendo che l’ultimo record inserito ha un id pari a 1, lo recuperiamo tramite il “find()”. A quel
punto, dopo la modifica, è possibile salvarlo con un semplice “save()”.
Ecco il risultato:
Eloquent ORM 250
Come puoi vedere abbiamo tranquillamente modificato un model già esistente e ne abbiamo salvato
tutte le modifiche.
Ancora una volta, ovviamente, tutto senza scrivere una sola riga di SQL. Un’altra bella vittoria. Solo
meraviglioso PHP.
Ah, chiaramente il valore di “updated_at” viene modificato di conseguenza automaticamente!
Anche per la modifica è tutto, per ora, e rimane una sola cosa… la cancellazione.
Cancellare i dati
Cancellare dei dati con Eloquent è un qualcosa di tremendamente semplice. Innanzitutto dobbiamo
trovare il dato da cancellare. In questo esempio, come prima, useremo “find()” per scegliere quale
dato, successivamente, eliminare.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $game = Game::find(1);
8 });
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $game = Game::find(1);
8 $game->delete();
9 });
Tuttavia, non è il solo modo di svolgere questa operazione: si può usare anche il metodo statico
“destroy()” del model:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Game::destroy(1);
8 });
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Game::destroy(1, 2, 3);
8 });
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Game::destroy(array(1, 2, 3));
8 });
Preparazione
Più in la impareremo una tecnica da usare per riempire un database con dei dati di esempio. Questa
tecnica è conosciuta come “Seeding”. La vedremo tra un po’, però: per ora limitiamoci ad inserire un
po’ di record di prova come siamo già abituati a fare.
Creiamo subito una nuova migration, in modo tale da creare la tabella di cui abbiamo bisogno.
Stavolta userò come esempio di entità degli album di musica.
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 // app/database/migrations/2013_07_21_103250_create_albums.php
6
7 class CreateAlbums extends Migration {
8
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('albums', function($table)
17 {
18 $table->increments('id');
19 $table->string('title', 256);
20 $table->string('artist', 256);
21 $table->string('genre', 128);
22 $table->integer('year');
23 });
24 }
25
26 /**
27 * Reverse the migrations.
28 *
29 * @return void
30 */
31 public function down()
32 {
33 Schema::drop('albums');
34 }
35
36 }
Perfetto. Abbiamo la struttura. Ora procediamo e creiamo un model adatto alla situazione in cui ci
troviamo, capace di interagire con la tabella.
1 <?php
2
3 // app/models/Album.php
4
5 class Album extends Eloquent
6 {
7 public $timestamps = false;
8 }
Amabile, semplice, pulito. Ho disabilitato i timestamps per semplificare un po’ le cose: per ora non ne
avremo bisogno. Anche se, ti dirò, in genere li aggiungo sempre. Mi piace tenere tutto sotto controllo.
Ci siamo? Perfetto, ora aggiungiamo un po’ di dati. Ricordi come si fa, vero? Lo abbiamo visto poco
fa!
Così:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/seed', function()
6 {
7 $album = new Album;
8 $album->title = 'Some Mad Hope';
9 $album->artist = 'Matt Nathanson';
10 $album->genre = 'Acoustic Rock';
11 $album->year = 2007;
12 $album->save();
13
14 $album = new Album;
15 $album->title = 'Please';
16 $album->artist = 'Matt Nathanson';
17 $album->genre = 'Acoustic Rock';
18 $album->year = 1993;
19 $album->save();
Eloquent e query 256
20
21 $album = new Album;
22 $album->title = 'Leaving Through The Window';
23 $album->artist = 'Something Corporate';
24 $album->genre = 'Piano Rock';
25 $album->year = 2002;
26 $album->save();
27
28 $album = new Album;
29 $album->title = 'North';
30 $album->artist = 'Something Corporate';
31 $album->genre = 'Piano Rock';
32 $album->year = 2002;
33 $album->save();
34
35 $album = new Album;
36 $album->title = '...Anywhere But Here';
37 $album->artist = 'The Ataris';
38 $album->genre = 'Punk Rock';
39 $album->year = 1997;
40 $album->save();
41
42 $album = new Album;
43 $album->title = '...Is A Real Boy';
44 $album->artist = 'Say Anything';
45 $album->genre = 'Indie Rock';
46 $album->year = 2006;
47 $album->save();
48 });
Come puoi notare, per ogni nuovo album ho creato una nuova istanza del model “Album”, in modo
tale da creare nuovi record sul database e non sovrascrivere le informazioni sulla stessa, ogni volta.
Visitiamo “/” e la tabella sarà pronta. Anche stavolta, come nel capitolo precedente, dovresti ottenere
in output una schermata bianca, dato che non abbiamo effettuato il return di nulla, nella route.
Abbiamo tutto quello che ci serve: cominciamo ad esplorare il mondo delle query con Eloquent!
Il “toString()” di Eloquent
In PHP, gli oggetti possono includere, volendo, un metodo __toString(), il cui scopo è molto
semplice: definisce il modo in cui l’oggetto in questione viene rappresentato, quando richiamato
come se fosse una stringa.
Eloquent e query 257
Grazie a questo metodo, i nostri model Eloquent possono essere riportati come semplici stringhe. Di
base, infatti, per la classe Eloquent è stato sovrascritto il metodo in questione, per fare in modo che
ritorni una stringa JSON con tutti i dati dell’oggetto.
Forse ti ho confuso un po’: vediamo subito un esempio per capire meglio di cosa parlo.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $album = Album::find(1);
8 return $album->title;
9 });
In questo esempio ho usato il metodo statico “find()” del model Album, passandogli come parametro
l’integer “1”. Fin qua nulla di nuovo: ho trovato l’istanza di cui ho bisogno e ne ho ritornato il titolo.
Il risultato? Questo:
Some Mad Hope
Togliamo l’ultima istruzione ed effettuiamo il return in questo modo:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::find(1);
8 });
Esatto! JSON! Eloquent, automaticamente, ha effettuato il return dell’oggetto di cui abbiamo bisogno
in uno dei formati più usati per lo scambio di dati. Niente spazi bianchi e niente indentazione, in
modo tale da risparmiare sulla banda durante il trasferimento del dato.
Ora ci penso io a renderlo più leggibile.
Ecco qui:
Eloquent e query 258
1 {
2 id: 1,
3 title: "Some Mad Hope",
4 artist: "Matt Nathanson",
5 genre: "Acoustic Rock",
6 year: 2007
7 }
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $albums = Album::all();
8 foreach ($albums as $album) {
9 echo $album->title;
10 }
11 });
Il metodo all() del model “Album” recupera tutti i record sulla tabella. Con un ciclo, quindi, li
mandiamo in output. Ecco il risultato:
Ora ti insegno un’altra cosa: il metodo “all()” non ritorna più un array. Precisamente, infatti, Laravel
4 restituisce quelle che chiamiamo “Collection”.
Lo so, ma non c’entra niente: l’oggetto di tipo “Collection”, infatti, implementa un’interfaccia che
permette agli oggetti di essere iterabili, appunto. Puoi effettuare un loop al loro interno senza avere
un semplice array PHP.
Eloquent e query 259
Non ti fidi proprio, eh? Ok, allora la facciamo alla vecchia maniera. Un bel var_dump() e passa la
paura!
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $albums = Album::all();
8 var_dump($albums);
9 });
Visita “/” e…
1 object(Illuminate\Database\Eloquent\Collection)[134]
2 protected 'items' =>
3 array (size=6)
4 0 =>
5 object(Album)[127]
6 public 'timestamps' => boolean false
7 protected 'connection' => null
8 protected 'table' => null
9 protected 'primaryKey' => stri
10
11 ... loads more information ...
Diamine, ma è vero!
Te l’avevo detto, eh. Sempre a dubitare. L’oggetto in questione, infatti, è un’istanza della classe
Illuminate\Database\Eloquent\Collection. Possiede un array interno di nome “items”, che
contiene gli elementi risultanti dall’operazione, oltre ad altri campi di vario genere. Più in la li
analizzeremo meglio nel dettaglio.
Perché, come per la singola istanza, anche una Collection ha il suo metodo “__toString()”, utile
alla rappresentazione del dato sottoforma di stringa… e così come per la singola istanza, anche la
Collection ritorna, in tal caso, una stringa JSON.
Facciamo subito una prova:
Eloquent e query 260
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::all();
8 });
Visitiamo “/”:
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 },
16 {
17 id: 3,
18 title: "Leaving Through The Window",
19 artist: "Something Corporate",
20 genre: "Piano Rock",
21 year: 2002
22 },
23 {
24 id: 4,
25 title: "North",
26 artist: "Something Corporate",
27 genre: "Piano Rock",
28 year: 2002
29 },
30 {
31 id: 5,
Eloquent e query 261
• Il Model
• Le Regole
• I Metodi di Fetch
Il Model è l’istanza attraverso la quale effettuiamo la query stessa. Tutti gli esempi in questa sezione
sono basati sul model “Album”.
Le regole sono delle istruzioni che permettono di identificare, in base alle condizioni necessarie, un
determinato subset delle righe della tabella. La regola più famosa è sicuramente il WHERE di SQL.
Infine abbiamo i metodi di fetch: tutti quei metodi che vengono usati per lanciare la query ed
effettuare il return dei risultati trovati.
Nell’esempio di seguito, per cominciare, una sintassi di esempio di una query, nella sua forma più
semplice e basilare.
Eloquent e query 262
1 <?php
2
3 Model::fetch();
Come già detto, tutte le query che creiamo fanno capo ad un determinato model. Le regole,
chiaramente, sono opzionali. Nell’esempio appena visto, infatti, non ce ne sono. Successivamente
abbiamo un metodo di fetch. Nello specifico, il metodo “fetch()” non esiste, è solo un esempio per
spiegare meglio la sintassi. Il primo metodo in una qualsiasi concatenazione del genere è chiamato
staticamente, attraverso l’uso doppio dei due punti (“::”).
In ogni query possiamo inserire una regola, più regole o magari non inserirne nessuna. Tutto a nostra
discrezione.
Qui di seguito una query di esempio con una singola regola.
1 <?php
2
3 Model::constraint()->fetch();
Da notare che, adesso, è la regola ad essere un metodo statico e non più il metodo di fetch.
Possiamo aggiungere quindi tutte le regole di cui abbiamo bisogno, come in questo esempio:
1 <?php
2
3 Model::constraint()
4 ->constraint()
5 ->constraint()
6 ->fetch();
L’importante è ricordarsi queste poche regole: si comincia sempre con un model e il primo metodo
viene chiamato staticamente. Chiaramente serve sempre un metodo di fetch, in quanto altrimenti la
query non viene lanciata.
1 <?php
2
3 Album::all();
Nell’esempio qui presente, “Album” è il model usato, mentre “all()” è uno dei metodi di fetch
disponbili, che infatti esegue la query e ne ritorna i risultati in una collection.
I metodi di fetch possono ritornare una singola istanza oppure una Collection. Tuttavia, come già
visto in precedenza, entrambe queste possibilità possono essere espresse anche in JSON.
Ora che abbiamo più chiara la visione generale, cominciamo dall’analizzare tutti i singoli metodi di
fetch disponibili.
Eloquent e query 263
Metodi di fetch
Cominciamo dal primo metodo che hai incontrato: “find()”.
Find
Il metodo “find()” può essere utilizzato per recuperare una singola istanza di un model, tramite
la specifica dell’id corrispondente al record desiderato. L’id viene passato, infatti, come primo
parametro del metodo.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::find(1);
8 });
In questo caso recuperiamo l’istanza (quindi il record corrispondente) il cui id è “1”. Di conseguenza:
1 {
2 id: 1,
3 title: "Some Mad Hope",
4 artist: "Matt Nathanson",
5 genre: "Acoustic Rock",
6 year: 2007
7 }
Nel momento in cui andiamo a specificare un array di id al posto di un singolo, verrà ritornata in
output una Collection contenente le istanze corrispondenti.
Eloquent e query 264
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::find(array(1, 3));
8 });
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 3,
11 title: "Leaving Through The Window",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 }
16 ]
All
Il metodo “all()” può essere usato per ottenere tutti i record contenuti nella tabella. Ecco un semplice
esempio in azione del metodo “all()”.
Eloquent e query 265
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::all();
8 });
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 },
16 {
17 id: 3,
18 title: "Leaving Through The Window",
19 artist: "Something Corporate",
20 genre: "Piano Rock",
21 year: 2002
22 },
23 {
24 id: 4,
25 title: "North",
26 artist: "Something Corporate",
27 genre: "Piano Rock",
28 year: 2002
29 },
30 {
31 id: 5,
Eloquent e query 266
First
A volte, in alcune circostanze, potremmo aver bisogno solo del primo risultato di un certo insieme.
Vero, non capita sempre, ma il metodo “first()” può essere usato per questa evenienza.
Ecco il codice corrispondente…
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::first();
8 });
1 {
2 id: 1,
3 title: "Some Mad Hope",
4 artist: "Matt Nathanson",
5 genre: "Acoustic Rock",
6 year: 2007
7 }
Eloquent e query 267
Update
Non sempre dobbiamo prelevare dei dati: c’è anche da modificarli. Usare il metodo “update()” serve
proprio a questo scopo.
Basta passare un array associativo con i valori da modificare, dove la chiave è il singolo campo e
l’elemento è il nuovo valore da memorizzare.
Ora, il metodo “update()” è speciale: non può essere usato senza nessuna regola. Di conseguenza
userò un semplice where. Se non dovessi capirci molto non ti preoccupare, tra un po’ avrai le idee
più chiare.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Album::where('artist', '=', 'Matt Nathanson')
8 ->update(array('artist' => 'Dayle Rees'));
9
10 return Album::all();
11 });
Dopo aver eseguito la closure, prova nuovamente ad usare il metodo “all()” per recuperare tutti i
record. Ecco cosa è successo:
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Dayle Rees",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Dayle Rees",
13 genre: "Acoustic Rock",
14 year: 1993
15 },
16 {
Eloquent e query 268
17 id: 3,
18 title: "Leaving Through The Window",
19 artist: "Something Corporate",
20 genre: "Piano Rock",
21 year: 2002
22 },
23 {
24 id: 4,
25 title: "North",
26 artist: "Something Corporate",
27 genre: "Piano Rock",
28 year: 2002
29 },
30 {
31 id: 5,
32 title: "...Anywhere But Here",
33 artist: "The Ataris",
34 genre: "Punk Rock",
35 year: 1997
36 },
37 {
38 id: 6,
39 title: "...Is A Real Boy",
40 artist: "Say Anything",
41 genre: "Indie Rock",
42 year: 2006
43 }
44 ]
Delete
Un po’ come succede per “update()”, anche “delete()” non restituisce istanze di nessun genere. Quello
che fa, infatti, è rimuovere i record dalla tabella in base alle condizioni specificate.
Ad esempio, togliamo dalla tabella tutti gli album di Matt Nathanson:
Eloquent e query 269
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Album::where('artist', '=', 'Matt Nathanson')
8 ->delete();
9
10 return Album::all();
11 });
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 },
16 {
17 id: 5,
18 title: "...Anywhere But Here",
19 artist: "The Ataris",
20 genre: "Punk Rock",
21 year: 1997
22 },
23 {
24 id: 6,
25 title: "...Is A Real Boy",
26 artist: "Say Anything",
27 genre: "Indie Rock",
28 year: 2006
Eloquent e query 270
29 }
30 ]
Ah, un trucco. Nel momento in cui dovessi aver bisogno di eliminare TUTTI i record, senza
particolari distinzioni, puoi usare il metodo “truncate()”.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 Album::truncate();
8 return Album::all();
9 });
1 [ ]
Get
Ok, questo è il metodo di fetch più importante. Serve ad ottenere, in ritorno, i risultati di una query.
Ad esempio, cercando tutti gli album di un solo artista non ha molto senso usare “all()” per il fetch.
Molto più appropriato, invece, usare “get()”.
Confuso? Tranquillo, facciamo un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Something Corporate')
8 ->get();
9 });
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 }
16 ]
Il metodo “get()”, inoltre, ha un parametro opzionale. Nel caso avessi bisogno solo di un certo numero
di campi, e non di tutti, puoi specificarli come array. Ecco un esempio in azione:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Something Corporate')
8 ->get(array('id', 'title'));
9 });
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window"
5 },
6 {
7 id: 4,
8 title: "North"
9 }
10 ]
Come puoi vedere, sono stati riportati solo i campi specificati nell’array.
Eloquent e query 272
Pluck
Il metodo “pluck()” può essere usato per recuperare un valore per un singolo campo. Ecco un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::pluck('artist');
8 });
Il primo ed unico parametro è il nome del campo da cercare e ritornare. Ecco il risultato:
1 Matt Nathanson
Lists
Nel momento in cui “pluck()” preleva un solo valore per un campo, “lists()” si occupa di ritornare
tutti i valori trovati, sempre per un campo. Anche qui, il metodo accetta un singolo parametro, che
è chiaramente il nome del campo desiderato.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::lists('artist');
8 });
Et voilà!
Eloquent e query 273
1 [
2 "Matt Nathanson",
3 "Matt Nathanson",
4 "Something Corporate",
5 "Something Corporate",
6 "The Ataris",
7 "Say Anything"
8 ]
ToSql
Ok, in questo caso non parliamo esattamente di un metodo di fetch, ma in ogni caso stiamo parlando
di qualcosa di davvero molto, molto utile.
Il metodo “toSql()” può essere usato al posto di un qualsiasi metodo di fetch: la differenza rispetto
a questi ultimi, però, è che “toSql()” ritornerà la query che avrebbe lanciato. Vediamo subito un
esempio per avere le idee più chiare.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Something Corporate')
8 ->toSql();
9 });
Chiamiamo “toSql()” e…
Ah si, il punto di domanda. Dunque, il costruttore automatico di query che ha Laravel usa dei
segmenti già pronti di query. In questo caso, il punto di domanda rappresenta il valore che verrà poi
usato come termine di ricerca. Normalmente, per maggiore sicurezza, tutti questi segmenti e termini
vengono sottoposti ad escape in modo tale da evitare le SQL Injection, che non piacciono a nessuno.
Direi che per i metodi di fetch ci siamo: adesso buttiamoci sulle regole.
Eloquent e query 274
Regole
I metodi di fetch visti nella sezione precedente sono molto utili: ti assicuro che sono ancora più utili,
in svariate situazioni, se le unisci a regole ben ponderate.
In questa sezione vedremo un po’ tutte le regole che abbiamo a disposizione e come si usano.
In matematica, con gli insiemi, uno dei concetti più semplici da capire è proprio quello di
“sottoinsieme”. È esattamente di questo che parliamo: di sottoinsiemi.
L’unica differenza, in questo caso, è che oltre alla selezione di subset ci sono anche istruzioni per
l’ordinamento dei valori secondo un certo criterio.
Non perdiamoci in chiacchiere: cominciamo con “where()”.
Where
Il grande classico che non ha bisogno di presentazioni. Il metodo “where()” viene usato per limitare
i risultati in base ad un termine di ricerca ben preciso.
Proviamo subito un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Matt Nathanson')
8 ->get();
9 });
In questo caso, come nell’esempio visto poco fa, abbiamo scelto di selezionare tutti gli album di Matt
Nathanson.
Il metodo “where()” accetta, normalmente, tre parametri. Il primo parametro è il nome del campo
da usare per il confronto. In questo esempio è “artist”. Il secondo parametro, invece, è l’operatore
da usare: in questo caso è stato usato il classico uguale (“=”), ma si può usare anche qualsiasi altro
operatore, come ad esempio “<”, “>”, “<=”, “>=” e così via.
Il terzo parametro, infine, è il termine di ricerca sul quale effettuare l’operazione di comparazione.
In questo caso, appunto, “Matt Nathanson”.
Ed ecco il risultato:
Eloquent e query 275
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 }
16 ]
Perfetto: entrambi gli album, come mi aspettavo, hanno “Matt Nathanson” in corrispondenza di
“artist”. Un modo perfetto per gestire la pagina della discografia del singolo artista.
Cosa? Hai bisogno solo del primo album che riesci a trovare? Allora usa “first()”:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Matt Nathanson')
8 ->first();
9 });
Ecco il risultato:
1 {
2 id: 1,
3 title: "Some Mad Hope",
4 artist: "Matt Nathanson",
5 genre: "Acoustic Rock",
6 year: 2007
7 }
Eloquent e query 276
Ok, adesso cambiamo operatore. Che ne dici di un bel “LIKE”? L’operatore SQL “LIKE” è molto utile
per effettuare la ricerca (e l’operazione di comparazione) senza dover necessariamente specificare il
termine di ricerca esatto: basta anche parziale.
Facciamo subito un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('title', 'LIKE', '...%')
8 ->get();
9 });
In questo caso stiamo cercando tutti gli album il cui titolo comincia per tre punti (“…”). Il segno %,
infatti, posto dopo la stringa, indica che non è importante il seguito della stringa, ma solo il suo
inizio.
Ecco il risultato!
1 [
2 {
3 id: 5,
4 title: "...Anywhere But Here",
5 artist: "The Ataris",
6 genre: "Punk Rock",
7 year: 1997
8 },
9 {
10 id: 6,
11 title: "...Is A Real Boy",
12 artist: "Say Anything",
13 genre: "Indie Rock",
14 year: 2006
15 }
16 ]
Come già detto in precedenza, non siamo limitati ad usare un singolo where() nelle nostre query.
Proviamo quindi a concatenarne un paio e vedere che succede.
Eloquent e query 277
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('title', 'LIKE', '...%')
8 ->where('artist', '=', 'Say Anything')
9 ->get();
10 });
Stavolta ho cercato tutti gli album il cui titolo comincia con i tre punti. Nel frattempo, questi album
devono essere dei “Say Anything”.
Il risultato? Il seguente:
1 [
2 {
3 id: 6,
4 title: "...Is A Real Boy",
5 artist: "Say Anything",
6 genre: "Indie Rock",
7 year: 2006
8 }
9 ]
OrWhere
Nelle varie query che andiamo a costruire per un’applicazione non sempre c’è il bisogno di far
coincidere più regole contemporaneamente: a volte, almeno una condizione diventa un requisito più
che sufficiente. In situazioni come queste, “orWhere()” è quello che serve. Come il nome suggerisce,
infatti, è un alternativa di “where()”, dove l’“AND” viene sostituito da un “OR”.
Ecco un esempio per comprenderne meglio il funzionamento:
Eloquent e query 278
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('title', 'LIKE', '...%')
8 ->orWhere('artist', '=', 'Something Corporate')
9 ->get();
10 });
Gli album da prelevare, in questo caso, saranno quelli il cui titolo comincia per “…”, oppure quelli il
cui artista è “Something Corporate”.
Ecco, infatti, il risultato.
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 },
16 {
17 id: 5,
18 title: "...Anywhere But Here",
19 artist: "The Ataris",
20 genre: "Punk Rock",
21 year: 1997
22 },
23 {
24 id: 6,
25 title: "...Is A Real Boy",
26 artist: "Say Anything",
27 genre: "Indie Rock",
Eloquent e query 279
28 year: 2006
29 }
30 ]
In base alle necessità, ovviamente, puoi sempre decidere di concatenare insieme uno o più “where()”
e “orWhere()”. Comodo!
WhereRaw
Quando abbiamo voglia di scrivere noi le query da utilizzare, per i più disparati motivi, “whereRaw()”
è quello di cui abbiamo bisogno.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereRaw('artist = ? and title LIKE ?', array(
8 'Say Anything', '...%'
9 ))
10 ->get();
11 });
Il metodo “whereRaw()” accetta come primo parametro la stringa SQL da usare. I vari punti inter-
rogativi verranno, in fase di esecuzione, sostituiti dai vari elementi dell’array presenti nel secondo
parametro. In questo modo, tra l’altro, i vari parametri passati verranno sottoposti automaticamente
ad escape, in modo tale da evitare problemi di sicurezza.
Alla fine, la query risultante sarà la seguente…
1 [
2 {
3 id: 6,
4 title: "...Is A Real Boy",
5 artist: "Say Anything",
6 genre: "Indie Rock",
7 year: 2006
8 }
9 ]
Il metodo “whereRaw()” è consigliato in situazioni in cui c’è bisogno di query molto, molto
complesse, che gli strumenti offerti da Laravel non riescono a rendere al meglio. Si parla di pochi
casi, ma è sempre meglio prevenire che curare!
Ah, prima che mi dimentichi: con la stessa identica sintassi, oltre al “whereRaw()” c’è “orWhere-
Raw()”!
WhereBetween
Il metodo “whereBetween()” è comodissimo quando bisogna controllare che un determinato valore
rientri in un altrettanto determinato range.
Riporto subito un esempio: spiega sempre meglio di mille parole!
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereBetween('year', array('2000', '2010'))
8 ->get();
9 });
Il primo parametro è il nome del campo sul quale effettuare il controllo. Il secondo parametro è un
array, contenente i due termini da usare come margine inferiore e superiore del range di cui abbiamo
bisogno.
Nell’esempio di seguito cerchiamo solo gli album pubblicati dal 2000 al 2010.
Infatti…
Eloquent e query 281
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 3,
11 title: "Leaving Through The Window",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 },
16 {
17 id: 4,
18 title: "North",
19 artist: "Something Corporate",
20 genre: "Piano Rock",
21 year: 2002
22 },
23 {
24 id: 6,
25 title: "...Is A Real Boy",
26 artist: "Say Anything",
27 genre: "Indie Rock",
28 year: 2006
29 }
30 ]
Anche qui segnalo la presenza di un metodo analogo “orWhereBetween()”, con la stessa sintassi.
WhereNested
Il metodo “whereNested()” entra in gioco quando è difficile lavorare solo con “where()” e “orWhe-
re()”, ma al tempo stesso non abbiamo voglia di usare “whereRaw()”.
Passando come primo parametro una closure, infatti, possiamo specificare delle query annidate
per tutti i successivi livelli di cui abbiamo bisogno. Da notare il parametro “$query”, da usare
successivamente come oggetto di partenza per i metodi desiderati.
Ecco un esempio in azione:
Eloquent e query 282
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereNested(function($query)
8 {
9 $query->where('year', '>', 2000);
10 $query->where('year', '<', 2005);
11 })
12 ->get();
13 });
Nonostante l’esempio semplice, sicuramente il metodo rende meglio quando c’è bisogno di descri-
vere situazioni più complesse.
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 }
16 ]
In questo caso non esiste un metodo “orWhereNested()”: basta usare “orWhere()” per lo scopo e
passargli una closure come primo parametro.
Eloquent e query 283
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereNested(function($query)
8 {
9 $query->where('year', '>', 2000);
10 $query->where('year', '<', 2005);
11 })
12 ->orWhere(function($query)
13 {
14 $query->where('year', '=', 1997);
15 })
16 ->get();
17 });
Nell’esempio qui riportato abbiamo bisogno di tutti gli album che siano stati pubblicati tra il 2000 e
il 2005, OPPURE nell’anno 1997. Si noti che un gruppo di condizioni, con “orWhere()”, non esclude
l’altro gruppo.
Ecco la query SQL risultante…
1 select * from `albums` where (`year` > ? and `year` < ?) or (`year` = ?)
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
Eloquent e query 284
15 },
16 {
17 id: 5,
18 title: "...Anywhere But Here",
19 artist: "The Ataris",
20 genre: "Punk Rock",
21 year: 1997
22 }
23 ]
WhereIn
Il metodo “whereIn()” entra in gioco quando c’è bisogno di sapere se un determinato valore rientra
in un set ben specifico. Particolarmente utile quando abbiamo già a disposizione un array di valori
per il confronto.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $values = array('Something Corporate', 'The Ataris');
8 return Album::whereIn('artist', $values)->get();
9 });
Il primo parametro del metodo “whereIn()” è ovviamente il campo sul quale effettuare il controllo.
Il secondo valore, invece, è l’array di valori di riferimento.
Ecco un esempio di query risultante:
I risultati, invece:
Eloquent e query 285
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 },
16 {
17 id: 5,
18 title: "...Anywhere But Here",
19 artist: "The Ataris",
20 genre: "Punk Rock",
21 year: 1997
22 }
23 ]
WhereNotIn
Il metodo “whereNotIn()” altri non è che il diretto opposto di “whereIn()”. Stavolta, al posto di passare
come secondo parametro l’array di valori da prendere in considerazione, viene passato un array di
valori da NON prendere in considerazione.
Un esempio al volo:
Eloquent e query 286
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $values = array('Something Corporate', 'The Ataris');
8 return Album::whereNotIn('artist', $values)->get();
9 });
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 },
16 {
17 id: 6,
18 title: "...Is A Real Boy",
19 artist: "Say Anything",
20 genre: "Indie Rock",
21 year: 2006
22 }
23 ]
WhereNull
La regola “whereNull()” viene usata per trovare i record che hanno un valore NULL in corrispon-
denza dei campi specificati.
Ad esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereNull('artist')->get();
8 });
1 [ ]
Oh, si, vero. Non ci sono album il cui artista sia “NULL”
Ovviamente, anche qui un metodo “orWhereNull()” è pronto ad essere usato.
WhereNotNull
Il metodo “whereNotNull()” è il diretto opposto di “whereNull()”, come puoi ben immaginare. Ragion
per cui, a differenza del caso precedente, stavolta dovremmo poter vedere qualche risultato!
Il suo compito, infatti, è controllare che un determinato campo non abbia come valore “NULL”
Eloquent e query 288
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereNotNull('artist')->get();
8 });
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 },
16 ... 4 more ...
17 ]
Esatto: tutti gli album nel database. Questo perché, chiaramente, nessun album ha in corrispondenza
dell’artista il valore “NULL”.
Anche qui è presente un valore “orWhereNotNull()”, da usare come i precedenti.
OrderBy
Un nome che parla chiaro: il metodo “orderBy()” serve ad ordinare i risultati della query secondo il
valore di un campo specificato.
Proviamo subito con un esempio:
Eloquent e query 289
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Matt Nathanson')
8 ->orderBy('year')
9 ->get();
10 });
Il primo parametro passato ad “orderBy()” è il campo da usare: in questo specifico caso stiamo
ordinando gli album in base all’anno di uscita, dal meno recente al più recente.
Questa è la query SQL risultante,
1 [
2 {
3 id: 2,
4 title: "Please",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 1993
8 },
9 {
10 id: 1,
11 title: "Some Mad Hope",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 2007
15 }
16 ]
Adesso proviamo a fare il contrario: usiamo sempre l’anno come criterio di ordinamento ma
vogliamo gli album dal più recente al meno recente.
Facile!
Eloquent e query 290
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Matt Nathanson')
8 ->orderBy('year', 'desc')
9 ->get();
10 });
Il secondo parametro è una stringa dal valore “desc”. Serve a spiegare che c’è bisogno dei record in
ordine decrescente.
Stavolta la query SQL risultante è:
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 }
16 ]
Et voilà! Nulla ti vieta, inoltre, di concatenare più istruzioni “orderBy()” in base alle necessità.
Take
Il metodo “take()” serve ad indicare a Laravel di prelevare solo un certo numero di risultati da un
determinato set ottenuto.
Ad esempio:
Eloquent e query 291
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::take(2)
8 ->get();
9 });
1 [
2 {
3 id: 1,
4 title: "Some Mad Hope",
5 artist: "Matt Nathanson",
6 genre: "Acoustic Rock",
7 year: 2007
8 },
9 {
10 id: 2,
11 title: "Please",
12 artist: "Matt Nathanson",
13 genre: "Acoustic Rock",
14 year: 1993
15 }
16 ]
Come tutti gli altri metodi puoi prenderlo e concatenarlo agli altri già visti.
Skip
Quando usi “take()”, “skip()” serve a specificare l’offset di partenza del risultato.
Eloquent e query 292
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::take(2)
8 ->skip(2)
9 ->get();
10 });
Il metodo “skip()” accetta un singolo parametro. L’offset, appunto. In questo specifico caso, per
esempio, abbiamo scartato i primi due record, passando ai successivi.
La query generata è la seguente:
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 }
16 ]
Il where “magico”
Ok, adesso qualcosa di magico per i nostri model! A questo punto della lettura, suppongo, avrai
acquisito una certa familiarità con “where()”.
Come già detto in precedenza, il compito di “where()” è di restringere il dataset finale limitando i
possibili valori accettati di un determinato campo.
Eloquent e query 293
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('artist', '=', 'Something Corporate')
8 ->get();
9 });
Questo esempio lo abbiamo già visto: stiamo cercando tutti gli album di “Something Corporate”.
Bene. Guarda qui, adesso:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereArtist('Something Corporate')->get();
8 });
1 [
2 {
3 id: 3,
4 title: "Leaving Through The Window",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 },
9 {
10 id: 4,
11 title: "North",
12 artist: "Something Corporate",
13 genre: "Piano Rock",
14 year: 2002
15 }
16 ]
Eloquent e query 294
In effetti è un po’ quello che ci aspettavamo: è come se fosse entrato in gioco il “where()” con il
segno “=” e avesse preso tutti gli album dell’artista “Something Corporate”. D’altronde si sa: è quella
l’operazione generalmente più frequente.
Così Taylor, nel creare Laravel, ha pensato bene ad una cosa del genere: da qui è nato il metodo
magico in questione.
Tutto dipende, comunque, dal nome del campo: in questo caso “artist” è diventato “whereArtist()”.
Il campo “name”, invece, trova corrispondenza in “whereName()” e il campo “shoe_size” diventa
“whereShoeSize()” (esatto, vengono eliminati gli underscore e le iniziali diventano maiuscole).
Vediamo subito un altro esempio per capirci qualcosa in più:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::whereTitle('North')->get();
8 });
In questo caso stiamo cercando gli album il cui titolo sia “North”.
Fatto!
1 [
2 {
3 id: 4,
4 title: "North",
5 artist: "Something Corporate",
6 genre: "Piano Rock",
7 year: 2002
8 }
9 ]
Query scopes
Le Query Scopes sono degli strumenti davvero molto utili, soprattutto quando devi ripetere la stessa
operazione un sacco di volte.
Proviamo a fare un esempio.
Eloquent e query 295
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::where('title', 'LIKE', '...%')->get();
8 });
1 <?php
2
3 // app/models/Album.php
4
5 class Album extends Eloquent
6 {
7 public $timestamps = false;
8 }
1 <?php
2
3 // app/models/Album.php
4
5 class Album extends Eloquent
6 {
7 public $timestamps = false;
8
9 public function scopeTriplePeriod($query)
10 {
11 return $query->where('title', 'LIKE', '...%');
12 }
13 }
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return Album::triplePeriod()->get();
8 });
Al posto di usare il classico “where()”, stavolta, basta un “triplePeriod()” (il nome del metodo senza
lo “scope” davanti e la prima lettera della prima parola in minuscolo).
Il risultato è esattamente quello di cui abbiamo bisogno.
Eloquent e query 297
1 [
2 {
3 id: 5,
4 title: "...Anywhere But Here",
5 artist: "The Ataris",
6 genre: "Punk Rock",
7 year: 1997
8 },
9 {
10 id: 6,
11 title: "...Is A Real Boy",
12 artist: "Say Anything",
13 genre: "Indie Rock",
14 year: 2006
15 }
16 ]
Ce l’abbiamo fatta: le query non sono più un mistero per noi studiosi di Laravel.
Adesso facciamo un altro passo avanti e analizziamo più da vicino le Collections.
Eloquent e le Collections
Io AMO le collezioni. Di qualsiasi cosa. Ne ho un sacco. Quando ero piccolo ne avevo una di
trasformers. Ora che sono cresciuto colleziono giochi e manga. Più sono nerd e più mi piacciono.
Ah, anche Laravel ha le sue collezioni! Di fan, di sviluppatori… una marea! C’è anche una nutrita
collezione di storie sull’origine del suo nome (e molte di queste sono false).
Poi, si, ci sono anche le Collections di Eloquent.
La classe Collection
Ogni collection di Laravel è un’estensione della classe Collection, che presenta svariati metodi di
comodo per lavorare con i risultati delle query. Fondamentalmente infatti ogni collection è un array
contenente i risultati, con l’aggiunta di metodi di utilità piuttosto interessanti.
In Laravel 3 tutto quello che veniva ritornato era un array di istanze dei model. In Laravel 4,
invece, abbiamo voluto cambiare un po’ le cose e migliorarle. Non ti preoccupare però: potrai
tranquillamente iterarci all’interno come fosse un semplice array!
In questo capitolo analizzeremo meglio questi metodi, in modo tale da ottenere il massimo da questa
nuova struttura. Per i vari esempi useremo un risultato ottenuto dalla seguente, semplice, query:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 });
All
Cominciamo dalle cose semplici. Il metodo “all()” è utile per ottenere l’array dei risultati al quale sei
stato già abituato da Laravel 3.
Niente di più, niente di meno. Mi raccomando, non confonderlo con “all()” del model! Qui stiamo
parlando di collections!
Vediamo subito il codice ed i risultati di una simile chiamata tramite var_dump():
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump($collection->all());
9 });
Ecco il risultato.
1 array (size=6)
2 0 =>
3 object(Album)[127]
4 public 'timestamps' => boolean false
5 protected 'connection' => null
6 protected 'table' => null
7 protected 'primaryKey' => string 'id' (length=2)
8 protected 'perPage' => int 15
9 public 'incrementing' => boolean true
10 protected 'attributes' =>
11 array (size=5)
12 'id' => int 1
13 'title' => string 'Some Mad Hope' (length=13)
14 'artist' => string 'Matt Nathanson' (length=14)
15 'genre' => string 'Acoustic Rock' (length=13)
16 'year' => int 2007
17 protected 'original' =>
18 array (size=5)
19 'id' => int 1
20 'title' => string 'Some Mad Hope' (length=13)
21 'artist' => string 'Matt Nathanson' (length=14)
Eloquent e le Collections 300
First
Il metodo “first()”, come puoi facilmente immaginare, serve a recuperare il primo elemento nel set.
Precisamente, il primo elemento contenuto nell’array interno della collection ottenuta.
Proviamo il metodo in azione:
Eloquent e le Collections 301
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump($collection->first());
9 });
Visitiamo “/” e…
1 object(Album)[127]
2 public 'timestamps' => boolean false
3 protected 'connection' => null
4 protected 'table' => null
5 protected 'primaryKey' => string 'id' (length=2)
6 protected 'perPage' => int 15
7 public 'incrementing' => boolean true
8 protected 'attributes' =>
9 array (size=5)
10 'id' => int 1
11 'title' => string 'Some Mad Hope' (length=13)
12 'artist' => string 'Matt Nathanson' (length=14)
13 'genre' => string 'Acoustic Rock' (length=13)
14 'year' => int 2007
15 protected 'original' =>
16 array (size=5)
17 'id' => int 1
18 'title' => string 'Some Mad Hope' (length=13)
19 'artist' => string 'Matt Nathanson' (length=14)
20 'genre' => string 'Acoustic Rock' (length=13)
21 'year' => int 2007
22 protected 'relations' =>
23 array (size=0)
24 empty
25 protected 'hidden' =>
26 array (size=0)
27 empty
28 protected 'visible' =>
29 array (size=0)
30 empty
Eloquent e le Collections 302
Niente di complesso: una semplice istanza singola del model rappresentato. In questo caso è il primo
album inserito nella tabella, ed il risultato è tale perché abbiamo usato l’istruzione “Album::all()”,
che preleva tutti i record della tabella. Nell’eventualità di una query differente il risultato sarebbe
stato altrettanto differente. Chiaro?
Last
Se c’è un metodo “first()”, perché non avere anche un metodo “last()”? Il suo funzionamento è
altrettanto ovvio da capire: al posto del primo elemento dell’array interno viene prelevato e ritornato
l’ultimo elemento. Proviamolo subito:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump($collection->last());
9 });
1 object(Album)[138]
2 public 'timestamps' => boolean false
3 protected 'connection' => null
4 protected 'table' => null
5 protected 'primaryKey' => string 'id' (length=2)
6 protected 'perPage' => int 15
7 public 'incrementing' => boolean true
8 protected 'attributes' =>
9 array (size=5)
10 'id' => int 6
11 'title' => string '...Is A Real Boy' (length=16)
12 'artist' => string 'Say Anything' (length=12)
13 'genre' => string 'Indie Rock' (length=10)
14 'year' => int 2006
15 protected 'original' =>
16 array (size=5)
17 'id' => int 6
18 'title' => string '...Is A Real Boy' (length=16)
19 'artist' => string 'Say Anything' (length=12)
20 'genre' => string 'Indie Rock' (length=10)
21 'year' => int 2006
22 protected 'relations' =>
23 array (size=0)
24 empty
25 protected 'hidden' =>
26 array (size=0)
27 empty
28 protected 'visible' =>
29 array (size=0)
30 empty
31 protected 'fillable' =>
32 array (size=0)
33 empty
34 protected 'guarded' =>
35 array (size=1)
36 0 => string '*' (length=1)
37 protected 'touches' =>
38 array (size=0)
39 empty
40 protected 'with' =>
41 array (size=0)
42 empty
Eloquent e le Collections 304
Shift
Bene, adesso passiamo a “shift()”. Un po’ come “first()”, “shift()” preleva e ritorna il primo elemento
presente nella Collection. Con una differenza sostanziale, però: oltre a ritornarlo, infatti, lo rimuove
anche dall’array interno alla Collection.
Testiamo subito il metodo con un po’ di codice:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump(count($collection));
9 var_dump($collection->shift());
10 var_dump(count($collection));
11 });
Se tutto va come deve andare, dovremmo vedere a schermo il conteggio degli elementi nell’array
prima dello shift, quindi l’elemento estratto con il metodo in oggetto e infine il conteggio degli
elementi dopo l’operazione. Proviamo?
1 int 6
2 object(Album)[127]
3 public 'timestamps' => boolean false
4 protected 'connection' => null
5 protected 'table' => null
6 protected 'primaryKey' => string 'id' (length=2)
7 protected 'perPage' => int 15
8 public 'incrementing' => boolean true
9 protected 'attributes' =>
10 array (size=5)
11 'id' => int 1
12 'title' => string 'Some Mad Hope' (length=13)
13 'artist' => string 'Matt Nathanson' (length=14)
Eloquent e le Collections 305
Perfetto! Inizialmente abbiamo sei elementi, otteniamo l’istanza della classe “Album” e quindi ce ne
ritroviamo solo cinque alla seconda chiamata di “count()”.
Pop
Pop è quel genere di musica che va in giro da decenni, pieno di riferimenti al consumo di alcolici ed
ai sogni più selvaggi di milioni di teenagers.
Ah, si, ovviamente è anche un metodo di una qualsiasi Colleciton. Lavora in modo simile a “shift()”,
con la differenza che ritorna l’elemento alla fine dell’array, per poi eliminarlo. Proviamolo subito.
Eloquent e le Collections 306
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump(count($collection));
9 var_dump($collection->pop());
10 var_dump(count($collection));
11 });
Qui il risultato:
1 int 6
2 object(Album)[138]
3 public 'timestamps' => boolean false
4 protected 'connection' => null
5 protected 'table' => null
6 protected 'primaryKey' => string 'id' (length=2)
7 protected 'perPage' => int 15
8 public 'incrementing' => boolean true
9 protected 'attributes' =>
10 array (size=5)
11 'id' => int 6
12 'title' => string '...Is A Real Boy' (length=16)
13 'artist' => string 'Say Anything' (length=12)
14 'genre' => string 'Indie Rock' (length=10)
15 'year' => int 2006
16 protected 'original' =>
17 array (size=5)
18 'id' => int 6
19 'title' => string '...Is A Real Boy' (length=16)
20 'artist' => string 'Say Anything' (length=12)
21 'genre' => string 'Indie Rock' (length=10)
22 'year' => int 2006
23 protected 'relations' =>
24 array (size=0)
25 empty
26 protected 'hidden' =>
27 array (size=0)
28 empty
Eloquent e le Collections 307
Each
Hai mai usato la liberia “Underscore” per Javascript o PHP? In tal caso dovresti avere una certa
familiarità con questo genere di metodo. Al posto di creare un “foreach()” per iterare all’interno dei
risultati, è possibile passare una closure come primo parametro del metodo in oggetto e lavorarci
allo stesso modo.
Ecco un esempio che spiega meglio di mille parole:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 $collection->each(function($album)
9 {
10 var_dump($album->title);
11 });
12 });
Il primo parametro della closure è la singola istanza (in questo caso del model Album), che può essere
quindi usata internamente alla closure stessa.
Ed ecco quindi il risultato.
Eloquent e le Collections 308
Map
Il metodo “map()” funziona in modo piuttosto simile ad “each()”. Con la differenza, tuttavia, di
ritornare un’altra collection come risultato.
Immaginiamo, ad esempio, di voler inserire un prefisso per ogni titolo. Si, dai, un prefisso come “An
ode to a fair panda: “.
Possiamo farlo con “map()”:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $new = $collection->map(function($album)
10 {
11 return 'An ode to a fair panda: '.$album->title;
12 });
13
14 var_dump($new);
15 });
Innanzitutto, il risultato del metodo “map()” viene ovviamente assegnato ad una nuova variabile
($new, in questo caso). Quindi, ad ogni singola iterazione interna al ciclo il valore del nuovo elemento
viene ritornato, in modo tale da essere aggiunto alla nuova Collection.
Ecco infine il risultato:
Eloquent e le Collections 309
1 object(Illuminate\Database\Eloquent\Collection)[117]
2 protected 'items' =>
3 array (size=6)
4 0 => string 'An ode to a fair panda: Some Mad Hope' (length=37)
5 1 => string 'An ode to a fair panda: Please' (length=30)
6 2 => string 'An ode to a fair panda: Leaving Through The Window' (length=5\
7 0)
8 3 => string 'An ode to a fair panda: North' (length=29)
9 4 => string 'An ode to a fair panda: ...Anywhere But Here' (length=44)
10 5 => string 'An ode to a fair panda: ...Is A Real Boy' (length=40)
Filter
Il metodo “filter()” viene usato per ridurre il numero di elementi presenti nella Collection, usando una
closure ed istruzioni ben definite per “raffinare” il set di partenza. Il controllo sarà di tipo booleano:
questo vuol dire che, iterando ogni singolo elemento potranno esservi svolti dei controlli. Se
questi controlli dovessero avere un esito positivo allora basterà ritornare true. L’elemento corrente,
quindi, verrà inserito nella Collection risultante. Altrimenti, semplicemente, non verrà preso in
considerazione.
Un esempio però dovrebbe far capire meglio tutta la procedura.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $new = $collection->filter(function($album)
10 {
11 if ($album->artist == 'Something Corporate') {
12 return true;
13 }
14 });
15
16 var_dump($new);
17 });
Eloquent e le Collections 310
Attraverso il metodo “filter()”, questa volta, stiamo cercando tutti gli album il cui artista equivale a
“Something Corporate”.
Il risultato finale, quindi, sarà:
1 object(Illuminate\Database\Eloquent\Collection)[117]
2 protected 'items' =>
3 array (size=2)
4 2 =>
5 object(Album)[135]
6 public 'timestamps' => boolean false
7 protected 'connection' => null
8 protected 'table' => null
9 protected 'primaryKey' => string 'id' (length=2)
10 protected 'perPage' => int 15
11 public 'incrementing' => boolean true
12 protected 'attributes' =>
13 array (size=5)
14 'id' => int 3
15 'title' => string 'Leaving Through The Window' (length=26)
16 'artist' => string 'Something Corporate' (length=19)
17 'genre' => string 'Piano Rock' (length=10)
18 'year' => int 2002
19 protected 'original' =>
20 array (size=5)
21 'id' => int 3
22 'title' => string 'Leaving Through The Window' (length=26)
23 'artist' => string 'Something Corporate' (length=19)
24 'genre' => string 'Piano Rock' (length=10)
25 'year' => int 2002
26 3 =>
27 object(Album)[136]
28 public 'timestamps' => boolean false
29 protected 'connection' => null
30 protected 'table' => null
31 protected 'primaryKey' => string 'id' (length=2)
32 protected 'perPage' => int 15
33 public 'incrementing' => boolean true
34 protected 'attributes' =>
35 array (size=5)
36 'id' => int 4
37 'title' => string 'North' (length=5)
38 'artist' => string 'Something Corporate' (length=19)
Eloquent e le Collections 311
Per salvare un po’ di spazio ho accorciato un po’ il testo risultante, ma il metodo ha fatto il suo
dovere egregiamente.
Sort
Il metodo “sort()”, come il nome suggerisce, serve ad ordinare la collection secondo criteri ben
determinati. Il suo funzionamento è simile a “uasort()”, che usa valori interi per effettuare le varie
comparazioni.
Per capire meglio il suo funzionamento, tieni a mente che la closure passata come parametro riceve,
a sua volta, due parametri: A e B. Questi due parametri vengono controllati (con regole definite da
te) e alla fine di questi controlli viene ritornato un valore a scelta tra 1, -1 e 0.
Precisamente,
Se A > B il valore di return è 1. Se A < B il valore di return è -1. Se A = B il valore di return è 0.
Ed ora, un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $collection->sort(function($a, $b)
10 {
11 $a = $a->year;
12 $b = $b->year;
13 if ($a === $b) {
14 return 0;
15 }
Eloquent e le Collections 312
In questo caso stiamo cercando di ordinare i vari risultati in base all’anno di uscita degli album.
“$a” e “$b” sono inizialmente i due album (nell’iterazione, chiaramente, ne vengono presi due alla
volta). Ad “$a” e “$b”, quindi, vengono assegnati rispettivamente l’anno di uscita di “$a” e di “$b”.
Successivamente viene effettuato il controllo (“return($a > $b) ? 1 : -1;”) e il valore di return decreta
lo spostamento dell’elemento avanti o indietro nell’array.
Questo metodo, comunque, è “distruttivo”, nel senso che va ad alterare la Collection iniziale. Iterando
all’interno della Collection risultante, infatti, ecco quale risultato si presenta:
1 int 1993
2 int 1997
3 int 2002
4 int 2002
5 int 2006
6 int 2007
Reverse
Il metodo “reverse()” può essere usato per invertire l’ordine della Collection in oggetto. Serve davvero
fare un esempio?
Mhh.. ma si, dai, alla fine sempre meglio essere il più completi possibile.
Eloquent e le Collections 313
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $collection->each(function($album)
10 {
11 var_dump($album->title);
12 });
13
14 $reverse = $collection->reverse();
15
16 $reverse->each(function($album)
17 {
18 var_dump($album->title);
19 });
20 });
Per prima cosa viene effettuata un’iterazione, in modo tale da controllare l’ordine iniziale degli
elementi. Successivamente assegnamo alla variabile $reverse la Collection stessa, invertita però
nell’ordine dei suoi elementi. Infine, con un altro “each()”, controlliamo che tutto sia andato come
sperato.
Il risultato? Eccolo.
Perfetto!
Eloquent e le Collections 314
Merge
Il metodo “merge()” serve a combinare insieme due collections. Il solo ed unico parametro da passare
è la seconda collection da unire alla prima.
Di seguito un esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $a = Album::where('artist', '=', 'Something Corporate')
8 ->get();
9 $b = Album::where('artist', '=', 'Matt Nathanson')
10 ->get();
11
12 $result = $a->merge($b);
13
14 $result->each(function($album)
15 {
16 echo $album->title.'<br />';
17 });
18 });
Per avere valori diversi abbiamo effettuato, innanzitutto, due query diverse: ad $a abbiamo assegnato
una Collection con i vari album dei “Something Corporate”, mentre a $b abbiamo assegnato una
Collection contenente gli album di “Matt Nathanson”.
A quel punto abbiamo usato il metodo “merge()” di $a passando come parametro “$b”.
Semplice, vero? Ecco il risultato:
Slice
Il metodo “slice()” è l’equivalente del metodo “slice()”, appunto, presente già in PHP. Può essere usato
per produrre un subset partendo da una collection esistente, partendo da un valore di offset e una
lunghezza ben definiti.
Cosa, sei confuso? Ok, bando alle ciance e guarda questo esempio: dovrebbe chiarirti le idee.
Eloquent e le Collections 315
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $sliced = $collection->slice(2, 4);
10
11 $sliced->each(function($album)
12 {
13 echo $album->title.'<br />';
14 });
15 });
Come già detto, il primo parametro è il cosiddetto “offset”, ovvero l’indice dal quale partire nel creare
il subset. Il secondo parametro è invece la lunghezza di questo nuovo subset. Insomma, un po’ come
dire “data la collection che ti sto passando, parti dall’elemento 2, prendi i successivi 3 (il 2 più i
successivi 3, per un totale di 4) e ritornali”.
Ecco il risultato:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8
9 $sliced = $collection->slice(-2, 4);
10
11 $sliced->each(function($album)
12 {
Eloquent e le Collections 316
In un caso del genere passare un offset negativo -2 significa contare all’indietro, dalla fine: un po’
come dire a Laravel di cominciare a contare gli elementi da inserire nel nuovo subset partendo dal
penultimo elemento.
Il risultato, quindi, sarà:
Si, ma conta che siamo partiti in corrispondenza di due elementi dalla fine dell’array, quindi solo
due elementi erano disponibili. Il metodo “slice()”, in fase di recupero degli elementi, non “fa il giro”
per poi ricominciare. Ricordatelo.
IsEmpty
Il metodo “isEmpty()” serve semplicemente a controllare se una Collection è vuota oppure no. Ritorna
un valore booleano, quindi è molto semplice da usare.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 // This query will return items.
8 $a = Album::all();
9
10 // This query won't.
11 $b = Album::where('title', '=', 'foo')->get();
12
13 var_dump($a->isEmpty());
14 var_dump($b->isEmpty());
15 });
1 boolean false
2 boolean true
ToArray
Il metodo di utilità “toArray()” è particolarmente utile per tutti gli amanti degli array. Oltre a
trasformare nel formato in oggetto i vari risultati della collection, infatti, trasforma anche la singola
istanza in un array!
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump( $collection->toArray() );
9 });
1 array (size=6)
2 0 =>
3 array (size=5)
4 'id' => int 1
5 'title' => string 'Some Mad Hope' (length=13)
6 'artist' => string 'Matt Nathanson' (length=14)
7 'genre' => string 'Acoustic Rock' (length=13)
8 'year' => int 2007
9 1 =>
10 array (size=5)
11 'id' => int 2
12 'title' => string 'Please' (length=6)
13 'artist' => string 'Matt Nathanson' (length=14)
14 'genre' => string 'Acoustic Rock' (length=13)
15 'year' => int 1993
16 2 =>
17 array (size=5)
18 'id' => int 3
19 'title' => string 'Leaving Through The Window' (length=26)
20 'artist' => string 'Something Corporate' (length=19)
21 'genre' => string 'Piano Rock' (length=10)
22 'year' => int 2002
23 ... and more ...
Eloquent e le Collections 318
ToJson
Il metodo “toJson()” si occupa di trasformare la Collection in una stringa JSON corrispondente. Nel
capitolo precedente, se ti ricordi, abbiamo scoperto come ritornare i dati di una Collection in questo
formato. Questo è il metodo che viene usato internamente per l’operazione.
Ecco un esempio pratico in azione:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump( $collection->toJson() );
9 });
Il risultato è il seguente:
Count
In un esempio visto prima abbiamo usato il metodo PHP “count()” per effettuare il conteggio
delle istanze all’interno di una collection. Esiste anche il metodo “count()” appartenente però alla
collection che fa la stessa identica cosa.
Eloquent e le Collections 319
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $collection = Album::all();
8 var_dump( $collection->count() );
9 });
1 int 6
Ci sono differenze? In realtà no, lo abbiamo inserito per ragioni di completezza. Tutto qui.
Best practice
Perfetto: siamo quasi arrivati alla fine. Prima di lasciare definitivamente il capitolo delle Collection
vorrei darti qualche consiglio, riguardante quella che normalmente è la best practice.
Alcuni metodi disponibili nelle Collection sono, a volte, duplicati di quelli già esistenti nel query
builder. Si pensi ad esempio al metodo “first()”, usato per recuperare solo il primo risultato trovato.
L’esempio di seguito, infatti, porta sempre allo stesso risultato:
1 Album::all()->first();
2 Album::first();
Ora, la domanda sorge spontanea: “bene, ma quale devo usare dei due?”.
La risposta è semplice: dipende. Essenzialmente dallo scenario di partenza e dalla situazione in cui
ci troviamo.
Facciamo qualche esempio per comprendere meglio le differenze.
In un primo ipotetico caso abbiamo bisogno di mostrare SOLO il titolo del primo album presente
nel database. Nessun problema, basta usare il metodo “first()” nel query builder di Eloquent.
Album::first();
Nel secondo scenario, invece, abbiamo bisogno di elencare tutti gli album e allo stesso tempo
mostrare il titolo del primo album all’interno di un box specifico. Supponiamo di avere un “album
dell’anno”, per intenderci. Potremmo fare qualcosa del genere, suppongo:
Eloquent e le Collections 320
1 $allAlbums = Album::all();
2 $albumOfTheYear = Album::first();
Potremmo, appunto. Le query effettuate in questo caso, infatti, sono due. C’è davvero bisogno di
usarne due?
Secondo me no. Potremmo invece usare il metodo “first()” partendo dalla Collection risultante, che
contiene già il dato di cui abbiamo bisogno.
1 $allAlbums = Album::all();
2 $albumOfTheYear = $allAlbums->first();
Esattamente lo stesso risultato, vero? Con la differenza che abbiamo lanciato una query in meno…
e sul lungo andare il server ci ringrazia.
Qui ho fatto solo un esempio, chiaramente: ma è sempre bene tenere a mente che molto spesso è
possibile ottimizzare il codice in tal senso per avere un minor carico di lavoro e, di conseguenza,
una migliore performance dell’applicazione. Fin quando fai delle prove in locale la differenza non si
vede, ma se la tua applicazione ha ambizioni diverse le cose cambiano in maniera sensibile.
Eloquent e le Relazioni
Era una notte fredda in Eloquent City. Le gocce di pioggia battevano forte sulle finestre, come fossero
lacrime di una persona senza più nessuna speranza.
Zack era nel suo ufficio. Una volta Eloquent City era un posto tranquillo, puro. La corruzione era
solo un qualcosa di inconsistente, sconosciuto. Poi però la pace se ne andò, lasciando il posto alle
guerre tra gang, alle rapine e ad ogni tipo di crimine.
Zack Kitzmiller, una volta, era un fiero poliziotto di Eloquent City. Proprio per questo motivo, come
ultimo uomo onesto di una città corrotta, aveva sentito il dovere di lasciare le forze della polizia.
Voleva sistemare le cose per conto suo.
L’unico modo rimasto. Diventando un investigatore privato.
Stava li, seduto con lo sguardo nel vuoto ed in mano un bicchiere di wiskey e un sigaro economico.
L’ufficio era abbastanza sporco. Un po’ come il suo proprietario, che non faceva la doccia da un po’.
Un rumore distolse Zack dai suoi pensieri.
Toc toc.
Una bellissima bionda era appena entrata nel suo studio. Zack alzò lo sguardo.
“Sei tu Kitzmiller, l’investigatore privato?”, chiese.
“Si, sono Zack Kitzmiller, bionda. Ma puoi chiamarmi come vuoi.”, rispose Zack con un leggero
sorriso malizioso.
“Mr Kitzmiller, il mio nome è Pivote Tableux. Sto cercando l’uomo che ha ucciso mio marito. Anzi,
so già chi è: Enrico Barnez”.
“Beh. Enrico Barnez. Il più famoso lord della droga in città. Se ne sta nascosto e non si fa vedere.
Sarà difficile farlo uscire fuori. Poi, costerà molto. Sicura di avere soldi a sufficienza per una cosa
del genere?”
“Mister Kitzmiller, i soldi non sono un problema. Ho tanti soldi, tutti quelli necessari.”
“È tutto quello che volevo sentire.”
Non c’era altro da dire o da fare, se non prendere il cappotto ed uscire.
Eloquent e le Relazioni 322
Zack era arrivato all’entrata principale del magazzino del porto. C’era un casino non indifferente,
tra navi distrutte, rottami e pezzi di veicoli sul pavimento.
Davanti a lui un uomo, probabilmente un magazziniere.
“Sei Messy Stinkman?”
“Forse si… forse no. In genere dipende da chi chiede.”
“Sto cercando Barnez. Dove posso trovarlo?”
“Barnez… Senti, il boss ha tanti amici. Molti di questi sono anche molto, molto pericolosi. Davvero
credi che possa rimanere al sicuro se ora ti dico dove si trova? Guarda questo magazzino, guarda
quello che c’è dentro. Tutta roba sua. Mi dispiace amico, non posso aiutarti. Vai via, sparisci.”
Zack aveva capito: le parole non sarebbero servite a niente. Decise di divertirsi un po’ con il suo
nuovo amico ed una mazza trovata li vicino.
“Ok ok, fermati ti prego! Te lo dico. Vuoi sapere dove si trova Enrico? Bene, girati.”
Si girò lentamente: dietro di lui c’era Enrico Barnez, l’uomo che cercava.
Adesso erano petto a faccia. Si, petto. Mica è colpa mia, Barnez è bassissimo.
“Cosa vuoi da me, straniero?”
Come risposta, Zack estrasse la pistola e la puntò dritta in faccia ad Enrico. Che fece la stessa cosa.
Attimi di tensione e di paura: erano arrivati ad uno stallo.
Poi un colpo. Un corpo a terra.
Cosa successe? Beh, la vita non è sempre tutta rose e fiori.
Detto questo, passiamo alle relazioni.
Prima che tu me lo chieda: si, vale anche per questo libro. Esisto, che ti piaccia o meno. Ne parleremo
un’altra volta.
Comunque, sappiamo che ogni libro appartiene ad un autore. Ecco, questa è una relazione. Quel
collegamento che l’autore ha con il suo libro. Non si tratta, quindi, di una proprietà del libro stesso
(come, ad esempio, il titolo). Assolutamente no: ogni autore infatti ha le sue proprietà. Un nome, una
data di nascita, una pizza preferita.
Da qui nasce l’esigenza di avere una classe apposita per l’autore: la classe Author, magari?
Hai già visto come creare un model ma… come collegare insieme due classi? Nel mondo dei database
relazionali siamo abituati ad usare le “chiavi esterne”, che normalmente sono di tipo INT.
Vediamo come si fa, invece, con Laravel. Partiamo dalle tabelle, innanzitutto.
books
1 +---------+------------------------+
2 | id (PK) | name |
3 +---------+------------------------+
4 | 1 | Code Sexy |
5 | 2 | Code Dutch |
6 | 3 | Code Bright |
7 +----------------------------------+
1 +---------+------------------------+
2 | id (PK) | name |
3 +---------+------------------------+
4 | 1 | Dayle Rees |
5 | 2 | Matthew Machuga |
6 | 3 | Shawn McCool |
7 +----------------------------------+
1 +---------+------------------------+-----------------+
2 | id (PK) | name | author_id (FK) |
3 +---------+------------------------+-----------------+
4 | 1 | Code Sexy | 2 |
5 | 2 | Code Dutch | 3 |
6 | 3 | Code Bright | 1 |
7 +----------------------------------+-----------------+
Quello che abbiamo appena fatto è stato aggiungere una chiave esterna. Precisamente, il campo
“author_id”. Anche in questo caso abbiamo a che fare con un campo di tipo intero, ma non identifica
nessuna riga di questa tabella. Quello che fa, infatti, è identificare l’id dell’autore. Per trovare l’autore
di un determinato libro, quindi, dovremo cercare la corrispondenza sulla tabella “authors”.
Dayle, un momento. Perché non mettere la chiave esterna, invece, nella tabella degli
autori?
Perchè, per ora, abbiamo dato per scontato che ci possono essere più libri per un singolo autore.
Ok, guarda questa aggiunta.
authors
1 +---------+------------------------+
2 | id (PK) | name |
3 +---------+------------------------+
4 | 1 | Dayle Rees |
5 | 2 | Matthew Machuga |
6 | 3 | Shawn McCool |
7 +----------------------------------+
books
1 +---------+------------------------+-----------------+
2 | id (PK) | name | author_id (FK) |
3 +---------+------------------------+-----------------+
4 | 1 | Code Sexy | 2 |
5 | 2 | Code Dutch | 3 |
6 | 3 | Code Bright | 1 |
7 | 4 | Code Happy | 1 |
8 +----------------------------------+-----------------+
Eloquent e le Relazioni 325
Aggiungendo Code Happy, adesso, ci sono due libri che appartengono a me. Hanno entrambi il
valore “1” in corrispondenza di “author_id”. Sulla tabella “authors”, infatti, “1” corrisponde al mio
nome!
Vediamo adesso cosa succede se, per esempio, volessimo inserire una o più chiavi esterne all’interno
della tabella “authors”, per indicare i singoli libri dell’autore.
authors
1 +---------+------------------------+---------------+-----------+
2 | id (PK) | name | book_one (FK) | book_two |
3 +---------+------------------------+---------------+-----------+
4 | 1 | Dayle Rees | 3 | 4 |
5 | 2 | Matthew Machuga | 1 | null |
6 | 3 | Shawn McCool | 2 | null |
7 +----------------------------------+---------------+-----------+
books
1 +---------+------------------------+-----------------+
2 | id (PK) | name | author_id (FK) |
3 +---------+------------------------+-----------------+
4 | 1 | Code Sexy | 2 |
5 | 2 | Code Dutch | 3 |
6 | 3 | Code Bright | 1 |
7 | 4 | Code Happy | 1 |
8 +----------------------------------+-----------------+
Non è un granchè, vero? Per ogni libro dovrei aggiungere un campo esterno, ma devo anche lasciare
il valore “null” in caso non sia stato pubblicato un secondo libro di un determinato autore. Immagina
cosa potrebbe succedere se almeno un autore arrivasse a pubblicare dieci libri!
Non hai tutti i torti: meglio lasciare solo la chiave esterna sulla tabella “books”.
Ci sono ovviamente tutte le variazioni inverse. Pensaci: se un libro ha un autore, allora un autore
ha tanti libri.
Autore ha tanti libri.
Nota: in Italiano normalmente parliamo, in questi casi, di “uno a molti”.
Adesso invece passiamo a quella più complessa, probabilmente. Immaginiamo di avere un sito dove
ogni utente può specificare i suoi libri preferiti. Se ci pensi, infatti:
Utente ha tanti libri. Libro ha tanti utenti.
Non possiamo usare il caso precedente: altrimenti la selezione di un libro da parte di un utente
escluderebbe lo stesso libro dalla possibile selezione di un altro utente del sistema!
Normalmente, per questo genere di relazioni (in Italiano usiamo, generalmente, il termine “molti a
molti”) si crea una tabella con gli id degli elementi da collegare.
Un po’ come in questo esempio.
users
1 +---------+------------------------+
2 | id (PK) | name |
3 +---------+------------------------+
4 | 1 | Dayle Rees |
5 | 2 | Matthew Machuga |
6 | 3 | Shawn McCool |
7 +----------------------------------+
books
1 +---------+------------------------+
2 | id (PK) | name |
3 +---------+------------------------+
4 | 1 | Code Sexy |
5 | 2 | Code Dutch |
6 | 3 | Code Bright |
7 | 4 | Code Happy |
8 +----------------------------------+
book_user
Eloquent e le Relazioni 327
1 +-------------------------+
2 | id | user_id | book_id |
3 +-----+---------+---------+
4 | 1 | 1 | 2 |
5 | 2 | 1 | 3 |
6 | 3 | 2 | 2 |
7 | 4 | 3 | 2 |
8 +-----+---------+---------+
La terza tabella viene chiamata normalmente in vari modi: tabella di join, tabella dei pivot, lookup
table o tabella intermedia.
Nella documentazione di Laravel, normalmente, viene usato il termine “Pivot Table”.
Ricorda: per ogni relazione “molti a molti” viene sempre creata una terza tabella dei pivot.
Detto questo, basta teoria per adesso. Passiamo alla pratica e scriviamo un po’ di codice, in modo
tale da capire cosa fanno Laravel ed Eloquent per aiutarci in questo complesso mondo di relazioni.
Implementando le relazioni
Cominciamo subito dalla creazione delle tabelle di cui abbiamo bisogno. Generalmente separo le
varie migrations, ma stavolta per comodità metterò tutte le istruzioni in un solo file.
Quello che stiamo per creare è molto semplice, ma stai comunque attento a tutto. Voglio realizzare
un sistema che contempli Artisti, Album e Ascoltatori.
Immaginiamo, quindi, le loro possibili relazioni:
Tutto chiaro?
Abbiamo una semplice relazione “uno a molti” tra artista ed album, mentre una relazione “molti a
molti” è quella che interessa album e ascoltatori. Sulla tabella degli album, quindi, avremo la chiave
esterna relativa relativa al suo artista. La relazione “molti a molti”, invece, necessiterà di una tabella
a parte.
Ora, tutti sappiamo che, a parte specifiche diverse, come nome della tabella viene usato di default il
plurale del nome del model. Come va chiamata, però, la tabella dei pivot? Semplice: con il nome in
singolare delle due entità in gioco, separate da underscore (“_”), in ordine alfabetico. In questo caso,
quindi, “album_listener”.
Eloquent e le Relazioni 328
Le colonne seguono una convenzione nei nomi simile: viene riportato il nome dell’entità in singolare
con “_id” davanti.
Avremo, quindi, “album_id” e “listener_id”.
Costruiamo, adesso, la migration.
Let’s look at the constructed migration, then we will examine anything that’s new in more detail.
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 // app/datebase/migrations/2013_08_26_130751_create_tables.php
6
7 class CreateTables extends Migration {
8
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
14 public function up()
15 {
16 Schema::create('artists', function($table)
17 {
18 $table->increments('id');
19 $table->string('name', 64);
20 $table->timestamps();
21 });
22
23 Schema::create('albums', function($table)
24 {
25 $table->increments('id');
26 $table->string('name', 64);
27 $table->integer('artist_id');
28 $table->timestamps();
29 });
30
31 Schema::create('listeners', function($table)
32 {
33 $table->increments('id');
34 $table->string('name', 64);
35 $table->timestamps();
Eloquent e le Relazioni 329
36 });
37
38 Schema::create('album_listener', function($table)
39 {
40 $table->integer('album_id');
41 $table->integer('listener_id');
42 });
43 }
44
45 /**
46 * Reverse the migrations.
47 *
48 * @return void
49 */
50 public function down()
51 {
52 Schema::drop('artists');
53 Schema::drop('albums');
54 Schema::drop('listeners');
55 Schema::drop('album_listener');
56 }
57
58 }
Nota: al momeno in cui scrivo queste righe sembra che ci siano alcuni problemi con i constraints
delle chiavi esterne. Potrebbe uscire sullo schermo il seguente errore:
1 SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint (SQL: alt\
2 er table `albums` add constraint albums_artist_id_foreign foreign key (`artist_i\
3 d`) references `artists` (`id`)
1 <?php
2
3 class Artist extends Eloquent
4 {
5 // Artist __has_many__ Album
6 public function albums()
7 {
8 return $this->hasMany('Album');
9 }
10 }
Innanzitutto, c’è da dire che il metodo in questione non richiede chissà quale nomenclatura: così
come ho usato “albums()” avrei potuto usare “relatedAlbums()”, o “pippo()”.
1 return $this->hasMany('Album');
Il metodo che stiamo creando ritorna il valore di “$this->hasMany(‘Album’);”. Questo metodo è uno
di quelli riservati alle relazioni, derivati dalla classe base Eloquent. Il primo parametro è il nome
completo del model di riferimento a cui la relazione fa capo. Come sempre, in caso decidessimo di
sistemare “Album” all’interno di un namespace dovremo modificare anche i riferimenti in tal senso.
Se la chiave esterna ha un nome differente da quello convenzionale (“artist_id” in questo caso) puoi
specificare il nome alternativo del campo come secondo parametro.
Per ora non ti preoccupare, lo vedremo a breve. Finiamo prima di creare i model!
Passiamo ora ad “Album”:
Eloquent e le Relazioni 331
1 <?php
2
3 class Album extends Eloquent
4 {
5 // Album __belongs_to__ Artist
6 public function artist()
7 {
8 return $this->belongsTo('Artist');
9 }
10
11 // Album __belongs_to_many__ Listeners
12 public function listeners()
13 {
14 return $this->belongsToMany('Listener');
15 }
16 }
Il model “Album” è quello coinvolto in più relazioni. Partiamo dal primo metodo, che definisce la
relazione “Album”-“Artist”.
1 return $this->belongsTo('Artist');
Dato che la chiave esterna esiste già nella tabella, possiamo usare il metodo $this->belongsTo()
per definire che il model “Album” è legato al model “Artist”. Il primo parametro specificato ancora
una volta è il model correlato, mentre il secondo, facoltativo, serve a definire il nome del campo di
collegamento da usare nel caso sia diverso dalla nomenclatura di default.
Il secondo metodo, invece, costruisce “una parte” della relazione molti a molti di cui abbiamo parlato
prima. Diamo un’occhiata da vicino:
1 return $this->belongsToMany('Listener');
Il metodo “$this->belongsToMany()” spiega ad Eloquent che deve cercare una tabella dei pivot per la
relazione che sta descrivendo. Il primo parametro, ancora una volta, è il model di riferimento nella
relazione.
Stavolta, però, i possibili parametri sono diversi rispetto a quelli a cui siamo stati abituati. Vediamoli,
con un altro esempio:
Il secondo parametro, opzionale, è il nome della tabella dei pivot utilizzata. Gli ultimi due parametri,
invece, specificano gli eventuali altri nomi dei campi da utilizzare per il collegamento.
A questo punto manca solo l’ultimo model: eccolo!
Eloquent e le Relazioni 332
1 <?php
2
3 class Listener extends ELoquent
4 {
5 // Listener __belongs_to_many__ Album
6 public function albums()
7 {
8 return $this->belongsToMany('Album');
9 }
10 }
Con questo ultimo metodo andiamo così a completare la relazione molti a molti cominciata nel
model precedente.
A questo punto, models alla mano, non rimane che cominciare a divertirci un po’ con qualche istanza.
Relazioni e query
Innanzitutto, creiamo un Artist ed un Album, con la relativa associazione. Userò, come al solito, la
route “/” per avere la possibilità di lavorare velocemente.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $artist = new Artist;
8 $artist->name = 'Eve 6';
9 $artist->save();
10
11 $album = new Album;
12 $album->name = 'Horrorscope';
13 $album->artist()->associate($artist);
14 $album->save();
15
16 return View::make('hello');
17 });
1 $album->artist()->associate($artist);
1 $album->artist();
Te lo ricordi? Esatto: è il metodo di relationship che hai specificato nel model “Album”.
Precisamente, questo metodo ritorna un’istanza dell’Eloquent Query Builder, un po’ come abbiamo
visto in altri capitoli. Tuttavia, questa istanza avrà alcune da seguire. Il set attuale di risultati ritornati
sarà quello dei relativi “Artist” (una collection da un’istanza sola, in questo caso).
Un po’ come scrivere:
Niente di impossibile da capire, alla fine. Data la natura del valore ritornato possiamo tranquilla-
mente concatenarci altre istruzioni e regole:
Ricordatelo sempre: c’è sempre bisogno di un metodo di trigger alla fine della join, se hai bisogno
di ritornare dei valori.
1 $album->artist()->get();
1 $album->artist()->first();
Bene.
Adesso torniamo al primo esempio.
Eloquent e le Relazioni 334
1 $album->artist()->associate($artist);
Il metodo “associate()” è un metodo di comodo davvero utile, disponibile nel contesto delle
relazioni tra due oggetti. Si occuperà di effettuare le operazioni di update delle chiavi esterne
automaticamente, in base al tipo di relazione specificato nei model.
Questo vuol dire che, passando “$album” al metodo “$associate” il campo “artist_id” di “Album”
verrà automaticamente popolato usando la chiave primaria del model “Artist”.
Volendo, comunque, potremmo decidere di passare direttamente la chiave esterna usando un
semplice attributo.
Come in questo esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $artist = new Artist;
8 $artist->name = 'Eve 6';
9 $artist->save();
10
11 $album = new Album;
12 $album->name = 'Horrorscope';
13 $album->artist_id = $artist->id;
14 $album->save();
15
16 return View::make('hello');
17 });
Entrambi gli esempi restituiscono lo stesso risultato e la relazione viene creata ugualmente.
Ricordati, chiaramente, di salvare sempre l’istanza tramite “save()”, prima di passarla al metodo
“associate()”. Saltando il “save()” non sarà possibile ottenere una primary key e, di conseguenza, non
sarà possibile popolare i campi relativi alle chiavi esterne.
I model correlati tramite una relazione molti a molti seguono una procedura leggermente differente.
Diamoci uno sguardo approfondito usando il model “Listener”.
Eloquent e le Relazioni 335
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $artist = new Artist;
8 $artist->name = 'Eve 6';
9 $artist->save();
10
11 $album = new Album;
12 $album->name = 'Horrorscope';
13 $album->artist()->associate($artist);
14 $album->save();
15
16 $listener = new Listener;
17 $listener->name = 'Naruto Uzumaki';
18 $listener->save();
19 $listener->albums()->save($album);
20
21 return View::make('hello');
22 });
Dopo aver popolato l’istanza del model “Listener”, dobbiamo innanzitutto salvarlo con “save()”.
Questo per il discorso relativo al bisogno della primary key accennato poco fa.
Ora, al posto di usare il metodo “associate()” abbiamo usato il metodo save()”, passandogli come pa-
rametro l’oggetto con il quale creare la relazione. L’effetto è praticamente lo stesso, con la differenza
che stavolta verrà coinvolta anche la tabella dei pivot, aggiornata con le ultime informazioni.
Ad ogni modo, se dovessi aver bisogno di effettuare un’associazione usando solo la chiave primaria
puoi comunque usare “attach()”, specificando appunto la primary key da usare.
In questo modo:
Eloquent e le Relazioni 336
1 $album->artist()->attach(2);
Dopo aver visto come creare un’associazione vediamo invece come rimuoverla: il metodo da usare,
stavolta, è “detach()”. Puoi usare come parametri sia la chiave primaria che un intero oggetto.
Ad esempio:
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 $album = Album::find(5);
8 $listener = Listener::find(2);
9 $album->listeners()->detach($listener);
10
11 return View::make('hello');
12 });
Per rimuovere TUTTE le associazioni di un oggetto che ha una relazione molti a molti basta chiamare
il metodo “detach()” senza nessun parametro aggiuntivo.
Esattamente così:
1 $album->listeners()->detach();
Da questo momento l’album del nostro esempio non ha più neanche un ascoltatore.
Eloquent è decisamente vasto, pieno di features di ogni genere da apprezzare. Non posso riportarle,
però, tutte ora. Sarebbe controproducente e non aiuterebbe nel tuo percorso di apprendimento.
Siamo arrivati ad un primo punto importantissimo: ora, infatti, hai tutti gli strumenti di base per
creare un’applicazione semplice, ma completa.
Nel prossimo capitolo creerò un primo piccolo sistema da usare per gestire i giochi della mia
Playstation 3.
Se vuoi saperne di più su Eloquent, invece, dovrai aspettare ancora un po’. Ti prometto, però, che
più in la avrai pane per i tuoi denti.
Applicazione Completa 1: Playstation
Game Collection
Bene, è arrivato il momento! Adesso tocca a te e, insieme, creeremo una prima applicazione
dall’inizio alla fine. Dopo tanta teoria direi che serve un po’ di pratica, no? Questa volta faremo
un’applicazione per gestire una collezione di giochi. Precisamente, di giochi per la Playstation. Una
sorta di “inventario” per tenerne traccia agevolmente.
Lo so, lo so, stai impazzendo per l’eccitazione.
Questa prima applicazione gestirà i vari giochi e si occuperà di memorizzarne i dati: nome del gioco,
publisher e permetterà anche di tenere traccia dei giochi finiti o ancora in corso. Si tratterà, come
puoi ben immaginare, di un’applicazione CRUD based (Create, Read, Update, Delete, nel caso non
te lo ricordi). Anche se all’apparenza sembra semplice è comunque un ottimo punto di partenza per
applicare quello che hai imparato nei precedenti capitoli del libro.
Una volta terminata questa prima applicazione ci sposteremo ad un livello più avanzato, analizzando
nel dettaglio le altre interessanti features che Laravel ci mette a disposizione. Come ciliegina sulla
torta, inoltre, una volta viste queste altre funzionalità torneremo a questa prima applicazione, per
integrarla con le nuove conoscenze acquisite.
Non sprechiamo altro tempo. Che ne dici di cominciare?
Il momento di riflettere!
Non stai pensando mica di buttarti subito sul codice, vero? Fermo! Alla base di ogni buona
applicazione c’è sempre un’analisi alle spalle. Senza, un’applicazione non ha molte possibilità di
sopravvivenza.
Prima riflessione: la nostra applicazione dovrà gestire video giochi. Ergo, pensiamo alle proprietà da
memorizzare per ogni singolo gioco. Rimaniamo sul semplice, per il momento.
Ecco cosa mi viene in mente:
• Titolo
• Publisher
• Stato (Completato/Non Completato)
Ora che abbiamo questi dati dobbiamo plasmarli e adattarli, facendoli diventare parte dello schema
di una tabella. Ad ogni dato, quindi, assegnamo un nome, un tipo e una lunghezza, dove necessario.
Applicazione Completa 1: Playstation Game Collection 338
Come schema dovrebbe andare bene: rende l’idea in modo piuttosto eloquente. Due stringhe, per
memorizzare titolo e publisher, con l’aggiunta di una booleana (true o false) per indicare se il gioco
è stato finito oppure no.
Dopo aver pensato ai dati possiamo passare alle features. Cosa deve fare un’applicazione del genere?
Saremo capaci di inserire nuovi giochi e avere sempre a portata di mano una lista completa. Potremo
effettuare anche delle modifiche, oppure cancellare giochi sbagliati. Come detto precedentemente:
un’applicazione CRUD. Sentirai spesso questo termine: molte applicazioni nel mondo reale si basano
su questo “pattern”.
Bene! Adesso usiamo ancora il cervello, per immaginare. Immaginare su quali pagine queste azioni
verranno effettuate. Che forma dare a queste pagine. Spremiamo le meningi per bene.
Vediamo… nella pagina di creazione potrebbe esserci un form, con tutti i vari input necessari ad
aggiungere un nuovo gioco. La schermata di lista, invece, potrebbe essere una tabella con tutti i
titoli ordinati secondo vari criteri, magari da scegliere.
Poi, la pagina di modifica. Normalmente molti sviluppatori usano la stessa pagina, sia per la modifica
che per il salvataggio della modifica. Per ora rimarremo sul semplice e di pagine ne faremo due.
Infine, la pagina di cancellazione: verrà data la possibilità di confermare la cancellazione, in modo
tale da evitare errori da parte dell’utente.
Dire che ci siamo… le pagine fino ad ora sono:
Ottimo! Ora che abbiamo tutto quello che ci serve possiamo cominciare.
Applicazione Completa 1: Playstation Game Collection 339
Il momento dell’azione!
Tutto il codice che verrà creato in questo capitolo è già disponibile in un repository, creato ad hoc
per l’occasione (codebright/games-app²⁰). Sentiti pure libero di scaricarlo, esplorarlo e giocarci!
Comunque, torniamo al codice. Per prima cosa effettua il deploy di una copia di Laravel, nella cartella
“games” creata appositamente. Come sempre avremo bisogno di installare le varie dipendenze
tramite Composer.
1 $ composer install
2 Loading composer repositories with package information
3 Installing dependencies (including require-dev)
4 - Installing doctrine/lexer (dev-master bc0e1f0)
5 Loading from cache
6
7 - Installing doctrine/annotations (v1.1.2)
8 Loading from cache
9
10 - Installing doctrine/collections (dev-master bcb5377)
11 Loading from cache
12
13 - Installing doctrine/cache (v1.1)
14 Loading from cache
15
16 - Installing doctrine/inflector (dev-master 8b4b3cc)
17 Loading from cache
18
19 ... lots more ...
20
21 - Installing laravel/framework (4.0.x-dev 733492c)
22 Downloading: 100%
23
24 monolog/monolog suggests installing mlehner/gelf-php (Allow sending log messages\
25 to a GrayLog2 server)
26 monolog/monolog suggests installing ext-amqp (Allow sending log messages to an A\
27 MQP server (1.0+ required))
28 monolog/monolog suggests installing ext-mongo (Allow sending log messages to a M\
29 ongoDB server)
30 monolog/monolog suggests installing doctrine/couchdb (Allow sending log messages\
31 to a CouchDB server)
32 monolog/monolog suggests installing raven/raven (Allow sending log messages to a\
²⁰http://github.com/codebright/gamesapp
Applicazione Completa 1: Playstation Game Collection 340
33 Sentry server)
34 symfony/translation suggests installing symfony/config ()
35 symfony/translation suggests installing symfony/yaml ()
36 symfony/routing suggests installing symfony/config ()
37 symfony/routing suggests installing symfony/yaml ()
38 symfony/debug suggests installing symfony/class-loader ()
39 symfony/event-dispatcher suggests installing symfony/dependency-injection ()
40 symfony/http-kernel suggests installing symfony/class-loader ()
41 symfony/http-kernel suggests installing symfony/config ()
42 symfony/http-kernel suggests installing symfony/dependency-injection ()
43 predis/predis suggests installing ext-phpiredis (Allows faster serialization and\
44 deserialization of the Redis protocol)
45 Writing lock file
46 Generating autoload files
47 Generating optimized class loader
Fatto!
Andiamo avanti e passiamo al database.
Database
Per tenere traccia di tutti i dati creeremo un database. Precisamente, un database MySQL che
chiameremo “games”.
Provvederemo, successivamente, a sistemare adeguatamente il file di configurazione di Laravel in
modo tale da collegarlo correttamente. Ecco qui il mio.
1 // app/config/database.php
2
3 /*
4 |--------------------------------------------------------------------------
5 | Default Database Connection Name
6 |--------------------------------------------------------------------------
7 */
8
9 'default' => 'mysql',
10
11 /*
12 |--------------------------------------------------------------------------
13 | Database Connections
14 |--------------------------------------------------------------------------
Applicazione Completa 1: Playstation Game Collection 341
15 */
16
17 'connections' => array(
18
19 'mysql' => array(
20 'driver' => 'mysql',
21 'host' => 'localhost',
22 'database' => 'games',
23 'username' => 'root',
24 'password' => 'foobar',
25 'charset' => 'utf8',
26 'collation' => 'utf8_unicode_ci',
27 'prefix' => '',
28 ),
29
30 ),
Abbiamo il database ma ci servono le tabelle. Generiamo un file per lo Schema Builder attraverso
Artisan.
Compiliamo i due metodi “up()” e “down()” adeguatamente, applicando nella pratica le riflessioni
fatte precedentemente sul database.
Ecco la migration completa:
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 // app/database/migrations/2013_09_14_155847_create_games.php
6
7 class CreateGames extends Migration {
8
9 /**
10 * Run the migrations.
11 *
12 * @return void
13 */
Applicazione Completa 1: Playstation Game Collection 342
Nel metodo “up()” abbiamo creato la tabella “games” con tutte le proprietà menzionate preceden-
temente. Abbiamo inoltre aggiunto il campo auto-incrementante “id” e i vari timestamps, dato che
gestiremo tutto con Eloquent ORM.
Nel metodo “down()”, invece, non faremo altro che gestire la cancellazione della tabella.
Con la migration al suo posto possiamo avviare la procedura di costruzione:
Anche questa è fatta: lo schema è stato implementato esattamente come richiesto. Dopo averlo
verificato, per sicurezza, perchè non passare ai model?
Procediamo…
Applicazione Completa 1: Playstation Game Collection 343
1 <?php
2
3 // app/models/Game.php
4
5 class Game extends Eloquent
6 {
7
8 }
Fatto!
Lo so, quasi non c’è più gusto, per quanto è facile!
Continuiamo come treni verso la gestione dei Controller!
Controller
La nostra applicazione avrà, precisamente, quattro pagine. Un singolo controller, quindi, sarà più
che sufficiente a gestire il tutto. Comunque sia, per ora creiamo il controller abbozzando i metodi, e
non scrivendoli completamente. Lasciamo solo degli stub, che aggiorneremo successivamente.
Nota: ricorda di cancellare il file “HomeController.php”, dato che non serve più.
1 <?php
2
3 // app/controllers/GamesController.php
4
5 class GamesController extends BaseController
6 {
7 public function index()
8 {
9 // Show a listing of games.
10 return View::make('index');
11 }
12
13 public function create()
14 {
15 // Show the create game form.
16 return View::make('create');
17 }
18
19 public function handleCreate()
20 {
Applicazione Completa 1: Playstation Game Collection 344
Come puoi ben immaginare, ogni metodo riguarda una action ben precisa dell’applicazione che
stiamo creando. Alcuni di questi non fanno altro che mostrare un form, altri invece servono a
gestire i dati inseriti in questi form. Avrai anche notato che i metodi “edit()” e “delete()” accettano
un parametro $game di tipo “Game”. Al momento giusto ti spiegherò tutto, non temere.
Creiamo, adesso, le view (vuote, per ora) che verranno usate dal controller. Precisamente:
• app/views/index.blade.php
• app/views/create.blade.php
• app/views/edit.blade.php
• app/views/delete.blade.php
Routes
Ora prepariamo le routes della nostra applicazione. Eccole:
Applicazione Completa 1: Playstation Game Collection 345
1 <?php
2
3 // app/routes.php
4
5 /*
6 |--------------------------------------------------------------------------
7 | Application Routes
8 |--------------------------------------------------------------------------
9 |
10 | Here is where you can register all of the routes for an application.
11 | It's a breeze. Simply tell Laravel the URIs it should respond to
12 | and give it the Closure to execute when that URI is requested.
13 |
14 */
15
16 // Bind route parameters.
17 Route::model('game', 'Game');
18
19 // Show pages.
20 Route::get('/', 'GamesController@index');
21 Route::get('/create', 'GamesController@create');
22 Route::get('/edit/{game}', 'GamesController@edit');
23 Route::get('/delete/{game}', 'GamesController@delete');
24
25 // Handle form submissions.
26 Route::post('/create', 'GamesController@handleCreate');
27 Route::post('/edit', 'GamesController@handleEdit');
28 Route::post('/delete', 'GamesController@handleDelete');
Ehi ehi, aspetta. Che cosa diamine è “Route::model()”? Te lo dico io: un’altra piccola magia che
Laravel ti offre. “Route::model()”, infatti, permette di definire che un determinato parametro con un
certo nome (“game”, in questo caso), viene automaticamente collegato ad un’istanza di un Eloquent
Model definito come secondo parametro.
Per fartela breve: nell’esempio qui di sopra abbiamo appena detto a Laravel di usare il model “Game”
ogni qualvolta definiremo un parametro di nome “game” nelle nostre routes. Automaticamente,
inoltre, Laravel eseguirà un controllo usando il valore inserito come chiave primaria. Basterà inserire
quindi l’id univoco del gioco per ottenere il relativo record.
Utilissimo, vero?
Da questo punto in poi le cose dovrebbero essere abbastanza familiari, vero? Abbiamo già parlato
del routing, dovresti già sapere cosa stai leggendo.
Applicazione Completa 1: Playstation Game Collection 346
Per sicurezza, verifichiamo che tutto funzioni. Mettiamo su al volo un server di prova, attraverso
l’istruzione “serve” di Artisan.
Il nostro server di sviluppo adesso gira sulla porta 8000. Visitiamo l’indirizzo “http://localhost:8000”
per ricevere un bel template… bianco. Totalmente bianco.
Si, è giunto il momento di passare alle views.
Views
Per i nostri template useremo Twitter Bootstrap. Si tratta di un framework CSS (magari lo conosci
già) che si occuperà di dare uno stile di base alla nostra applicazione. Ti consiglio di darci un’occhiata,
per fare prototipi è perfetto!
Chiaramente non ci addentreremo molto per quanto riguarda il frontend, ma faremo solo lo stretto
necessario.
Innanzitutto, sappiamo che c’è del codice di base che ogni pagina di template condivide. Qualcosa
del genere:
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Games Collection</title>
6 </head>
7 <body>
8 <!-- website content here -->
9 </body>
10 </html>
La nostra applicazione, inoltre, conterà anche un foglio di stile comune a tutte le pagine.
Ogni pagina della nostra applicazione “estenderà” questa prima pagina basilare che stiamo creando.
Costruiremo questa prima pagina e la chiameremo “layout”: tutte le altre saranno semplici derivate
della prima.
Ecco il file layout.blade.php pronto. Lo so, non si vedrà un granchè negli e-readers… ma non posso
compattare di più l’html. Mi dispiace!
Applicazione Completa 1: Playstation Game Collection 347
1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Games Collection</title>
6 <link rel="stylesheet" href="{{ asset('css/bootstrap.min.css') }}" />
7 </head>
8 <body>
9 <div class="container">
10 <nav class="navbar navbar-default" role="navigation">
11 <div class="navbar-header">
12 <a href="{{ action('GamesController@index') }}" class="navbar-br\
13 and">Games Collection</a>
14 </div>
15 </nav>
16 @yield('content')
17 </div>
18 </body>
19 </html>
Diamo uno sguardo al codice nel dettaglio. Partiamo dalla prima linea interessante, che specifica il
file CSS da usare.
Come già detto, stiamo usando un singolo file globale per ogni view. Includendo qui il file CSS
di Twitter Bootstrap potremo facilmente estendere ogni eventuale modifica a tutte le pagine della
nostra applicazione. Comodo, vero?
La seconda istruzione piuttosto interessante riguarda il link alla pagina principale del nostro
software.
Ancora una volta stiamo usando una funzione helper per evitare spiacevoli collisioni con il nostro
sistema di routing basato sui path. Stavolta, tramite “action()” creeremo un URL assoluto per il
collegamento alla specifica route. Usare “action()” insieme alle {{ doppie parentesi graffe }} effettua
direttamente l’echo in output del link. Il parametro specificato è un controller: per questo motivo c’è
il simbolo @ a separare i due segmenti.
Questo però lo sapevi già, vero?
Passiamo, infine, alla linea forse più importante del nostro template:
Applicazione Completa 1: Playstation Game Collection 348
1 @yield('content')
La direttiva @yield() permette di creare una sezione in cui sarà possibile, successivamente, inietteare
del codice tramite il sistema di blade. Useremo il nome “content” per identificare questa regione del
template.
Ora che abbiamo sistemato la view base possiamo creare tutte le altre! Partiamo dalla lista dei giochi.
Prima di modificare la view, però, torniamo un secondo al controller per aggiungere quel pizzico di
logica necessaria.
Nella action “index()” stiamo usando “Game::all()” per recuperare la lista dei giochi presenti nel
sistema. A quel punto usiamo “compact()” e “View::make()” per passare alla view l’intera collection.
Nel caso tu non abbia mai usato “compact()” prima d’ora, sappi che si tratta di un metodo che crea
un array partendo da un qualsiasi numero di variabili, che usa i nomi delle variabili stesse come
indici degli elementi del futuro array. L’esatto opposto del metodo “extract()”.
Adesso mostriamo questi giochi! Creiamo la view app/views/index.blade.php.
1 @extends('layout')
2
3 @section('content')
4 <div class="page-header">
5 <h1>All Games <small>Gotta catch 'em all!</small></h1>
6 </div>
7
8 <div class="panel panel-default">
9 <div class="panel-body">
10 <a href="{{ action('GamesController@create', $game->id) }}" class="b\
11 tn btn-primary">Create Game</a>
12 </div>
13 </div>
14
15 @if ($games->isEmpty())
16 <p>There are no games! :(</p>
17 @else
18 <table class="table table-striped">
Applicazione Completa 1: Playstation Game Collection 349
19 <thead>
20 <tr>
21 <th>Title</th>
22 <th>Publisher</th>
23 <th>Complete</th>
24 <th>Actions</th>
25 </tr>
26 </thead>
27 <tbody>
28 @foreach($games as $game)
29 <tr>
30 <td>{{ $game->title }}</td>
31 <td>{{ $game->publisher }}</td>
32 <td>{{ $game->complete ? 'Yes' : 'No' }}</td>
33 <td>
34 <a href="{{ action('GamesController@edit', $game->id) }}\
35 " class="btn btn-default">Edit</a>
36 <a href="{{ action('GamesController@delete', $game->id) \
37 }}" class="btn btn-danger">Delete</a>
38 </td>
39 </tr>
40 @endforeach
41 </tbody>
42 </table>
43 @endif
44 @stop
La funzione di blade “@extends(‘layout’)”, in corrispondenza della prima linea di codice, indica che
vogliamo estendere il layout creato precedentemente con questa sezione. Il layout infatti non è altro
che un wrapper.
Infatti, subito dopo, abbiamo la @section.
1 @section('content')
2 <!-- HTML here -->
3 @stop
La funzione “@section” di blade è usata per iniettare i contenuti specificati dentro la regione definita
con “@yield” in precedenza, nel file “layout”. L’unico parametro da specificare è il nome della regione
che stiamo iniettando.
Arriviamo quindi all’istruzione “@if”. Dobbiamo controllare, infatti, se la nostra collection è vuota
(non ancora ci sono giochi nel sistema) oppure se qualche elemento effettivamente c’è. Il metodo
Applicazione Completa 1: Playstation Game Collection 350
usato per il controllo è “isEmpty()”. In questo modo potremo mostrare all’utente una notifica nel
caso non ci sia ancora nessun dato memorizzato.
Se, invece, i giochi ci sono dovremmo mostrarli. Creiamo quindi una tabella ed al suo interno un
loop. Questo loop, preso in esame nel dettaglio qui di seguito, eseguirà l’output dei vari attributi
(titolo e publisher).
1 @foreach($games as $game)
2 <tr>
3 <td>{{ $game->title }}</td>
4 <td>{{ $game->publisher }}</td>
5 <td>{{ $game->complete ? 'Yes' : 'No' }}</td>
6 <td>
7 <a href="{{ action('GamesController@edit', $game->id) }}" class="btn btn\
8 -default">Edit</a>
9 <a href="{{ action('GamesController@delete', $game->id) }}" class="btn b\
10 tn-danger">Delete</a>
11 </td>
12 </tr>
13 @endforeach
Tramite un operatore ternario, inoltre, abbiamo indicato anche lo status di un gioco (completato
oppure no).
In aggiunta, per ogni gioco verranno presentati due pulsanti: uno di modifica e l’altro di cancella-
zione.
Anche qui viene usato il metodo “action()”. Stavolta, però, viene specificato anche un secondo
parametro. Si tratta dell’id del gioco da modificare (o cancellare).
La prima view è fatta: adesso tocca alla successiva. Ecco la view app/views/create.blade.php, da
usare per inserire un nuovo gioco.
Applicazione Completa 1: Playstation Game Collection 351
1 @extends('layout')
2
3 @section('content')
4 <div class="page-header">
5 <h1>Create Game <small>and someday finish it!</small></h1>
6 </div>
7
8 <form action="{{ action('GamesController@handleCreate') }}" method="post" ro\
9 le="form">
10 <div class="form-group">
11 <label for="title">Title</label>
12 <input type="text" class="form-control" name="title" />
13 </div>
14 <div class="form-group">
15 <label for="publisher">Publisher</label>
16 <input type="text" class="form-control" name="publisher" />
17 </div>
18 <div class="checkbox">
19 <label for="complete">
20 <input type="checkbox" name="complete" /> Complete?
21 </label>
22 </div>
23 <input type="submit" value="Create" class="btn btn-primary" />
24 <a href="{{ action('GamesController@index') }}" class="btn btn-link">Can\
25 cel</a>
26 </form>
27 @stop
La struttura è la stessa vista prima: stiamo sempre estendendo la pagina “layout”. Questa volta, però,
stiamo iniettando un form all’interno della regione “content”. Vediamolo da vicino.
Il metodo “action()” stavolta è usato (pardon per il gioco di parole, ma occhio a non fare confusione
tra metodo e attributo) per definire la “action” del form. Ti starai chiedendo perchè non sto usando i
metodi di opening del form visti nel capitolo apposito: questione di forma e preferenze. Usare questi
helper in questo modo mi sa di “pulito” e istintivamente uso sempre questo stile. Poi, comunque,
ricordao: è tutto a tua discrezione.
Il resto del form, ad ogni modo, è decisamente nella norma. Niente di complesso, solo dei campi da
usare per definire i vari dettagli di un nuovo gioco. Non manca, ovviamente, un pulsante di submit
e un link per l’annullamento dell’operazione, nel caso l’utente cambi idea.
Applicazione Completa 1: Playstation Game Collection 352
Passiamo ora alla schermata di cancellazione: il seguente codice sarà inserito nel file app/views/-
delete.blade.php.
1 @extends('layout')
2
3 @section('content')
4 <div class="page-header">
5 <h1>Delete {{ $game->title }} <small>Are you sure?</small></h1>
6 </div>
7 <form action="{{ action('GamesController@handleDelete') }}" method="post" ro\
8 le="form">
9 <input type="hidden" name="game" value="{{ $game->id }}" />
10 <input type="submit" class="btn btn-danger" value="Yes" />
11 <a href="{{ action('GamesController@index') }}" class="btn btn-default">\
12 No way!</a>
13 </form>
14 @stop
Piano piano cominci a prenderci la mano, vero? Anche qui abbiamo un altro form: il target stavolta è
la route “handleDelete” e contiene un campo (nascosto) usato per identificare il gioco che vogliamo
cancellare dal sistema.
Il pulsante “Yes” avvierà il submit del form, mentre il pulsante “No” effettuerà un redirect alla lista.
Esattamente quello che ci si aspetta da un meccanismo di “Cancel”.
Ok… cosa rimane ancora?
Ah si! La view di modifica! Eccola qui, nel file app/views/edit.blade.php. Non dissimile dalla view
di inserimento di un nuovo gioco, come puoi vedere.
1 @extends('layout')
2
3 @section('content')
4 <div class="page-header">
5 <h1>Edit Game <small>Go on, mark it complete!</small></h1>
6 </div>
7
8 <form action="{{ action('GamesController@handleEdit') }}" method="post" role\
9 ="form">
10 <input type="hidden" name="id" value="{{ $game->id }}">
11
12 <div class="form-group">
13 <label for="title">Title</label>
14 <input type="text" class="form-control" name="title" value="{{ $game\
Applicazione Completa 1: Playstation Game Collection 353
Anche qui effettuiamo l’iniezione di codice da una view all’altra tramite l’extend. Stavolta l’opera-
tore ternario viene usato per segnare la casella di “completato” in base al valore opportuno.
Bene: adesso c’è tutto! Con le view al proprio posto direi che manca solo un po’ di logica per far
funzionare tutto, vero?
Logica dell’Applicazione
Cominciamo dalla action di partenza: vediamo cosa abbiamo al momento.
Qui ci siamo: abbiamo la lista e la passiamo in maniera adeguata alla view. Vediamo invece
handleCreate().
Applicazione Completa 1: Playstation Game Collection 354
Qui non c’è ancora niente. Ad essere onesti tra l’altro non credo che quel commento si occuperà di
gestire il form per noi.
Scherzi a parte, aggiungiamo queste istruzioni:
Abbiamo creato una nuova istanza del model Game e ne abbiamo settato adeguatamente le proprietà.
I valori sono prelevati dalla richiesta grazie al metodo “Input::get()”. Tramite il metodo “save()”,
infine, i valori vengono salvati nel database.
C’è bisogno di dare un segnale all’utente, però! Deve sapere (o almeno intuire) che il gioco è stato
inserito correttamente! Per ora reindirizziamolo semplicemente alla pagina principale.
Direi che pure qui manca un po’ di logica: c’è bisogno di popolare il form. La view lo fa già, ma da
dove prende i dati se non abbiamo definito una “sorgente”? Sistemiamo le cose.
Il metodo “compact()” anche questa volta crea l’array con, al suo interno, l’oggetto “game”. Se
non ti spieghi da dove arrivino i dati, torna un po’ indietro di qualche pagina e controlla cosa ho
detto sul metodo “Route::model()”. Passiamo adesso alla gestione del form: modifichiamo la action
handleEdit().
Da così…
a così:
Il metodo “findOrFail()” è simile al metodo “find()”, con l’eccezione che, in caso di record non trovato,
verrà lanciata un’eccezione di tipo 404. Una scorciatoia piuttosto utile, direi!
Anche qui, dopo la modifica, si effettua il redirect alla pagina principale.
Adesso i nostri giochi non solo possono essere inseriti, ma anche modificati! Manca solo la
cancellazione: diamo uno sguardo a handleDelete().
Anche qui stiamo usando il “findOrFail()” per sicurezza. Una volta effettuata la cancellazione viene
chiamato il redirect verso la home page.
Eccolo, il nostro GameController, come appare dopo questo “restyle”.
1 <?php
2
3 // app/controllers/GamesController.php
4
5 class GamesController extends BaseController
6 {
7 public function index()
8 {
9 // Show a listing of games.
10 $games = Game::all();
11
12 return View::make('index', compact('games'));
Applicazione Completa 1: Playstation Game Collection 357
13 }
14
15 public function create()
16 {
17 // Show the create game form.
18 return View::make('create');
19 }
20
21 public function handleCreate()
22 {
23 // Handle create form submission.
24 $game = new Game;
25 $game->title = Input::get('title');
26 $game->publisher = Input::get('publisher');
27 $game->complete = Input::has('complete');
28 $game->save();
29
30 return Redirect::action('GamesController@index');
31 }
32
33 public function edit(Game $game)
34 {
35 // Show the edit game form.
36 return View::make('edit', compact('game'));
37 }
38
39 public function handleEdit()
40 {
41 // Handle edit form submission.
42 $game = Game::findOrFail(Input::get('id'));
43 $game->title = Input::get('title');
44 $game->publisher = Input::get('publisher');
45 $game->complete = Input::has('complete');
46 $game->save();
47
48 return Redirect::action('GamesController@index');
49 }
50
51 public function delete(Game $game)
52 {
53 // Show delete confirmation page.
54 return View::make('delete', compact('game'));
Applicazione Completa 1: Playstation Game Collection 358
55 }
56
57 public function handleDelete()
58 {
59 // Handle the delete confirmation.
60 $id = Input::get('game');
61 $game = Game::findOrFail($id);
62 $game->delete();
63
64 return Redirect::action('GamesController@index');
65 }
66 }
Ci hai fatto caso? L’intera applicazione fonda tutta la sua logica su… 66 righe di codice! Non è
fantastico?
Stop! Relax
Innanzitutto assicurati che l’applicazione sia servita dal webserver: in caso contrario usa il comando
“serve”.
Ricorda, puoi trovare tutto il codice di cui hai bisogno (nel caso volessi confrontarlo con il tuo) su
Github, nel repository codebright/games-app²¹.
Esercizi
Ci sono tante variazioni sul tema che puoi implementare per esercitarti al meglio. Ad essere onesti
non ho mai usato molto le sezioni “Esercizi” dei libri, per cui se vuoi salta pure la sezione.
Se invece vuoi fare le cose per bene, perchè non provare qualche altra cosa? Magari un po’ di
validazione, per evitare quelli che sono errori di “distrazione” dei programmatori stessi!
Un esempio? Prova ad inserire un gioco senza nome… ci sei riuscito, vero?
Beh, sappi che non va bene.
Nei prossimi capitoli, inoltre, vedremo un sacco di novità: una su tutte l’Authentication.
²¹http://github.com/codebright/gamesapp
Autenticazione
Lo so, normalmente comincio un nuovo capitolo con una piccola storia. Questa settimana però ho
poco tempo, per cui ecco il piano: lavoriamo davvero bene con l’immaginazione e crederemo tutti sul
serio che io abbia scritto una fantastica e divertentissima storia su Ian Landsman, due leoni marini
addomesticati, una bottiglia di Advocaat e la metropolitana di Roma.
Si, potrebbe funzionare.
Bene, veniamo a noi: cosa scopriamo in questo nuovo capitolo?
Ognuno di noi ha i suoi segreti… vero? Si, anche tu, segreti oscuri, che non vuoi mai veder rivelati
in giro.
Cosa? Non ce li hai? Davvero? Oh, ok.
Allora, diciamo che ce li ho solo io. Dobbiamo proteggere questi segreti e tenerli lontani da occhi
indiscreti.
In questo capitolo impariamo ad usare il sistema di Authentication di Laravel, per rendere
determinati contenuti accessibili solo ad un gruppo ristretto di utenti.
Detto questo, cominciamo! Prima di tutto prepara quello di cui hai bisogno: un nuovo progetto
Laravel con un database correttamente configurato. Ricordi come effettuare il setup del database,
vero? In caso contrario, torna indietro al capitolo dedicato.
Inoltre, prima di iniziare, c’è bisogno di definire dove sistemare i dati delle nostre sessioni. Una
sessione è un piccolo payload testuale con determinati dati associati al browser al loro interno.
Vengono usate da PHP (e da Laravel) per “ricordare” determinate cose tra una richiesta e l’altra.
Entriamo nel file app/config/session.php:
1 /*
2 |--------------------------------------------------------------------------
3 | Default Session Driver
4 |--------------------------------------------------------------------------
5 |
6 | This option controls the default session "driver" that will be used on
7 | requests. By default, we will use the lightweight native driver but
8 | you may specify any of the other wonderful drivers provided here.
9 |
10 | Supported: "native", "cookie", "database", "apc",
11 | "memcached", "redis", "array"
12 |
Autenticazione 361
13 */
14
15 'driver' => 'database',
Come puoi vedere, Laravel offre svariati modi di agire per quanto riguarda la memorizzazione delle
sessioni. Puoi usare qualasiasi metodo tu voglia: in questo capitolo la mia scelta sarà “database”. I
dati di sessione verranno quindi memorizzati sul database.
Il metodo di memorizzazione non è la sola cosa da scegliere: si può specificare la lunghezza
della sessione, il nome del cookie usato, il nome della tabella dedicata e così via. Per ora non ci
complichiamo la vita e lasciamo tutto come di default.
Ora che abbiamo deciso il metodo dobbiamo provvedere a creare la tabella per memorizzare queste
informazioni. Ancora una volta Laravel ti facilita la vita, anzichè complicarla: usa il comando
artisan session:table per effettuare il deploy della tabella di sessione.
Il comando artisan session:table crea una migration che, a sua volta, crea la tabella di cui
abbiamo bisogno. Abbiamo già parlato delle migrations in precedenza: sai bene quindi che tutto
quello che ti rimane da fare è eseguire il comando migrate.
1 <?php
2
3 use Illuminate\Auth\UserInterface;
4 use Illuminate\Auth\Reminders\RemindableInterface;
5
6 class User extends Eloquent implements UserInterface, RemindableInterface {
7
8 /**
9 * The database table used by the model.
10 *
11 * @var string
12 */
Autenticazione 362
Un sacco di roba utile, come puoi vedere. Niente superficialità, però: vediamo nel dettaglio cosa
abbiamo.
Autenticazione 363
1 <?php
2
3 use Illuminate\Auth\UserInterface;
4 use Illuminate\Auth\Reminders\RemindableInterface;
5
6 class User extends Eloquent implements UserInterface, RemindableInterface {
1 /**
2 * The attributes excluded from the model's JSON form.
3 *
4 * @var array
5 */
6 protected $hidden = array('password');
I campi contenuti nell’array $hidden saranno esclusi da qualsiasi output ottenuto tramite toJson()
e __toString(). Insomma, non vogliamo mandare in giro la password del nostro utente, giusto?
Anche qui comunque decidi tu cosa fare: io per ora lascio tutto così.
Diamo uno sguardo, adesso, ai metodi. Cominciamo da getAuthIdentifier().
1 <?php
2
3 /**
4 * Get the unique identifier for the user.
5 *
6 * @return mixed
7 */
8 public function getAuthIdentifier()
9 {
10 return $this->getKey();
11 }
Questo metodo permette di specificare una proprietà che verrà assunta come id univoco per l’utente
in questione. Di default è la chiave primaria: infatti il valore ritornato è quello di $this->getKey(),
ereditato dal model base.
Autenticazione 364
1 /**
2 * Get the password for the user.
3 *
4 * @return string
5 */
6 public function getAuthPassword()
7 {
8 return $this->password;
9 }
Il metodo getAuthPassword() viene usato per identificare la proprietà usata come password dal
model. Di default, ovviamente, il valore usato è password.
Infine, ecco che arriviamo al metodo getReminderEmail().
1 /**
2 * Get the e-mail address where password reminders are sent.
3 *
4 * @return string
5 */
6 public function getReminderEmail()
7 {
8 return $this->email;
9 }
Qui andiamo a specificare il campo email da usare nel meccanismo di remind della password.
A questo punto… direi che è ora di creare il model User!
1 <?php
2
3 use Illuminate\Database\Migrations\Migration;
4
5 class CreateUserTable extends Migration {
6
7 /**
8 * Run the migrations.
9 *
10 * @return void
11 */
12 public function up()
13 {
14 Schema::create('users', function($table)
15 {
16 $table->increments('id');
17 $table->string('username', 128)->unique();
18 $table->string('password', 60);
19 $table->string('email', 320)->unique();
20 $table->timestamps();
21 });
22 }
23
24 /**
25 * Reverse the migrations.
26 *
27 * @return void
28 */
29 public function down()
30 {
31 Schema::drop('users');
32 }
33
34 }
Nota: il campo password conta 60 caratteri per un motivo ben preciso. Tra poco ne parleremo.
Ho aggiunto, inoltre, unique() in corrispondenza di ‘username’ ed ‘email’, dato che vogliamo essere
sicuri di non avere duplicati. Inoltre, non fa mai male aggiungere un id univoco ed i cari vecchi
timestamps.
Avviamo la migration.
Autenticazione 366
Facciamo adesso un altro passo avanti: aggiungiamo i primi utenti al sistema. Hurrah!
Ci serve un form, però. D’altronde, tutti noi sviluppatori adoriamo i form… vero? Stavolta niente
validazione però!
Ecco il codice del nostro form: molto basilare e semplice. Mettiamolo nel file app/views/create_-
user_form.blade.php.
1 <html>
2 <head>
3 <title>Create User</title>
4 </head>
5 <body>
6 <form action="{{ url('user') }}" method="post">
7 <p><label for="username">Username:</label></p>
8 <p><input type="text" name="username" placeholder="Username" /></p>
9 <p><label for="email">Email:</label></p>
10 <p><input type="text" name="email" placeholder="Email" /></p>
11 <p><label for="password">Password:</label></p>
12 <p><input type="password" name="password" placeholder="Password" /><\
13 /p>
14 <p><input type="submit" value="Create"></p>
15 </form>
16 </body>
17 </html>
1 <?php
2
3 // app/routes.php
4
5 Route::get('/', function()
6 {
7 return View::make('hello');
8 });
9
Autenticazione 367
10 Route::get('/user', function()
11 {
12 return View::make('create_user_form');
13 });
14
15
16 Route::post('/user', function()
17 {
18 $user = new User;
19 $user->username = Input::get('username');
20 $user->password = Hash::make(Input::get('password'));
21 $user->email = Input::get('email');
22 $user->save();
23
24 return Response::make('User created! Hurray!');
25 });
Abbiamo lasciato la route “/” di benvenuto nel file “routes.php”. Dopodiché abbiamo aggiunto una
GET /user, da usare per mostrare il form.
Infine, per concludere, abbiamo creato una route POST /user per creare il nuovo utente sul database,
usando i dati del form.
Da notare che abbiamo usato Hash::make() per crittare la password tramite il meccanismo bcrypt,
adottato di default da Laravel.
Anzi, ti dico qualcosa di più: prova a richiamare più volte Hash::make() sulla password. Verrà
generata una stringa sempre nuova e sempre diversa! Niente paura però: è perfettamente normale.
Anche con stringhe in output diverse, infatti, l’algoritmo di autenticazione riconoscerà ugualmente
la password come valida.
Magia della matematica, baby.
Di ritorno, per questioni di velocità, ritorniamo in output un semplice messaggio testuale, che avvisa
il corretto esito dell’operazione.
Bene! Adesso proviamo il tutto. Visitiamo /user per creare un nuovo utente. Se non hai ancora
creato un server in locale per i tuoi progetti, il comando php artisan serve configura al volo il
server PHP built-in.
Ora si che possiamo nascondere i nostri più oscuri segreti! Oh, no, aspetta. Stiamo scordando
qualcosa…
Diamine, si! Mancano ancora i segreti. Come ho fatto a non pensarci?
Autenticazione 368
Ok, vediamo cosa potrei nascondere… ci sono! La lista segreta delle celebrità che amo! No, non ditelo
alla mia ragazza. Rimane un segreto.
Innanzitutto creiamo una route GET /crush che mostri una view.
<?php
1 // app/routes.php
2
3 Route::get('/crush', function()
4 {
5 return View::make('crush');
6 });
1 <ul>
2 <li>Keira Knightley</li>
3 <li>Emma Watson</li>
4 </ul>
1 Route::get('/crush', array(
2 'before' => 'auth.basic',
3 function()
4 {
5 return View::make('crush');
6 }
7 ));
Autenticazione 369
Aggiunto il filtro il risultato è garantito: l’accesso è negato a tutti quelli che non hanno effettuato
il login. Non devi neanche fare altro: includere questo filtro è TUTTO quello che ti serve. Non ci
credi?
Proviamo subito. Visita la route GET /crush nel tuo browser. Verrà mostrato un form di login, il
cui aspetto potrebbe cambiare in base al browser che stai usando. Di default, il meccanismo base di
autenticazione usa l’indirizzo email dell’utente come username. Provvedi quindi ad inserire email e
password usati prima, nella “registrazione”.
Magia!
Ci è stato abilitato l’accesso e ora possiamo vedere la famigerata lista… meraviglioso! L’accesso è
garantito fino alla scadenza della sessione.
Bene, abbiamo effettuato il login. Perchè non preparare anche un logout? Basta pochissimo:
1 Route::get('/logout', function()
2 {
3 Auth::logout();
4 return Response::make('You are now logged out. :(');
5 });
Nella route GET /logout, tramite il metodo Auth::logout() eliminiamo la sessione attuale. Prova
a ritornare a GET /crush: devi inserire nuovamente le credenziali! Funziona!
Proviamo a fare qualche piccola modifica… usare l’email per il login non è male, ma se voles-
simo usare un campo “username”? Nessun problema: basta modificare il filtro “auth.basic” in
app/filters.php così:
1 Route::filter('auth.basic', function()
2 {
3 return Auth::basic('username');
4 });
Una semplice istruzione: il metodo Auth::basic() fornendo come parametro il nome del campo da
usare. Riprova la procedura di login: adesso dovrai usare l’username. Se va tutto bene, procedi di
nuovo con il logout.
Ora facciamo un altro passo avanti. L’autenticazione via HTTP basic è comoda, vero, ma non è un
granchè, alla fine. Non puoi personalizare il form di login, flessibilità zero e tanta imprevedibilità.
Dobbiamo usare un form vero, in una view.
Creiamo una view app/views/login_form.blade.php, come la seguente:
Autenticazione 370
1 <html>
2 <head>
3 <title>Login</title>
4 </head>
5 <body>
6 <form action="{{ url('login') }}" method="post">
7 <p><label for="username">Username:</label></p>
8 <p><input type="text" name="username" placeholder="Username" /></p>
9 <p><label for="password">Password:</label></p>
10 <p><input type="password" name="password" placeholder="Password" /><\
11 /p>
12 <p><input type="submit" value="Login"></p>
13 <p><input type="checkbox" name="remember" /> <label for="remember">R\
14 emember me.</label></p>
15 </form>
16 </body>
17 </html>
Perfetto: il nostro form di login è pronto. Simile all’altro, stavolta però con i soli campi username e
password. Il target del form sarà la route POST /login. Abbiamo sistemato anche una checkbox con
il testo “Ricordami”… non devo spiegarti a cosa serve, vero?
Creiamo la route per mostrare il form: GET /login. Lo so, la mia creatività è all’apice umano possibile.
1 <?php
2
3 // app/routes.php
4
5 Route::get('/login', function()
6 {
7 return View::make('login_form');
8 });
1 <?php
2
3 // app/routes.php
4
5 Route::get('/crush', array(
6 'before' => 'auth',
7 function()
8 {
9 return View::make('crush');
10 }
11 ));
1 Route::filter('auth', function()
2 {
3 if (Auth::guest()) return Redirect::guest('login');
4 });
Ah ecco! Il filtro auth usa Auth::guest() per capire se l’utente è un guest (non loggato) oppure no.
Nel caso viene riconosciuto come guest viene reindirizzato alla pagina /login.
Ora che abbiamo creato il nostro form, creiamo una route per gestire l’invio dei dati. Creiamo la
route POST /login.
1 <?php
2
3 // app/routes.php
4
5 Route::post('/login', function()
6 {
7 $credentials = Input::only('username', 'password');
8 if (Auth::attempt($credentials)) {
9 return Redirect::intended('/');
10 }
11 return Redirect::to('login');
12 });
L’autenticazione avviene in modo molto semplice. Innanzitutto viene costruito un array di creden-
ziali. In questo array viene messo l’username e la password, con le omonime chiavi. Non c’è bisogno
di fare l’hashing della password: ci pensa Laravel.
Autenticazione 372
Da notare che possiamo aggiungere anche altri dati a questo array. Ad esempio, si potrebbe
aggiungere un “enabled”, per verificare lo status dell’account.
Una volta che tale array è pronto viene passato al metodo Auth::attempt(). Questo metodo
ritorna una booleana che, ovviamente, indica l’esito dell’operazione. Se il tentativo va a buon fine
automaticamente il sistema crea la nuova sessione e, altrettanto automaticamente, ci ritroviamo
all’interno del sistema. Da autenticati.
Avrai notato, sicuramente, il metodo Redirect::intended(). Di cosa si tratta?
Dunque, immagina di avere la necessità di comprare una nuova auto. Fai un giro su internet e il
tuo amico Dayle ti manda un link di un offerta molto interessante. Per vederla, però, devi essere
autenticato nel sito. Clicchi sul link, quindi, e… ti ritrovi sulla pagina di login. Una volta entrato, ti
ritrovi nella pagina principale.
Che fine ha fatto la macchina?
Bene: il metodo Redirect::intended() serve esattamente a gestire questo genere di situazioni,
reindirizzando l’utente automaticamente alla pagina desiderata. Il primo parametro specificato,
infatti, non è la pagina in cui andare, ma una pagina di default da visualizzare nel caso non ci
siano pagine “desiderate”. Comodo, vero?
Direi che è arrivato il momento di fare un test, vero? Innanzitutto, logout! Visitiamo la route GET
/logout per essere sicuri di uscire. Vai alla route GET /crush per essere rimandato al form di login.
Compila il form, conferma e… siamo arrivati alla route GET /crush! Ottimo!
Bene, adesso dobbiamo fare un altro passo avanti e… oh, no.
Aspetta.
Stavamo per scordare la funzionalità “Ricordami l’accesso”. Non è un grande problema comunque:
basta fare qualche piccola modifica, come segue.
1 <?php
2
3 // app/routes.php
4
5 Route::post('/login', function()
6 {
7 $credentials = Input::only('username', 'password');
8 $remember = Input::has('remember');
9 if (Auth::attempt($credentials, $remember)) {
10 return Redirect::intended('/');
11 }
12 return Redirect::to('login');
13 });
Autenticazione 373
Abbiamo aggiunto giusto una linea di codice, per recuperare il valore specificato nella checkbox. È
bastato poi passare questo valore come secondo parametro del metodo Auth::attempt() e… voilà!
Il gioco è fatto.
Ora possiamo davvero andare avanti.
Una volta che sei dentro il sistema, infatti, può essere comodo recuperare i dati dell’utente
attualmente connesso. Non ti preoccupare, anche qui la vita è semplicissima! Tramite il metodo
Auth::user(), infatti, puoi recuperare i dati dell’utente attuale prelevandolo direttamente come
istanza del model.
Accedere ad una qualsiasi proprietà è pressoché immediato:
1 $user = Auth::user();
2 echo $user->username;
Facilissimo, vero? Ora, prima di concludere il capitolo, un ultimo trucco. A volte si può avere il
bisogno di “emulare” l’accesso di un altro utente. Qualcosa come “Passa a Paolo”, mentre tu ti chiami
Francesco. No? Beh, capita, te l’assicuro.
Comunque sia, fare una cosa del genere è semplicissimo. Guarda un po’:
1 $user = User::find(5);
2 Auth::login($user);
Niente male, direi! Tutto quello che devi fare è passare ad Auth::login() l’istanza del model.
Dove lo metto?
Nell’ultimo periodo io e Taylor abbiamo sentito, molto spesso, una domanda. Te la sarai posta pure
tu, qualche volta, o magari sei uno di quelli che ce l’ha fatta. “Dove metto xxx?” oppure “Dove
va messo xxx?”. In un certo senso ci siamo posti la stessa domanda durante la realizzazione di
Laravel. Tuttavia, alla fine, abbiamo pensato che nel mondo PHP (soprattutto con qualcosa basato
su Composer) si sarebbe manifestata la necessità di maggiore libertà. Nei limiti del possibile niente
più cartella dei model, o dei controller.
Non voglio confonderti però: facciamo una cosa alla volta. Chiudi i tuoi occhi e immagina.
Immagina di essere in una cucina. Dove andresti a prendere un piatto? Non è così scontata come
risposta, se ci pensi. Potremmo metterlo vicino al lavello, ad asciugarsi, o magari in un mobile. Ok,
ma quale mobile?
In conclusione si tratta di preferenze. Non si tratta di regole fisse, ma di quello che preferisci in base
alle tue abitudini. Se ci pensi, non posso decidere io dove mettere il piatto, perchè magari non lo
troveresti mai più.
Allo stesso modo, chi sono io per dirti dove mettere il tuo codice? Questo è il punto di tutto: non posso
dirti dove mettere il tuo codice, ma posso aiutarti nel prendere questa decisione. Posso mostrarti
come dare una forma al tuo progetto. Come ti sembra?
Esatto!
Bene, adesso pensa al codice di Laravel, per qualche momento. C’è una parte di codice procedurale
ed una parte, invece, di codice ad oggetti.
Calmo, pistolero. Che ti piaccia o meno c’è sempre (e ci sarà sempre) una piccola parte di codice
procedurale. Puoi anche smentirmi se vuoi: scrivi ed esegui un’applicazione PHP usando solo una
classe.
:(
Ecco, bravo. In PHP non c’è l’equivalente di public static void main(). C’è sempre bisogno di
un po’ di bootstrapping.
Dove lo metto? 375
1 <?php
2
3 $a = new MyApplication;
4 $a->init(); // Start our doom machine.
Questo è un classico esempio di bootstrapping fatto in PHP. Crei l’istanza dell’oggetto di tipo
MyApplication e via, il tuo bolide parte.
Laravel non è poi così differente. Prova a guardare tu stesso: apri il file public/index.php file.
Comincia tutto da qui.
1 <?php
2 /**
3 * Laravel - A PHP Framework For Web Artisans
4 *
5 * @package Laravel
6 * @author Taylor Otwell <[email protected]>
7 */
8
9 require __DIR__.'/../bootstrap/autoload.php';
10 $app = require_once __DIR__.'/../bootstrap/start.php';
11 $app->run();
Ho tolto alcuni commenti, ma l’idea dovrebbe essere chiara. Anche in questo caso quello che abbiamo
davanti è un po’ di codice procedurale, che avvia l’applicazione attraverso l’esecuzione di $app-
>run();.
Non è qualcosa che riguarda solo questo file, tra l’altro. Dai un’occhiata ad app/routes.php o
app/global/start.php. Codice procedurale.
Perchè?
Molte volte, in Laravel, i files che contengono codice procedurale sono usati per configurazioni o
per registrazioni di “cose”: observers dei modelli, routes, validatori personalizzati, eventi e così via.
La logica di questi componenti, invece, si trova all’interno di una classe. Non cambia, però, il fatto
che dobbiamo usare linee di codice come:
1 <?php
2
3 Validator::extend('foo', 'Me\Validators\Validate@validate');
Senza questa istruzione, infatti, Laravel dovrebbe girarsi un po’ tutte le cartelle del progetto per
trovare il file adatto. Non tanto flessibile e pratico come approccio, no? C’è bisogno di dare un
percorso ben preciso a tutti i files.
Tutto questo è libertà. Che si riflette anche sul concetto di personalità. Se parliamo di un progetto in
“solitaria”, perché il codice non dovrebbe poter esprimere quello che sei davvero? In caso di lavoro
in team, perchè non lasciare al gruppo la libertà di decidere tutte le convenzioni sulla posizione dei
vari files?
Ecco perché, per quanto riguarda i Validator, gli Observer e così via, Laravel non ha cartelle di
default.
Hai visto tu stesso che c’è una cartella app per loro. Questo perché stiamo parlando comunque di
un framework MVC e chiunque si aspetta una cartella del genere in un contesto simile. Da qualche
parte devi pur cominciare.
Anche in questo caso però non vogliamo forzarti: se proprio non ti piace la cartella app, prendi e
spacc(hett)a tutto!
Detto questo, vediamo i vari tipi di codice e come personalizzare il più possibile il tuo ambiente di
sviluppo.
Codice Procedurale
Quando pensiamo al codice procedurale, molto probabilmente, ci viene in mente il file app/rou-
tes.php. Magari ora che ci hai fatto caso una domanda ti passa per la testa… “da dove viene eseguito
il codice di routes.php?”.
Diamo un’occhiata al file vendor/laravel/framework/src/Illuminate/Foundation/start.php.
1 <?php
2
3 // Istruzioni di boostrapping varie...
4
5 /*
6 |--------------------------------------------------------------------------
7 | Load The Application Routes
8 |--------------------------------------------------------------------------
9 |
10 | The Application routes are kept separate from the application starting
11 | just to keep the file a little cleaner. We'll go ahead and load in
12 | all of the routes now and return the application to the callers.
Dove lo metto? 377
13 |
14 */
15
16 $routes = $app['path'].'/routes.php';
17
18 if (file_exists($routes)) require $routes;
19
20 });
Eccolo qui! Se il file delle routes esiste, questo viene caricato ed eseguito durante la fase di
boostrapping.
Nota: dato che il file in questione si trova in “vendor”, e non nella cartella della nostra applicazione,
sarebbe una cattiva idea aggiungerci delle istruzioni.
Andiamo un po’ in su e vediamo cosa c’è:
1 <?php
2
3 /*
4 |--------------------------------------------------------------------------
5 | Load The Application Start Script
6 |--------------------------------------------------------------------------
7 |
8 | The start script gives us the application the opportunity to override
9 | any of the existing IoC bindings, as well as register its own new
10 | bindings for things like repositories, etc. We'll load it here.
11 |
12 */
13
14 $path = $app['path'].'/start/global.php';
15
16 if (file_exists($path)) require $path;
Guardando il commento si capisce subito cosa succede: è proprio nel file di start che possiamo
effettuare i vari ovverride dei binding all’interno del container IoC.
Cosa significa? Semplice: che i vari componenti di Laravel, a questo punto dell’esecuzione, sono stati
caricati e che è possibile usarli.
Nulla ci vieta, quindi, di usare il caro vecchio metodo require() per includere altri file da
app/start/global.php, definendo la nostra struttura.
Come concetto potrebbe non essere così immediato: ecco un esempio pratico.
Dove lo metto? 378
1 <?php
2
3 ...
4
5 /*
6 |--------------------------------------------------------------------------
7 | Require The Filters File
8 |--------------------------------------------------------------------------
9 |
10 | Next we will load the filters file for the application. This gives us
11 | a nice separate location to store our route and application filter
12 | definitions instead of putting them all in the main routes file.
13 |
14 */
15
16 require app_path().'/filters.php';
17
18 // Create our own structure for files.
19
20 require app_path().'/observers.php'; // Model observers.
21 require app_path().'/validators.php'; // Custom validators.
22 require app_path().'/listeners.php'; // Event listeners.
23 require app_path().'/composers.php'; // View composers.
24 require app_path().'/services.php'; // Container bindings.
25 require app_path().'/helpers.php'; // Helper functions.
Non scordarti di creare questi file, altrimenti riceverai un errore. L’helper app_path() restituisce il
percorso della cartella /app. Quello che abbiamo appena fatto, nel nostro codice, è stato inserire vari
files, dividendo per ambiti i caricamenti da effettuare.
Gli observers in un posto, i validatori in un altro e così via: niente più mix immensi in routes.php.
Sia chiaro: ancora una volta, non prendere per “perfetta” la struttura appena proposta. Si tratta solo di
un consiglio, come sempre: la struttura perfetta è quella che si adatta perfettamente al tuo progetto.
Inoltre, come accennavo prima, puoi sempre liberarti della cartella /app. A dirtla tutta, molto spesso
nei miei progetti è quasi sempre vuota. L’unica eccezione sono i files di configurazione, che faccio
rimanere li.
Le mie routes, ad esempio, le metto sempre in /src/routes.php.
Lo accenno solo perchè un consiglio non è mai uno standard. Anzi, spesso le persone fanno proprio
questo errore: trasformano un consiglio in uno standard e questo standard diventa automaticamente
il modo di risolvere questo problema. Non è così che devono andare le cose: i problemi cambiano.
Anche le soluzioni.
Detto questo, ora parliamo del codice orientato agli oggetti.
Dove lo metto? 379
1 "autoload": {
2 "classmap": [
3 "app/commands",
4 "app/controllers",
5 "app/models",
6 "app/database/migrations",
7 "app/database/seeds",
8 "app/tests/TestCase.php"
9 ]
10 },
Abbiamo letto tutti il capitolo su Composer: sappiamo che tutte le classi nelle directory registrate
saranno prese e “convertite” in una grande mappa di tutti i nomi, aggiornata in base alle richieste
effettuate a Composer stesso.
È proprio così che Laravel trova i nostri files. Il problema è che molte persone non erano ancora
abituate a Laravel 4 quando è stato rilasciato. Sarà capitato a tante persone, infatti, di scordarsi il
comando composer dump alla creazione di una nuova classe.
Ti lascio immaginare quante volte composer dump sia stata la soluzione a problemi come “aiuto,
Laravel non trova la mia classe!”.
Per viziarti un po’ di più abbiamo pensato anche a questo: un secondo meccanismo di loading
all’interno del core. Apri nuovamente il file app/start/global.php:
1 <?php
2
3 /*
4 |--------------------------------------------------------------------------
5 | Register The Laravel Class Loader
6 |--------------------------------------------------------------------------
7 |
8 | In addition to using Composer, you may use the Laravel class loader to
9 | load your controllers and models. This is useful for keeping all of
10 | your classes in the "global" namespace without Composer updating.
11 |
12 */
Dove lo metto? 380
13
14 ClassLoader::addDirectories(array(
15
16 app_path().'/commands',
17 app_path().'/controllers',
18 app_path().'/models',
19 app_path().'/database/seeds',
20
21 ));
22 ?>
Ad ogni aggiunta di classi in queste cartelle non hai bisogno, infatti, di ripetere il comando composer
dump.
Il succo del discorso è questo: puoi usare il file composer.json oppure app/start/global.php.
Cool, vero? Mi piace pensare, tra l’altro, che il vero potere di Composer si scatena quando usi i
namespace che vuoi per le tue classi. Facciamo un passo avanti e configuriamo un nuovo meccanismo
di caricamento automatico in composer.json per la nostra applicazione.
1 "autoload": {
2 "classmap": [
3 "app/database/migrations",
4 "app/database/seeds"
5 ],
6 "psr-0": {
7 "Example": "src"
8 }
9 },
Attraverso il metodo psr-0 avrai bisogno, innanzitutto, di un namespace di base. Nell’esempio visto
qui sopra ho usato il namespace ‘Example’, che punta alle classi e directory situate all’interno della
cartella, appunto, src, relativamente alla root del progetto.
Chiaramente è consigliabile usare qualcosa di diverso dal nome Example. In molti usano il proprio
cognome.
Una volta avviato il comando composer update il meccanismo di autoloading è stato registrato:
adesso siamo liberi di lavorare sulla nostra nuova struttura come meglio crediamo, a patto di mettere
tutto in “src”.
Ecco un esempio di struttura dei vari files:
Dove lo metto? 381
1 src/Example/Controllers/UsersController.php
2 src/Example/Controllers/BooksController.php
3 src/Example/Models/User.php
4 src/Example/Models/Book.php
5 src/Example/Validators/UsersValidator.php
6 src/Example/Validators/BooksValidator.php
7 src/Example/Observers/BooksObserver.php
8 src/Example/SomethingDoer/Other/DoSomething.php
Bello, vero? Una struttura tutta fatta da noi, senza convenzioni o regole di sorta. L’unico limite è la
nostra esigenza.
Anzi, una regola (solo una) c’è: l’importante è che la struttura path/namespace combaci sempre.
Ecco cosa conterrà, ad esempio, il file src/Example/Validators/UsersValidator.php.
1 <?php
2
3 namespace Example\Validators;
4
5 class UsersValidator
6 {
7 // Your code here...
8 }
Tutto qui.
Quando usi i metodi di registrazione di Laravel, ricorda comunque che devi fornire il nome completo
anche del namespace:
1 Route::get('/', 'Example\Controllers\UsersController@showUsers');
Ah, una cosa importante. Potrebbe servirti. Anzi, ti servirà. Quando la tua classe è sotto un
namespace specifico, PHP assume che le altre eventuali classi che usi siano sotto quel namespace.
Guarda un po’ qui:
Dove lo metto? 382
1 <?php
2
3 namespace Example\Controllers;
4
5 class UsersController extends Controller
6 {
7 public function showUsers()
8 {
9 return 'all the users! \o/';
10 }
11 }
1 <?php
2
3 namespace Example\Controllers;
4
5 use Controller;
6
7 class UsersController extends Controller
8 {
9 public function showUsers()
10 {
11 return 'all the users! \o/';
12 }
13 }
Eh si! Un bel po’ di alias! Non pensavi ce ne fossero così tanti, vero? Guardando questa lista puoi
effettivamente capire quali sono le vere classi dietro la magia.
Quando scrivo i miei namespace e le mie classi mi piace specificare, in use, la vera classe. Credo sia
più descrittivo e più preciso, mi da sempre l’idea di quello che sto facendo. Come sempre, però, sono
sempre punti di vista.
Ecco un altro esempio:
1 <?php
2
3 namespace Example\Controllers;
4
5 use Illuminate\Routing\Controller;
6 use Illuminate\Support\Facades\Input;
7
8 class UsersController extends Controller
9 {
10 public function showUsers()
11 {
12 return Input::get('name');
13 }
14 }
Anche stavolta siamo arrivati alla fine del capitolo. Sono sicuro che ora hai le idee molto più chiare
e Laravel è sempre più dentro di te.
Ti lascio questo consiglio, per concludere: non copiare pari pari gli esempi che vedi in queste pagine.
Sperimenta, fai tua la tecnica mettendoci effettivamente del tuo. È l’unico modo per fare le cose
come si deve.
Per cui… la prossima volta che stai per chiedere…
Eccitante, vero? Arrivi alla magione Code Bright, con il tuo vestito della domenica e le bandiere
colorate tra le mani. Nella sala principale trovi migliaia di sviluppatori simpaticissimi. Non puoi fare
a meno di notare, sui muri, i meravigliosi dipinti ad olio dei vari panda che, durante le generazioni
passate, hanno servito la famiglia Rees.
Vengono serviti caviale e champagne. Il tutto, ovviamente, dagli elegantissimi panda fieri nei loro
tuxedo bianco e arancione.
Un grande evento, davvero.
Salgo sul palco: è arrivato il momento di salutare gli ospiti.
“Benvenuti a tutti, meravigliosi amici miei, al party annuale del Panda. Siete stati invitati per un
motivo molto semplice: l’ottimo gusto in termini di scrittura tecnica. Ora, so bene che questi eventi
pieni di small-talking possono essere noiosi per gli sviluppatori. Non vi preoccupate però: ho studiato
un sistema intelligente per gestire la cosa. Ognuno di voi, spero, ha portato le sue bandiere colorate.”
sorrisi
“Perfetto! Sono contento di vedervi così entusiasti. Vi spiego come funzionerà la cosa. Se volete
parlare del tempo, allora tenete in mano la bandiera rossa. In caso di sport, invece, alzate la bandiera
blu. Se preferite parlare di videogiochi, infine, usate la bandiera verde. Chiaro?
Bene. Con questo semplice sistema vi auguro di godervi la festa. Brindiamo a Laravel e a tutte le sue
future features!”
Concept
Ok, tutto molto bello, ma cosa c’entra con il capitolo? Semplice: storia raccontata a parte, è una
discreta metafora per spiegare la programmazione ad eventi. Il programmatore che alza una certa
Eventi 386
bandierina, infatti, equivale allo “scatenare” un determinato evento. Oppure a “farlo succedere”, se
rende meglio l’idea.
Vediamo un esempio. Alziamo la bandiera blu:
1 Event::fire('raise.blue.flag');
Altre persone possono aspettare che qualcuno alzi questa bandiera blu: ecco come si traduce in
codice il concetto.
1 Event::listen('raise.blue.flag', function()
2 {
3 return new SmallTalk::make('sports');
4 });
Con il nostro listener al posto giusto, nel momento in cui qualcuno alza la banidera blu automati-
camente comincia una conversazione sugli sport con un’altra persona. Un processo davvero molto
efficiente, se ci pensi, dato che posso registrare tutti i listener che voglio per qualsiasi evento: non
c’è un numero fisso di listener possibili.
Bene… detto questo, perché non prendere un esempio del genere in Laravel? Semplice: l’hai appena
visto. C’è solo una differenza però: la classe SmallTalk non esiste. Per il resto non c’è niente che non
ci sia già. Sono tutte cose che già esistono!
Ancora un po’ confuso? Tranquillo, ora vediamo tutto nel dettaglio.
1 Event::fire('my.event');
Automaticamente, tutti i listener associati all’evento verranno eseguiti nel momento in cui questo
viene scatenato.
Utile come meccanismo, vero? Non preoccuparti però: non ci si ferma mica qui. Possiamo fare un
sacco di altre cose con il sistema ad eventi. Ad esempio, volendo, possiamo “passare” informazioni
aggiuntive ai vari listener. In questo modo:
Eventi 387
Il secondo parametro, opzionale, identifica un array di variabili PHP che verranno poi “prese” dai
vari listener.
Nota: qui valgono le regole base del PHP. Gli oggetti vengono passati direttamente,
mentre i tipi base vengono passati come copia.
Cosa vuol dire? Semplicemente, che se nell’array dei parametri inserisci un determinato oggetto
potrai modificare tale oggetto all’interno dei listener.
Detto questo, passiamo a vedere i listener nel dettaglio.
Creare i listener
Creare un listener è molto semplice. Il metodo da usare è Event::listen(). Il primo parametro da
passare è la chiave scelta in precedenza, in fase di fire. Il secondo parametro, invece, è una closure
che specifica “cosa” fare.
Vediamo subito un esempio collegato a quello precedente.
1 Event::listen('my.event', function()
2 {
3 // Perform some action.
4 // Update the database?
5 });
Con le closure possiamo effettuare tutte le azioni di cui abbiamo bisogno. Potremmo scrivere dei
log, o magari aggiornare il database model. Puoi farci qualsiasi cosa… e come sempre l’unico limite
è la tua immaginazione!
Come già detto in precedenza, possiamo passare dei dati aggiuntivi ai vari listener. In questo modo:
I parametri vanno specificati nello stesso ordine in cui sono specificati all’interno dell’array del
metodo di fire. Ricordalo.
Tra l’altro ricordi sicuramente anche che puoi registrare più di un listener per un singolo evento.
Eventi 388
1 Event::listen('my.event', function()
2 {
3 // First listener.
4 });
5
6 Event::listen('my.event', function()
7 {
8 // Second listener.
9 });
10
11 Event::listen('my.event', function()
12 {
13 // Third listener.
14 });
15
16 Event::fire('my.event');
Nell’esempio appena visto abbiamo eseguito tutti e tre i listener specificati con un solo “fire”.
Cosa fare, inoltre, se fossi già soddisfatto dei risultati del primo listener e, di conseguenza, non volessi
procedere con il secondo ed il terzo?
No problem: basta far ritornare false al primo listener: quelli successivi non verranno calcolati.
1 Event::listen('my.event', function()
2 {
3 // First listener.
4 return false;
5 });
6
7 Event::listen('my.event', function()
8 {
9 // Second listener.
10 });
11
12 Event::listen('my.event', function()
13 {
14 // Third listener.
15 });
16
17 Event::fire('my.event');
Interessante, vero?
Eventi 389
Inoltre, so che posso sembrare ripetitivo, ma lo dico un’altra volta: ogni volta che c’è la possibilità di
usare una closure… puoi usare anche una classe. La sintassi, come sempre, è molto molto semplice.
Ecco un esempio di “Listener Class”:
1 class MyListener
2 {
3 public function process()
4 {
5 // Handle the event here.
6 }
7 }
Una volta creata la classe basta registrare il listener con una sintassi simile a quella usata per i
controller e le actions.
1 Event::listen('my.event', 'MyListener@process');
Se non indichi nessun metodo come secondo parametro, Laravel cerca automaticamente un metodo
handle() nella classe.
La cosa, però, non finisce qui. I listener, infatti, possono essere specificati anche con una certa priorità
nell’esecuzione. Un esempio:
1 Event::listen('my.event', function()
2 {
3 // First listener.
4 }, 1);
5
6 Event::listen('my.event', function()
7 {
8 // Second listener.
9 }, 3);
10
11 Event::listen('my.event', function()
12 {
13 // Third listener.
14 }, 5);
15
16 Event::fire('my.event');
Il terzo parametro del metodo listen() indica la priorità. Più questa è alta, prima viene eseguito
rispetto agli altri con priorità più bassa. Semplice e veloce!
Quando registri un evento, inoltre, non è detto che tu debba essere così… preciso, riguardo alla chiave
da usare. Laravel, infatti, supporta un sistema di wildcard (*) per i listener.
Eventi 390
1 Event::listen('my.*', function()
2 {
3 // ..
4 });
Questo listener è eseguito ogni singola volta che viene scatenato un evento la cui chiave comincia
per my..
Molto, molto comodo.
Subscribers (sottoscrittori)
Nel capitolo precedente ho condiviso alcuni consigli su “dove” registrare il tuo codice. Con gli eventi
si apre un nuovo scenario, in questo senso. Un subscriber, infatti, può essere usato per creare una
classe che gestisce, insieme, un certo insieme di eventi.
Vediamone un semplice esempio.
1 class MyListeners
2 {
3 public function firstListener()
4 {
5 // First event listener.
6 }
7
8 public function secondListener()
9 {
10 // Second event listener.
11 }
12
13 public function thirdEventListener()
14 {
15 // Third event listener.
16 }
17
18 public function subscribe($events)
19 {
20 $events->listen('first.event', 'MyListeners@firstListener');
21 $events->listen('second.event', 'MyListeners@secondListener');
22 $events->listen('third.event', 'MyListeners@thirdListener');
23 }
24 }
Eventi 391
Come puoi vedere, un subscriber è simile alla classe vista in precedenza, per il singolo evento.
Ha alcuni listener ed un metodo subscribe(). Questo metodo accetta una certa istanza di evento
chiamata, appunto, $events.
Ora, nell’esempio precedente abbiamo usato la classe Event per scatenare ed “ascoltare” gli eventi.
Bene, è praticamente la stessa cosa. Questa istanza può essere usata per registrare eventi e listeners
in un modo “diverso” da quello solito. Più strutturato, in un certo senso.
Una volta sistemata questa nuova struttura basta inizializzare il subscriber. Con il metodo subscri-
be(), chiaramente.
1 Event::subscribe(new MyListeners);
Anche in questo caso, insomma, massima libertà. La scelta rimane sempre tua, in base alle situazioni
e alle necessità.
Eventi globali
Vuoi che ti riveli un altro segreto? Rimane tra me e te però, eh. Non mi va di condividere troppo la
cosa. Devi sapere che possiamo spiare Laravel. Davvero!
Volendo, infatti, possiamo intercettare i suoi “messaggi”. Laravel, come puoi immaginare, ha i suoi
eventi. Un esempio? L’esecuzione di una query SQL. In questo caso l’evento ad essere scatenato è
illuminate.query (illuminate è il nome in codice di Laravel, ricordi?).
I parametri passati ad illuminate.query, ovviamente, riguardano la query stessa.
Un altro esempio è l’evento illuminate.log: suppongo tu possa immaginare a cosa può servire.
Ed ancora, ci sono un sacco di eventi che Taylor ha creato, in modo tale da fornirti una marea di
scorciatoie per gestire al meglio il tuo sistema.
Un’altra cosa molto interessante è il metodo App::after(), che viene eseguito alla fine di tutto il
ciclo della richiesta. Puoi passargli una closure in modo tale da fargli eseguire determinate operazioni
alla fine di tutto.
Nell’esempio qui in alto, ad esempio, abbiamo modificato l’oggetto $response in modo tale da
aggiungere l’header ‘Access-Control-Allow-Origin’.
Puoi ben immaginare, tra l’altro, che esiste anche App::before(), eseguito prima di tutto il resto
della richiesta. Non esiste il parametro $response ma solo $request.
Se dovessi averne bisogno, infine, molti degli stub di questi metodi li trovi all’interno del file
filters.php.
Eventi 392
Casi d’uso
Il miglior augurio che posso farti è quello di usare il sistema nel modo più creativo possibile per
risolvere i tuoi problemi. Ad ogni modo, riporto qui qualche piccolo caso d’uso che ho trovato
davvero molto utile.
• user.created
• user.deleted
• profile.updated
Le idee dietro questi eventi possono essere delle più svariate: potresti tenere traccia di eventuali
errori con un log, o magari potresti associare all’utente appena creato l’invio di una mail.
L’overhead per il fire di un evento in genere è decisamente piccolo, per cui non mi faccio problemi
a piazzarli ovunque possa averne bisogno.
Hooks
Immagina di dover realizzare un CMS, o magari un task management system. Gli eventi potrebbero
essere una core feature del tuo sistema. Offrire un’ottima struttura, facilmente estendibile, in questi
casi fa la differenza tra un buon prodotto e un pessimo prodotto. Ricordalo.
Inoltre, se estendi un po’ anche questo ragionamento, gli eventi sono un ottimo modo di estendere il
proprio sistema senza dover ricorrere alla modifica di un codice, magari, già testato e funzionante.
Anche con gli eventi è andata!
Dal prossimo capitolo le cose si faranno ancora più interessanti: entrerà in gioco, infatti, il principio
di inversion of control.
Il Container
Shauna Gordon sgattaiolò in silenzio nella Otwell Mansion. A passi felpati, protetta dalle ombre. Al
di fuori di ogni possibilità di essere vista. Shauna non era estranea a questo tipo di cose: non era
solo una sviluppatrice, ma anche una delle ladre di gatti più famose degli Stati Uniti. Quella sera,
tuttavia, non avrebbe rubato gatti. No, aveva in mente altro: molto di meglio.
Mentre pensava al suo premio con una certa soddisfazione rimuoveva con delicatezza il grimaldello
dalla porta. La porta esterna era protetta da sei serrature diverse: preludio degno di quello che
avrebbe trovato all’interno. Non sarebbero resistite, comunque: una ad una, le serrature cadevano
sotto la sua maestria.
Il suo tesoro: componenti delle precedenti versioni di Laravel, perle senza prezzo. Trofei che venivano
dalle battaglie più cruente e dalle cerimonie più sentite. A Shauna, però, non interessavano. Lei
puntava più in alto.
Poi lo vide. In tutto il suo splendore, assoluto e totale. Era davanti a lei e brillava, così come a lei
brillavano gli occhi.
Il cuore di Laravel: il Container! Il tesoro più prezioso che avrebbe potuto trovare nella magione.
Doveva sapere cosa c’era al suo interno: non poteva resistere. L’aveva aperto e ora stava guardando
al suo interno: una marea di bagliori avevano invaso il salone.
Bagliori che però avevano svegliato anche Taylor, che ora era appoggiato alla ringhiera del piano
superiore, con un fucile a pompa per braccio.
“Shauna!? Ancora tu? Fuori da casa mia! Non toccare ciò che non è tuo, clona il
repository se vuoi così tanto il container!”
“Dayle, hai già scritto abbastanza sul container nel capitolo dedicato all’architettura!””
Si, l’ho fatto. Vero. Pensi davvero, però, che sia tutto lì quello che c’è bisogno di sapere sul container?
Il misterioso cuore di Laravel ha un solo, singolo segreto da scoprire? No, dai.
Il container è la parte più importante dell’intero framework. Tiene tutto insieme: senza container
non esisterebbe Laravel nella sua ultima versione. Non ci sarebbe CodeBright e tutti sarebbero un
po’ più tristi.
Il Container 394
Nel capitolo dedicato all’architettura ho parlato di come l’oggetto $app nel cuore di Laravel estende
la classe Illuminate\Container\Container. Ho spiegato inoltre come memorizzare oggetti al suo
interno, usando una sintassi simile a quella usata per un array. Come una scatola magica, contiene
senza problemi tutto quello che tu vuoi che contenga. Con questi presupposti è normale averne
un’idea misteriosa e, a tratti, confusa.
È arrivato il momento di conoscerlo meglio.
Inversion of Control
Avrai sentito parlare del container anche come IoC Container. Per cosa sta questo “IoC”?
“Inversion of Control.””
Non fare il furbo, l’hai visto nel titolo. Comunque, direi che è tutto chiaro, no?
“No.””
Beh si, in effetti il termine Inversion Of Control sembra non spiegare praticamente niente.
Allora semplifichiamo: il termine IoC, essenzialmente, significa “permettere al framework di
costruire delle cose al posto nostro”.
Daremo al framework la possibilità di creare degli oggetti o dei “valori” al posto nostro. Stiamo,
appunto, trasferendo (o invertendo) il controllo sulla creazione di istanze da noi al framework. Gli
diamo fiducia.
Qualche capitolo indietro ti ho spiegato come usare $app alla stregua di un array, in modo tale da
fargli tenere delle “cose”. Bene: in Laravel possiamo usare anche la Facade ‘App’, disponibile in ogni
singolo “luogo” nel contesto del framework. Il suo alias è infatti presente nel root namespace, in
modo tale da poter essere usato ovunque.
Possiamo usare tale Facade per memorizzare (o prelevare) queste “cose” dal container, condividen-
dole tra le diverse classi che costituiscono la nostra applicazione.
Facciamo subito un esempio senza Facade.
Il Container 395
1 <?php
2
3 // app/routes.php
4
5 // Otteniamo l'istanza dell'oggetto $app.
6 $app = app();
7
8 // Binding dell'intero 3 nel container.
9 $app['three'] = 3;
10
11 // Output della risposta.
12 echo $app['three'];
Funziona alla grande e senza problemi. Ottenere il valore 3 precedentemente memorizzato è una
cosa da un secondo.
In realtà, comunque, non stiamo di certo lavorando con un array: c’è qualcosa di molto più sofisticato
che non vedi. Facciamo un esempio più complesso: l’istanza di una libreria immaginaria per l’invio
di SMS. Potresti aver voglia di conservare i parametri di configurazione in un file apposito ma, nel
contesto di questo esempio, lasciamo le cose semplici.
1 <?php
2
3 // app/routes.php
4
5 $app = app();
6
7 // Creiamo il componente SMS.
8 $sms = new SMSClient;
9 $sms->setUsername('shauna');
10 $sms->setPassword('1_5t341_c4t5!');
11
12 // Bind del componente.
13 $app['sms'] = $sms;
14
15 // Invio dell'SMS.
16 $app['sms']->send('+55121212153', 'Yo dawg.');
Abbiamo sistemato l’istanza del client in corrispondenza dell’elemento sms dell’array $app. Tuttavia
non è così.
Il container di Laravel infatti implementa l’interfaccia ArrayAccess, che permette di simulare alcune
proprietà di un array. Una di queste è, appunto, l’accesso ai suoi valori via indice.
Il Container 396
Andiamo quindi a guardare da vicino il sorgente del container. Si, è una classe molto complicata,
ma ti assicuro che comprenderla sarà utilissimo.
1 <?php
2
3 // Container.php
4
5 /**
6 * Get the value at a given offset.
7 *
8 * @param string $key
9 * @return mixed
10 */
11 public function offsetGet($key)
12 {
13 return $this->make($key);
14 }
15
16 /**
17 * Set the value at a given offset.
18 *
19 * @param string $key
20 * @param mixed $value
21 * @return void
22 */
23 public function offsetSet($key, $value)
24 {
25 // If the value is not a Closure, we will make it one. This simply gives
26 // more "drop-in" replacement functionality for the Pimple which this
27 // container's simplest functions are base modeled and built after.
28 if ( ! $value instanceof Closure)
29 {
30 $value = function() use ($value)
31 {
32 return $value;
33 };
34 }
35
36 $this->bind($key, $value);
37 }
Il Container 397
1 <?php
2
3 // Container.php
4
5 return $this->make($key);
Il container ci sta dicendo: “se provi ad accedere a qualcosa come se stessi usando un array, usa il
metodo make() per ottenere ciò di cui hai bisogno.”
Il metodo make può essere usato per costruire oggetti (e valori, in generale) da ciò che è memorizzato
nel container. Fa tutta una serie di operazioni “intelligenti”, che guarderemo meglio nella prossima
sezione.
Passiamo ad offsetSet().
1 <?php
2
3 // Container.php
4
5 if ( ! $value instanceof Closure)
6 {
7 $value = function() use ($value)
8 {
9 return $value;
10 };
11 }
12
13 $this->bind($key, $value);
Quando proviamo ad impostare un valore all’interno del container usando una sintassi array-like,
innanzitutto controlliamo se il valore “proposto” non è una closure. Nel caso sia una closure allora
impostiamo il valore desiderato come quello ritornato dalla closure in questione.
Come lo facciamo? Beh, con il metodo bind(), che prende una closure come parametro.
Riguardiamo l’esempio precedente step by step.
Il Container 398
1 <?php
2
3 // app/routes.php
4
5 // Bind del componente.
6 $app['sms'] = $sms;
… diventa …
1 <?php
2
3 // app/routes.php
4
5 $app['sms'] = function () use ($sms) {
6 return $sms;
7 };
1 <?php
2
3 // app/routes.php
4
5 $sms = $app['sms'];
… sapremo che verrà eseguita una closure ed un certo meccanismo di base si occuperà di ritornare
l’istanza desiderata.
Ok, fermi un secondo. Adesso direi di lasciar perdere l’oggetto $app. Perché non usare la Facade? Con
la Facade non possiamo sfruttare i benefici di ArrayAccess direttamente: dobbiamo usare i metodi
bind() e make().
Sappiamo che bind() riceve una closure. Memorizziamo un intero, come nel primo esempio.
Il Container 399
1 <?php
2
3 // app/routes.php
4
5 // Bind di un intero nel container.
6 App::bind('three', function () {
7 return 3;
8 });
9
10 // Recupera l'intero dal container.
11 echo App::make('three');
Non stiamo facendo niente di nuovo: abbiamo solo coperto la vecchia sintassi con una più leggibile
e pulita. Il metodo bind() prendere come primo parametro l’indice desiderato e come secondo
parametro la closure che ritorna l’intero.
Possiamo anche usare, come già detto, il metodo make(), fornendo l’indice che abbiamo usato in
precedenza.
Ecco un esempio simile, usando però il componente SMS visto prima.
1 <?php
2
3 // app/routes.php
4
5 // Crea il componente.
6 $sms = new SMSClient;
7 $sms->setUsername('shauna');
8 $sms->setPassword('1_5t341_c4t5!');
9
10 // Bind del componente.
11 App::bind('sms', function () use ($sms) {
12 return $sms;
13 });
14
15 // Manda un SMS.
16 App::make('sms')->send('+55121212153', 'Yo dawg.');
Prima di tutto creiamo il componente. Dopodiché si provvede al suo bind, usando una stringa come
indice ed una closure per ottenerne l’istanza. A quel punto possiamo “risovlere” tale valore dal
container in qualsiasi momento vogliamo, usando l’indice apposito.
C’è un problema, tuttavia. L’hai notato? Tranquillo, può essere difficile farci caso la prima volta.
Il Container 400
Abbiamo creato la nuova istanza, effettuandone un bind con il container. Cosa fare, però, se non
volessimo usarla in ogni singola richiesta? È probabile infatti che ne avremo bisogno poche volte,
rispetto al numero totale di chiamate alla nostra applicazione. Un bello spreco di risorse!
Proprio qui le Closure diventano utili. La closure “nel” nostro container viene eseguita solo se
effettivamente provi a risolverla (a chiamarla) dal container. Pensa come una sorta di “stampo”
per creare il valore di cui hai bisogno.
Perché, quindi, non spostare l’intera creazione del componente all’interno della closure? Vediamo
come implementare tale soluzione.
1 <?php
2
3 // app/routes.php
4
5 // Bind del componente.
6 App::bind('sms', function () {
7
8 // Creazione del componente.
9 $sms = new SMSClient;
10 $sms->setUsername('shauna');
11 $sms->setPassword('1_5t341_c4t5!');
12
13 // Ritorno il componente creato.
14 return $sms;
15 });
16
17 // Mando un sms.
18 App::make('sms')->send('+55121212153', 'Yo dawg.');
Ci siamo: non sembra niente di che, vero? In realtà, in questo momento, abbiamo appena invertito
il controllo nella nostra applicazione. Abbiamo dato al framework il potere di creare il nostro
componente SOLO in caso di bisogno. Un bel cambiamento. Tutta la procedura di creazione
dell’istanza ora è delegata a App::make('sms').
Ora Inversion of Control non è più spaventoso come termine. Vero? Forse si sarebbe dovuto
chiamare: “Permette Al Framework Di Creare Cose Solo Quando Ne Hai Bisogno”, ma “Container
PAFDCCSQNHB” non sarebbe stato un granché elegante come nome.
Comunque, c’è solo un’ultima cosa da sistemare riguardo il nostro componente.
Riguarda il codice di seguito. Vedi nulla di strano?
Il Container 401
1 <?php
2
3 // app/routes.php
4
5 // Bind del componente.
6 App::bind('sms', function () {
7
8 // Creazione del componente.
9 $sms = new SMSClient;
10 $sms->setUsername('shauna');
11 $sms->setPassword('1_5t341_c4t5!');
12
13 // Return dell'istanza.
14 return $sms;
15 });
16
17 // Invio di un SMS.
18 App::make('sms')->send('+55121212153', 'Yo doge.');
19
20 // Invio di un altro SMS.
21 App::make('sms')->send('+55121223432', 'SUCH TEXT. WOW');
Abbiamo aggiunto una seconda linea, per inviare un altro SMS. Non dovrebbe essere un problema,
vero? Il codice funziona, se lo provi: tuttavia non è molto efficiente. Ripensiamo al flow di esecuzione
ancora una volta.
Abbiamo creato un blueprint per la creazione di un oggetto. Risolviamo il componente, mandiamo
l’sms, lo risolviamo nuovamente e inviamo anche il secondo componente.
Esatto, forse l’hai capito anche tu: stiamo risolvendo lo stesso componente per due volte! Vuol dire
che, appunto, istanzieremo per due volte la stessa cosa. Imposteremo le credenziali di accesso per
due volte e così via. Tutto a doppio.
Ora, perchè mai dovremmo sprecare in questo modo i cicli del nostro caro processore?
Sicuramente il buon Taylor avrà pensato a qualcosa per aggirare il problema… ed in effetti è così.
Potremmo usare il metodo singleton() del container: permetterebbe di “memorizzare” il risultato
della Closure (eseguendola una sola volta), in modo tale da evitare ripetizioni inutili.
Vediamo come può essere usato.
Il Container 402
1 <?php
2
3 // app/routes.php
4
5 // Bind del componente come singleton.
6 App::singleton('sms', function () {
7
8 // Creo l'istanza del componente.
9 $sms = new SMSClient;
10 $sms->setUsername('shauna');
11 $sms->setPassword('1_5t341_c4t5!');
12
13 // Return dell'istanza.
14 return $sms;
15 });
16
17 // Mando un SMS.
18 App::make('sms')->send('+55121212153', 'Yo doge.');
19
20 // Mando un secondo SMS.
21 App::make('sms')->send('+55121223432', 'SUCH TEXT. WOW');
Adesso il nostro codice sarà molto più efficiente. Ed è bastato un semplice scambio tra bind() con
singleton(). Che, tra l’altro, hanno la stessa sintassi.
Probabilmente avrai anche sentito parlare del pattern Singleton. Il suo “compito” è assicurarsi che ci
sia solo ed una sola istanza di una determinata classe per volta, e non ne permette la duplicazione.
Quello visto nell’esempio non è un vero Singleton al 100%, ma il concetto non cambia di una virgola.
La Closure verrà “risolta” una ed una sola volta. Quella che otterrai in output sarà la stessa istanza
del componente, sempre.
Cosa? Non ti fidi? Essere umano di poca fede… Va bene, ora ti dimostrerò tutto.
Torniamo ai dare numeri: guarda il seguente snippet.
Il Container 403
1 <?php
2
3 // app/routes.php
4
5 App::bind('rand', function () {
6 return rand();
7 });
8
9 var_dump(App::make('rand'));
10 var_dump(App::make('rand'));
11 var_dump(App::make('rand'));
1 int 1742870120
2 int 385800925
3 int 1573429337
Decisamente random, eh? Grande. Bene, adesso cambia la sintassi esattamente come abbiamo fatto
prima. Al posto di bind() scrivi singleton().
1 <?php
2
3 // app/routes.php
4
5 App::singleton('rand', function () {
6 return rand();
7 });
8
9 var_dump(App::make('rand'));
10 var_dump(App::make('rand'));
11 var_dump(App::make('rand'));
1 int 903162162
2 int 903162162
3 int 903162162
Visto? La Closure viene eseguita una sola volta. L’integer 903162162 è quello ottenuto dalla chiamata
a rand(). Dopo la prima chiamata il risultato viene memorizzato e restituito anche in occasione delle
chiamate successive.
Il Container 404
Sarà compito nostro, quindi, decidere quando usare tale feature a nostro vantaggio e quando invece
lavorare con un bind classico. A volte, infatti, potremmo aver bisogno di una risorsa “fresca” ad ogni
nuova chiamata: si rivelerebbe quindi controproducente l’uso del Singleton.
Bene, direi che abbiamo visto tutto del container.
No, ovviamente scherzo. Anzi, ad essere sinceri stiamo per addentrarci in una delle features più
interessanti di Laravel!
Dependency Injection
Lo so, sembra un altro termine spaventoso. Soprattutto se hai appena cominciato con la programma-
zione orientata agli oggetti, non ti dico. In ogni caso puoi stare tranquillo: la dependency injection
è una tecnica molto utile. Lo riconoscerai anche tu a fine capitolo. Promesso!
Una volta ho letto una frase, sulla dependency injection, che mi è piaciuta un sacco. Non ricordo chi
l’abbia detta, comunque eccola qua:
Fantastico, perché è decisamente vero. “Dependency Injection” sembra qualcosa di assurdo, tuttavia
in realtà è qualcosa di molto semplice.
Cominciamo in piccolo, dando un’occhiata ad un esempio di risoluzione di classe tramite il container.
1 <?php
2
3 // app/routes.php
4
5 $collection = App::make('Illuminate\Support\Collection');
Questo codice, come lo vedi, non è un granché utile. Istanzia un nuovo oggetto di classe Il-
luminate\Support\Collection. Eppure, non ricordo neanche di aver effettuato il bind di tale
classe.
Non ne hai bisogno: il container è intelligente ed è capace di risolvere una chiave che non esiste, se
corrisponde ad una classe il cui nome (ed eventuale namespace) è giusto.
Diamo uno sguardo al volo alla sintassi del costruttore di tale classe.
Il Container 405
1 <?php
2
3 // Collection.php
4
5 public function __construct(array $items = array())
Avrai notato che puoi passare un array di oggetti al costruttore, in modo tale da impostare il set
iniziale di contenuti della collezione. Viene specificato anche un valore di default (array vuoto). Di
conseguenza avviene in automatico e viene usato un array vuoto.
Vediamo, adesso, che succede usando una classe che non ha un valore di default per uno dei
parametri passati nel costruttore.
1 <?php
2
3 // app/routes.php
4
5 // An example class, injecting a 'something' instance.
6 class Example
7 {
8 protected $something;
9
10 public function __construct($something)
11 {
12 $this->something = $something;
13 }
14 }
15
16 // Tentativo di risoluzione della classe Example.
17 $example = App::make('Example');
Abbiamo una classe Example che prende in input tramite il costruttore un’istanza $something. Quello
che stiamo facendo è iniettare tale dipendenza nella classe. La stiamo “sistemando”. Tutto qui, ecco
cos’è la Dependency Injection.
Sfortunatamente, nel nostro caso il container non ha la più pallida idea di cosa sia questo
$something. Infatti, eseguendo il codice in questione otterremo un errore. Un’eccezione di tipo Il-
luminate\Container\BindingResolutionException con un messaggio ‘Unresolvable dependency
resolving [Parameter #0 [
Come aiutare il container a capire cosa usare? Innanzitutto, la cosa migliore da fare sarebbe
“descrivere” (o fare type-hinting) in qualche modo cosa stiamo iniettando. Per farlo dobbiamo
specificare il tipo del parametro passato. Implementiamo la classe Something come segue.
Il Container 406
1 <?php
2
3 // app/routes.php
4
5 // Classe Something.
6 class Something
7 {
8 // ...
9 }
10
11 // La classe Example.
12 class Example
13 {
14 protected $something;
15
16 public function __construct(Something $something)
17 {
18 $this->something = $something;
19 }
20 }
21
22 // Risoluzione di "Example".
23 $example = App::make('Example');
Stavolta tutto funziona senza problemi. La classe Example è stata istanziata correttamente. Vediamo
cosa è successo.
Il container da un’occhiata ai parametri richiesti, e nota che la classe Example ha bisogno di
un’istanza della classe “Something” come parametro del costruttore. Dato che esiste una definizione
di tale classe, il container la istanzia e la passa ad Example. Semplice e veloce: Laravel gestisce per noi
la gerarchia delle classi. La cosa ovviamente può essere gestita al livello che si preferisce: guardiamo
insieme un esempio sovracomplicato.
1 <?php
2
3 // app/routes.php
4
5 class First
6 {
7 public function __construct(Second $second) {}
8 }
9
10 class Second
Il Container 407
11 {
12 public function __construct(Third $third, Fourth $fourth) {}
13 }
14
15 class Third
16 {
17 public function __construct(
18 Fourth $fourth,
19 Illuminate\Container\Container $container
20 ) {}
21 }
22
23 class Fourth
24 {
25 public function __construct(array $myArray = array()) {}
26 }
27
28 $first = App::make('First');
Stavolta la gerarchia scende fino al quarto livello: per Laravel non c’è problema e, soprattutto, non
c’è differenza.
La parte migliore di questo sistema è che molte delle classi usate dal framework sono già tutte risolte
in questo modo.
Ad esempio, ovunque tu usi una sintassi come questa:
1 Some\Namespaced\Class@andMethod
… beh, la classe viene risolta dal container automaticamente. Tutte le dipendenze, e dipendenze di
dipendenze (e così via), vengono risolte sempre automaticamente. Controller, filtri, composer e così
via.
Figo, eh?
Nel prossimo capitolo daremo uno sguardo a come creare dei service provider per librerie di terze
parti.
In Arrivo
Ehi, ma dov’è il resto del libro?
Come già spiegato nella descrizione del libro, ho deciso di pubblicare Code Bright progressivamente,
mano a mano che lo scrivo. Se stai leggendo questa pagina significa che, per un motivo o per un
altro, non è ancora finito.
Non ti preoccupare, però: ho intenzione di continuare a scrivere gli altri capitoli e sistemare questa
o quella cosa in base anche al feedback che mi arriverà.
Chiaramente, ogni update futuro per te sarà gratuito, per cui non hai niente da temere: Leanpub ti
avviserà via email non appena ci sarà nuova carne al fuoco!
Vorrei inoltre usare questa pagina per ringraziare tutti quelli che mi hanno supportato in questo
periodo. Mi sono divertito un sacco a scrivere questi due libri (CodeHappy e CodeBright) e grazie
per tutto il feedback che mi è già arrivato, oltre che per quello che mi deve ancora arrivare.
Se CodeHappy e CodeBright ti sono stati utili, condividi con i tuoi amici e colleghi il link al libro:
http://leanpub.com/codebright²², te ne sarei davvero riconoscente.
Attualmente, il mio piano per il libro è il seguente: quindi ecco cosa ti aspetta prossimamente.
• Descrizione dei componenti e concept del framework; - Scrivere un capitolo dedicato alla
costruzione di una semplice applicazione, partendo da quello che abbiamo imparato; -
Esplorazione delle features più avanzate del framework; - Capitoli dedicati alla costruzioni di
applicazioni più avanzate, forti dell’esperienza maturata grazie alla conoscenza delle features
avanzate; - Best practices e trucchi vari;
Sto inoltre pensando alla realizzazione di un capitolo FAQ, basato sul feedback più frequente. Ragion
per cui, nel caso dovessi avere qualche domanda da fare… falla!
Sono qui per questo, no?
Come già detto all’inizio, comunque, se volessi parlare con me per qualsiasi motivo e contattarmi,
puoi farlo via mail all’indirizzo [email protected]²³ oppure su twitter (daylerees on Twitter²⁴).
Spesso, inoltre, mi troverai in giro per il canale #laravel su Freenode IRC.
Detto questo, grazie ancora per essere parte di Code Bright.
Con affetto,
Dayle.
²²http://leanpub.com/codebright
²³mailto:[email protected]
²⁴http://twitter.com/daylerees