Fino a pochi minuti fa, per rinfrescarmi un po’ le idee, stavo riguardando alcuni video su Laracasts dedicati ai Design Pattern.
Nello specifico, il video in questione riguardava l’Observer Design Pattern, uno dei più semplici da imparare ed al tempo stesso uno dei più famosi.
Ad un certo punto, il buon Jeffrey Way sforna uno dei suoi ottimi consigli (mi chiedo se gli sia mai capitato di darne di sbagliati): per evitare la duplicazione di codice, usare i trait è sicuramente una buona idea.
Devo ammetterlo: non li ho mai usati più di tanto, se non quasi mai. Così mi sono chiesto: perché non approfondire l’argomento?
Qualche ricerca mi ha portato a leggere svariati articoli, alcuni dei quali (come questo) cercano di dare un’idea complessiva, includendo anche i pro ed i contro.
La cosa molto interessante è proprio il dibattito che si è costruito intorno a questo costrutto, dato che ha coinvolto svariate “scuole di pensiero” riguardo l’ereditarietà multipla, best e bad practice, e così via.
Per cui eccomi qui, a scrivere un articolo per fare un po’ il punto della situazione.
Definizioni, innanzitutto
Cos’è un Trait? Chiediamolo direttamente al manuale di PHP:
Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.
ovvero:
I Trait sono un meccanismo per il riuso del codice in linguaggi ad ereditarietà singola, come PHP. L’obiettivo di un Trait è quello di ridurre alcune delle limitazioni dovute a questa tipologia di ereditarietà, permettendo di definire un determinato set di metodi da riusare in più classi che non sono necessariamente nella stessa gerarchia.
In poche parole: i Trait permettono di definire un insieme di metodi in un’entità a parte. Tale entità può essere quindi “condivisa” da altre classi che non necessariamente hanno lo stesso “genitore” o, comunque, sono nella stessa gerarchia.
Approfondiamo il discorso vedendo un veloce esempio per capire meglio di cosa si tratta.
Supponiamo di avere un insieme di entità (che non si trovano nella stessa gerarchia) che condividono una funzionalità di condivisione. Un po’ come avviene su Facebook: ho testi, foto, video e voglio poterli condividere, tramite un’apposita funzione share().
Un’idea è quella di implementare separatamente i vari metodi:
class Post {
public function share($item)
{
return ‘share this item’;
}
}
class Comment {
public function share($item)
{
return ‘share this item’;
}
}
Tuttavia, la funzione di condivisione è praticamente uguale per ogni singolo item… che senso ha duplicarla N volte tante quante sono le entità in gioco?
Ecco che entrano così in scena i trait:
trait Sharable {
public function share($item)
{
return ‘share this item’;
}
}
Esattamente come per una classe astratta, un trait non può essere istanziato, ma viene “incluso” tramite use nella classe che vuole fare uso dei suoi metodi.
Le due classi appena viste, quindi, diventeranno:
class Post {
use Sharable;
}
class Comment {
use Sharable;
}
Provando ad usare le due classi, adesso, il risultato sarà esattamente lo stesso. Abbiamo quindi scritto lo stesso metodo una volta sola, minimizzando la duplicazione di codice.
Sicuramente una bella comodità.
C’è anche da sottolineare un’altra cosa molto importante:
un trait è diverso da un’interfaccia, perché l’interfaccia definisce un “contratto” a cui una classe deve aderire. Il trait invece dona una determinata funzionalità ad una classe;
un trait è diverso da una classe astratta, perché esula dai meccanismi dell’ereditarietà. Non c’è bisogno di estendere una classe, basta includere il trait tramite use;
Detta così sembra quasi una manna dal cielo: la risoluzione di tutti i problemi e la promessa di una felicità eterna. Tuttavia…
… non è così per tutti!
No, non lo è.
Alcuni programmatori, infatti, per svariati motivi, hanno deciso di non sostenere l’uso di Trait nei propri progetti.
Uno di questi motivi, sicuramente anche uno dei più validi, è che il Trait rischia facilmente di essere usato male, e di diventare un’altra grande “zozzata”.
Non è un mistero che nella storia di PHP ci sia tanto codice scritto male, che nel corso del tempo ha contribuito in buona parte a “sminuire” il valore stesso del linguaggio.
Inoltre, basta pochissimo ad usare male i trait e creare classi pesanti che infrangono il Single Responsibility Principle. Quindi, massima attenzione.
Quindi? Li uso o non li uso?
Un buon punto di partenza, secondo me, è vedere qualche esempio di “buon uso” dei trait in un’applicazione vera.
Uno dei package consigliati per “esplorare” questo concetto è sicuramente Cashier, di Laravel. Nello specifico, puoi vedere un trait in azione in questo specifico file.
Nelle ultime ore sto giocando molto con Digital Ocean. Lo trovo un servizio fantastico e ci sto quasi facendo un pensierino per Laravel-Italia.
Ho dato uno sguardo alla guida presente sul sito, dedicata al come installare il nostro framework preferito… e ho deciso di farne una veloce traduzione.
Nello specifico, in questo articolo vedremo come installare Laravel su un webserver Nginx, su Ubuntu 14.04.
Installiamo tutto il necessario
La prima cosa da fare, per poter lavorare con Laravel, è installare tutto lo stack di software necessario. Possiamo farlo senza problemi tramite i default repository di Ubuntu.
Eseguiamo l’update dell’indice, in modo tale da avere una lista aggiornata di tutti i package disponibili. Dopodiché procediamo con l’installazione del software necessario.
Con queste due semplici istruzioni, in pochi minuti, ci troveremo Nginx, insieme al PHP e tutti i tool necessari ad eseguire un’applicazione creata con Laravel.
Nota: installeremo anche Git, che verrà usato da Composer per scaricare le varie dipendenze.
Modifica del file php.ini
Abbiamo tutti i componenti necessari: passiamo a sistemare i file di configurazione. Cominciamo con PHP. Roba di pochi secondi.
Apriamo il file di configurazione principale di PHP-fpm, usato da Nginx.
sudo nano /etc/php5/fpm/php.ini
Avremo bisogno di modificare un solo, singolo valore di configurazione. Cerca, precisamente, il parametro cgi.fix_pathinfo. Impostalo su 0, come segue:
cgi.fix_pathinfo=0
A cosa serve? In poche parole spiega a PHP di non provare ad eseguire uno script nel caso il file in questione non venga trovato. È una cosa molto importante a livello di sicurezza: permette di evitare alcuni tipi di attacchi che consentono ad un malintenzionato di eseguire codice tramite una richiesta costruita ad-hoc.
Sistemato il file, salvalo e chiudilo.
Rimane solo da abilitare, tramite esplicita richiesta, l’estensione MCrypt, dalla quale Laravel dipende. Basta molto poco:
sudo php5enmod mcrypt
Ci siamo: riavvia il servizio php5-fpm con il seguente comando:
sudo service php5-fpm restart
Bene: PHP è configurato. Passiamo ad Nginx.
Configurare Nginx e la Web Root
Adesso tocca configurare il web server. L’operazione si svolgerà seguendo due step specifici e distinti.
Il primo consisterà nella configurazione della document root e della struttura delle directory da usare per i file di Laravel. I nostri file verranno infatti messi in una cartella /var/www/laravel.
Al momento attuale, l’unica cartella ad essere già presente è /var. Iniziamo quindi con il creare la struttura desiderata.
sudo mkdir -p /var/www/laravel
Nota: il flag -p serve a creare tutta la struttura in una volta sola.
Adesso che abbiamo un luogo “fisico” per i nostri file, possiamo passare alla modifica del file di configurazione di Nginx. Nello specifico, modificheremo la parte relativa ai server block.
Apriamo il file di configurazione dei siti disponibili:
sudo nano /etc/nginx/sites-available/default
Subito dopo l’installazione, il file sarà pieno di sezioni commentate. Tuttavia, la struttura basilare dovrebbe essere questa:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
}
La prima cosa da fare è cambiare la document root da usare. Laravel è stato installato in var/www/laravel. Agiamo di conseguenza.
Ricordiamo, comunque, che la directory esposta dal server non sarà quella principale, ma la subdirectory /public.
Quindi, modifichiamo così il file.
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/laravel/public;
index index.php index.html index.htm;
server_name localhost;
location / {
try_files $uri $uri/ =404;
}
}
Occorre, adesso, impostare la direttiva server_name che indica l’attuale nome di dominio del server. Se non ne hai uno, usa l’indirizzo IP che ti è stato assegnato.
Dobbiamo inoltre modificare come Nginx gestisce le richieste. L’elemento da modificare è la direttiva try_files.
L’idea di fondo è semplice: ogni richiesta deve essere gestita come se si stesse cercando un file. Se il file non viene trovato, allora si passa a gestire la richiesta come se fosse un parametro query per index.php.
La procedura può essere rappresentata così:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/laravel/public;
index index.php index.html index.htm;
Infine, dobbiamo procedere con la creazione di un blocco ulteriore, che gestisca l’esecuzione di ogni singolo file PHP. Cercherà di eseguire il file, oppure gestirà la richiesta come parametro query per il file index.php.
Impostiamo le varie direttive fastcgi_* in modo tale che i percorsi vengano riconosciuti a dovere.
Infine, imposteremo il parametro SCRIPT_FILENAME in modo tale da permettere a PHP di rintracciare i file in modo corretto.
A lavoro ultimato, il file di configurazione dovrebbe apparirti così:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /var/www/laravel/public;
index index.php index.html index.htm;
Al momento in cui scriviamo la versione di Laravel è la 4.*.
Nota di Francesco: Anche con Laravel 5 non ci dovrebbero essere troppe difficoltà. In caso di sostanziali cambiamenti scriverò un altro articolo.
A questo punto, i file saranno stati scaricati nella cartella che abbiamo specificato. Tuttavia, sono ancora “posseduti” dall’account root. Bisogna quindi modificare alcuni permessi e ownership, in modo tale da poter servire i vari contenuti senza problemi.
Iniziamo il nuovo anno con una chicca piuttosto interessante.
Qualche giorno fa, infatti, sono stato contattato da Alessandro di WebYourMind, un ragazzo appassionato di sviluppo (e di Laravel) per una segnalazione che farà piacere a molti di voi.
Su YouTube è infatti disponibile, già da qualche mese, una serie di video tutorial dedicati al nostro framework preferito. Il progetto è ancora in fase di sviluppo, e ci sono svariate lezioni da aggiungere a quelle già presenti.
Non temete però: i realizzatori ci hanno promesso che verrà finita.
Nel momento in cui scrivo, la serie copre solo le basi del framework. Sono presenti, in tutto, quattro capitoli sui dieci promessi.
È vero, tra poco sta per essere rilasciato Laravel 5, ma sono sicuro può essere comunque molto interessante mettere le mani in pasta con una guida del genere.
Spesso, nella nostra applicazione, abbiamo bisogno di tracciare e notificare le attività degli utenti: ad esempio chi ha effettuato il login, chi ha inserito un determinato contenuto, chi ha modificato qualcosa. Sarebbe comodo memorizzare queste azioni registrando anche “quando” sono avvenute, insieme ad altre informazioni.
Per venire incontro a questa esigenza ti presento ActivityLog, un package che ti permette di registrare con facilità i log per le diverse azioni degli utenti nel database e visualizzarle ad esempio nella dashboard dell’amministratore.
Installazione
Installazione, registrazione del service provider e aliasing
Per installare ActivityLog assicurati di aggiungere all’interno del composer.json la riga regulus/activity-log tra i require così:
“require”: {
“regulus/activity-log”: “0.3.0”
},
Esegui quindi da riga di comando l’istruzione composer update.
Composer installerà il package ActivityLog. Non ti rimane che registrare il service provider nel file app/config/app.php, nell’ array providers:
‘RegulusActivityLogActivityLogServiceProvider’,
Aggiungi questa riga nell’array aliases:
‘Activity’ => ‘RegulusActivityLogActivity’,
Migration e Seed del Database
Per eseguire la migration (una singola tabella DB), esegui il comando artisan:
php artisan migrate –package=regulus/activity-log
Pubblicazione del File di Configurazione
Se vuoi personalizzare la configurazione di ActivityLog, è necessario pubblicare il file di configurazione. Esegui dalla riga di comando:
php artisan config:publish regulus/activity-log
Ora potrai modificare il file di configurazione in app/config/packages/regulus/activity-log.
Grazie al codice appena visto verranno registrate le azioni dell’utente correntemente loggato. L’indirizzo IP verrà salvato in automatico e il flag “developer” sarà impostato se l’utente corrente avrà una variabile di sessione “developer” settata su true.
Una piccola ottimizzazione che ti propongo è quella di scatenare un evento che esegue Activity::log() ogni volta che lo desideri, magari parametrizzandolo opportunamente.
Prendiamo ad esempio l’azione di login. Ogni volta che l’utente viene autenticato puoi lanciare l’evento in questo modo:
Ti ritrovi spesso nella posizione di dover informare gli utenti, in risposta a qualche azione o evento particolare?
Oggi vedremo insieme un nuovo strumento a nostra disposizione, il package laracastsflash.
Installazione
Per prima cosa aggiungi il package al file composer.json, ed esegui composer update:
“require”: {
“laracasts/flash”: “~1.0”
}
Successivamente, dopo aver eseguito da terminale il comando composer update, aggiungiamo il service provider all’interno dell’array providers del file app.php:
Ed un metodo speciale (lo vedrai in azione a fine articolo):
Flash::overlay(‘Modal Message’, ‘Modal Title’)
Ogni metodo visto, memorizza in sessione, le seguenti chiavi:
flash_notification.message – Il messaggio che vogliamo mostrare all’utente
flash_notification.level – Una stringa rappresentante il tipo di avviso (utile per applicare una classe css al codice HTML)
Nota: Servendosi di Twitter Bottstrap ‘flash_notification.level’ assumerà i seguenti valori: alert-info, alert-success, alert-danger e alert-warning, che rappresentano le classi di default per la visualizzazione di box di alert.
Una volta memorizzato nella sessione il messaggio, siamo pronti a visualizzarlo all’interno delle nostre view.
Il template utilizzato di default è il seguente:
@if (Session::has(‘flash_notification.message’))
{{ Session::get(‘flash_notification.message’) }}
@endif
Nota: questo package è ottimizzato per Twitter Bootstrap.
Poichè i messaggi di avviso sono comuni in un applicazione web, che interagisce con gli utenti, anziché riscrivere ogni volta il codice appena visto, puoi usare (o modificare) il template che questo package offre.
Basta includerlo nelle view in questo modo:
@include(‘flash::message’)
Non stai usando Bootstrap ? Ahia! Allora non puoi usare questo package…
… scherzo! Se non ancora usi Twitter Bootstrap, puoi modificare i layouts dei messaggi.
Per farlo digita da console :
php artisan view:publish laracasts/flash
In questo modo avrai a disposizione le views utilizzate, e potrai facilmente modificarne il codice html. Ad maiora!
Esempio
Vediamo insieme un esempio pratico di utilizzo di questo package.
Creiamo una view per la registrazione di utenti al nostro sito. Aggiungiamo un campo per l’username e l’email, ed uno per la password, in questo modo:
{{ Form::open() }}
{{ Form::close() }}
Il nostro controller avrà un metodo destinato alla gestione della registrazione:
public function register()
{
// validazione dati
$validator = Validator::make(Input::all(), [‘username’ => ‘required’, ‘email’ => ‘required’, ‘password’ => ‘required]);
if ($validator->fails())
{
// Impostare il messaggio di errore
return Redirect::back()->withInput()->withErrors($validator);
}
// Impostare il messaggio di registrazione effettuata
return Redirect::back();
}
Ora, come ti ho spiegato ad inizio articolo, dobbiamo inserire il nostro messaggio per l’utente, prima del redirect.
Per prima cosa aggiungi il messaggio in caso la validazione fallisca:
if ($validator->fails())
{
// Impostare il messaggio di errore
Flash::error(‘Attenzione i dati inseriti non sono validi !’);
Può capitare alle volte di dover inserire all’interno delle nostre view, del codice che richiede l’esecuzione di alcune operazioni.
Ad esempio, visualizzare la lista di utenti iscritti al nostro sito, con nome e cognome di ognuno, oppure visualizzare un avatar caricato dai nostri utenti, ecc ecc.
In questo scenario, hai diverse soluzioni per gestire queste situazioni:
Forzare l’inserimento del codice all’interno della view
Inserire il codice all’interno dei nostri model
Tuttavia, esiste una terza alternativa. L’uso dei Presenter.
Cominciamo a realizzare una piccola applicazione, per studiare le funzionalità di questo package.
Le tabelle
Cominciamo dalla creazione della tabella dei videogame. Usiamo il comando artisan ‘php artisan migration:make create_videogames’:
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(‘videogames’, function(Blueprint $table)
{
$table->increments(‘id’);
$table->string(‘title’);
$table->string(‘description’);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop(‘videogames’);
}
Passiamo alla tabella dei voti, che andrà a rappresentare la relazione molti a molti tra videogame ed utenti:
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create(‘user_videogame’, function(Blueprint $table)
{
$table->increments(‘id’);
$table->integer(‘user_id’)->unsigned()->index();
$table->foreign(‘user_id’)->references(‘id’)->on(‘users’)->onDelete(‘cascade’);
$table->intger(‘videogame_id’);
$table->foreign(‘videogame_id’)->references(‘id’)->on(‘videogames’)->onDelete(‘cascade’);
$table->float(‘rating’);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop(‘ratings’);
}
Prima di proseguire, non dimenticarti di eseguire le migration. Per farlo basta usare il comando ‘php artisan migrate’.
public function voters($id)
{
return $this->belongsToMany(‘User’);
}
}
Nota: Il metodo belongsToMany della classe Eloquent viene usato per rappresentare la relazione molti a molti, in questo caso quella tra utenti e videogame.
##I Controllers
Ora passiamo al controller dei videogames:
class VideogamesController extend BaseController {
public function __construct()
{
$this->beforeFilter(‘auth’, [‘only’ => [‘postRatings’]]);
}
public getIndex()
{
$videogames = Videogame::all();
public function postRatings($id)
{
// Salvataggio voto
$videogame = Videogame::findOrFail($id);
// registro il voto dell’utente assieme alla valutazione data per il videogame
return $videogame->voters()->attach(Auth::id(), [‘ratings’ => Input::get(‘rating’));
}
}
La registrazione del voto verrà effettuata aggiornando la tabella pivot user_videogame, aggiungendo il valore di rating inserito dall’utente.
La View
Creiamo una view in cui verrà mostrata la lista dei videogames, con il relativo form per la votazione:
@foreach ($videogames as $videogame)
{{ $videogame->title }}
{{ $videogame->description }}
{{– Visualizzazione della media voto del videogame –}}
A questo punto dobbiamo far visualizzare il voto medio per ogni videogame. Abbiamo due alternative:
Inserire il calcolo in un metodo all’interno del model
Inserire il calcolo della media voto direttamente nella view
Ora, perchè “sporcare” i nostri model o le nostre view aggiungendoci del codice in eccesso ? Possiamo fare diversamente ?
Certo ! Ed è qui che entra in gioco LaracastsPresenter.
Le routes
Impostiamo le route necessarie per il nostro esempio in questo modo:
Installiamo il package. Digita da terminale ‘composer require laracastspresenter’, inserendo come versione la 0.1.*, premi invia ed attendi l’installazione.
Una volta terminata, siamo pronti ad usare i nostri presenter.
Uso
Per prima cosa crea il tuo primo presenter per i videogame in questo modo:
use LaracastsPresenterPresenter;
class VideogamePresenter extends Presenter {
public function averageRating()
{
if ($this->entity->voters->count())
{
return $this->entity->voters()->sum(‘rating’) / $this->entity->voters->count();
}
return 0;
}
/**
* Ritorna una stringa con il numero di votanti per il videogame
*
*@return string
*/
public function numberOfVoters()
{
return ‘‘ . $this->entity->voters->count() . ‘‘;
}
}
Il metodo averageRating, calcola il voto medio del singolo videogame (una semplice divisione tra il totale di tutti voti e il numero di votanti).
Nota: Dalla classe VideogamePresenter per accedere alle relazioni del model Videogame, basta usare $this->entity.
Successivamente all’interno model Videogame aggiungi il trait LaracastsPresenterPresentableTrait, che farà in modo di istanziare automaticamente il presenter del modello Videogame, in questo modo:
use LaracastsPresenterPresentableTrait;
class Videogame extends Eloquent {
use PresentableTrait;
La chiamata al metodo averageRating si effettua con il metodo present(),
che ritorna una nuova istanza dell’oggetto VideogamePresenter o l’istanza precedentemente memorizzata.
Perchè non aggiungere un presenter anche ad User ?
Magari per visualizzare la lista dei votanti per ogni videogame e, per ogni utente visualizzare la rispettiva username ed email.
Modifichiamo la clase User, aggiungendo il trait PresentableTrait:
use LaracastsPresenterPresentableTrait;
class User extends Eloquent {
use PresentableTrait;
protected $presenter = ‘UserPresenter’;
/**
* Lista dei giochi votati dall’utente
*
*/
public function videogames()
{
return $this->belongsToMany(‘Videogame’);
}
}
Ed infine crea la classe UserPresenter:
use LaracastsPresenterPresenter;
class UserPresenter extends Presenter {
/**
* Visualizza il nome completo dell’utente con username ed email
*
* @return string
*/
public function fullName()
{
return $this->username . ‘ – ‘ . $this->email;
}
}
A questo punto, non ti rimane che mostrare la lista dei votanti per il videogame:
@foreach ($videogames as $videogame)
{{ $videogame->title }}
{{ $videogame->description }}
{{– Visualizzazione della media voto del videogame –}} media voto : {{ $videogame->present()->averageRating }}/5 su {{ $videogame->present()->numberOfVoters }}
Lo so, ti occorre un pò di pazienza per imparare tutte queste classi. Un buon modo per iniziare può essere quello di elencare specificando di cosa si occupano:
Former: è la classe principale e si occupa delle configurazioni e delle interazioni tra le varie componenti di un form
FormerServiceProvider: è il Service Provider per Laravel 4
Helpers: un set di helpers statici a supporto delle varie librerie
Dispatch: intercetta la chiamata che viene fatta alla Former facade rimandandola alla classe di competenza
LiveValidation: gestisce la validazione dei campi e la traduzione delle regole negli attributi
Populator: è un contenitore di valori che Former sfrutta per fare il get/set degli stessi
Traits:
Checkable: metodi e proprietà relativi a radiobutton e checkbox
Field: metodi e proprietà relativi ai campi
FormerObject: oggetto che estende le caratteristiche di cui sopra, gestisce attributi dinamici
Framework: metodi e proprietà comuni ai vari framework
Interfaces: metodi required dei vari framework
FieldInterface
FrameworkInterface
Framework : queste classi generano il corretto markup in base al framework in uso
Nude
TwitterBootstrap
ZurbFoundation
Form: le seguenti classi gestiscono il markup di una form
Actions: gestisce le azioni dei pulsanti submit
Elements: gestisce i vari elementi del form che non sono azioni (legende, ecc..)
Form: gestisce l’apertura e la chiusura dei form e altri metodi correlati
Group: si occupa del wrap e dello stato dei campi, del recupero degli errori e altro
Fields: una serie di classi ognuna delle quali gestisce un tipo di campo
Button
Checkbox
File
Hidden
Input
Radio
Select
Textarea
Uneditable
Facades : Alcuni entry points che facilitano l’esperienza attraverso i vari ambiaenti
Agnostic : la facade di base usata da Former al di fuori di ogni framework
FormerBuilder :Metodi comuni e blocchi da usare per tutte le facade
Illuminate : la facade di Laravel 4, aggancia Former ad alcuni componenti (localizzazione etc)
LaravelThree : la facade di Laravel 3, aggancia Former ad alcuni componenti (localizzazione etc)
Legacy : Un set di A set of classi redirector che unificano l’interfaccia tra Laravel 3 e 4
Config
Redirector
Session
Translator
Descrizione dei metodi
FormerFormer
E’ la classe principale e costituisce la tua interfaccia per tutto in Former. Chiaramente Former ti da la possibilità di interagire con le sue sottoclassi e quindi non solo puoi usare i suoi metodi ma anche quelli delle sottoclassi.
Option
E’ un set di opzioni utili a modificare il comportamento di Former
Di default, Former tenta di tradurre labels, legend e help text. Prima effettua un tentativo di traduzione delle stringhe in automatico poi controlla che il testo sia presente da un’altra parte. Puoi impostare questa variabile che di default è settata a 'validation.attributes.mykey'.
Di default quando chiami Former::open, una fallback form type viene assegnata al form – dice di che tipo è il form. Può essere uno dei valori o null se non vuoi usare le classi di Bootrap per i form.
Former::config(‘fetch_errors’, [boolean]) (true)
Ogni volta che chiamo il metodo open, se è settato a true, Former controllerà nell’array Session la presenza dell’oggetto Message che il metodo ->with_errors() della classe Redirect avrebbe dovuto creare. Ciò significa che se chiami Redirect::to()->with_errors() quando la validazione fallisce, Former assocerà automaticamente gli errori visualizzandoli accanto ai campi corrispondenti senza dover scrivere ulteriore codice.
Permette all’utente di attivare o meno le label automatiche. Quando è attivo, se crei un campo di nome foo senza specificare la label, Former assumerà che foo sia la label per quel campo. In sostanza Former tenterà di tradurla e di usarla come label. Se il campo non è attivo nessuna label verrà assegnata al campo.
In questo caso i checkboxes devono essere sempre presenti nei dati in POST: sia se checked o no.
Helpers
Former::populate([array|Eloquent])
Popola i campi con un array di valori o con un oggetto Eloquent
Former::withErrors([Validator])
Dopo il fallimento di una validazione Former effettua il fetch della variabile $errors dalla Session e imposta come non corretti i campi corrispondenti visualizzando l’errore. Se ti trovi all’interno del controller puoi passare l’oggetto $validator a Former per ottenere lo stesso comportamento.
Former::withRules([array])
Consente di passare una serie di regole Laravel a Former, provando ad applicarle su attributi HTML come pattern, maxlength, ecc..
Former::framework([bootstrap|zurb|null])
Seleziona il tipo di framework CSS Former deve usare per la sua sintassi dandoti più o meno accesso ai metodi disponibili: ad esempio non puoi usare il metodo Bootstrap ->blockHelp se stai usando Zurb, ecc…
$error = Former::getErrors([string])
E’ l’equivalente di $error = isset($errors) ? $errors->first('field') : null.
La validazione dei dati è una parte essenziale per qualsiasi applicazione web. Se conosci le basi di laravel, saprai che esiste un sistema molto semplice di validazione che utilizza la classe Validator.
Il package “laracasts/validation” ti offre un modo alternativo, e ben strutturato per il controllo dell’input.
Scopriamolo insieme, in questo articolo.
Installazione del Package
Installiamo il package tramite composer, aggiungendo questa stringa nel campo require:
“require”: {
“laracasts/validation”: “~1.0”
}
Successivamente, dopo aver eseguito da terminale il comando composer update, aggiungiamo il service provider all’interno dell’array providers del file app.php.
‘LaracastsValidationValidationServiceProvider’
Utilizzo
Immagina di dover validare i dati degli utenti che effettuano un login. Per prima cosa, crea una classe LoginForm, in cui andrai ad inserire le tue regole di validazione, in questo modo:
use LaracastsValidationFormValidator
class LoginForm extends FormValidator {
/**
* Validation rules for logging in
*
* @var array
*/
protected $rules = [
‘username’ => ‘required’,
‘password’ => ‘required’
];
}
Il passo successivo è di includere questa nuova classe all’interno del nostro controller, o comunque dove tu abbia bisogno di eseguire la validazione.
use MyAppFormsLogin as LoginForm;
use LaracastsValidationFormValidationException;
protected $loginForm;
public function __construct(LoginForm $loginForm)
{
$this->loginForm = $loginForm;
}
Se la validazione va a buon fine, verrà ritornato true. Altrimenti verrà lanciata un’eccezione FormValidationException.
A questo punto hai due scelte: la prima è quella di intercettare l’eccezione all’interno del tuo controller come nell’esempio, con il blocco try catch.
La seconda è di usare un intercettazione globale, inserendo questo semplice codice nel file global.php:
Come ben saprai… (perché lo sai, vero?) global.php contiene degli handlers globali per intercettare gli errori. Quindi se la validazione fallisce, l’utente verrà reindirizzato nella pagina del form.
Ora non rimane che validare i dati, usando il metodo validate:
$this->registrationForm->validate(Input::all());
Ed voilà, il gioco è fatto.
Spero che questo package possa esserti utile: se hai qualche osservazione, feedback o semplice parere commenta qui sotto!
Note Conclusive
Questo package non sarà necessario in Laravel 5.0.
Quante volte ti sei trovato davanti alla creazione di un bottone che al click dovesse eliminare quel determinato id, in quella determinata tabella?
Sicuramente più di una volta. Sicuramente ti sarai chiesto: “come posso fare per far comparire un popup per avere la conferma che l’utente voglia realmente eliminare quel determinato elemento?”
Bene! Oggi ti voglio spiegare come fare.
// Generare un token CSRF e metterlo all’interno di un attributo data-delete personalizzato
public function buttonDelete()
{
$format = ‘‘;
$link = URL::route(‘accounts.groups.delete’, [‘id’ => $this->resource->id]);
$token = csrf_token();
$title = “Delete the group”;
return sprintf($format, $link, $token, $title);
}
// Funzione jQuery che intercetta gli eventi click su elementi aventi un attributo data-delete
$(‘[data-delete]’).click(function(e){
e.preventDefault();
// Se l’utente conferma l’eliminazione
if (confirm(‘Do you really want to delete the element ?’)) {
// URL route
var url = $(this).prop(‘href’);
// Token
var token = $(this).data(‘delete’);
// Creo un elemento form
var $form = $(‘
Oggi ti presento Former, un package in PHP per Laravel che ti permette di gestire i form in modo incredibilmente semplice.
Former fa tanto lavoro al posto tuo: si occupa della ripopolazione dei campi, della validazione, della gestione dei grouped field e si distingue per l’integrazione con framework CSS quali Bootstrap e Foundation.
Introduzione
Lo scopo di Former è quello di ripensare in maniera elegante il processo di creazione dei form trasformando ogni campo in un proprio “oggetto” con attributi e metodi. In pratica una cosa del genere:
In riferimento all’esempio, se chiami un metodo che non esiste, Former assumerà che tu stia tentando di settare un attributo e quindi lo creerà. Scrivendo ->rows(10) definirai un attributo che indica il numero di righe.
Se invece vuoi definire un metodo che contiene dei trattini puoi farlo utilizzando l’underscore nel nome del metodo: con ->data_foo('bar') definisci l’attributo data-foo="bar".
Ora ti starai chiedendo: e se voglio definire un attributo con underscore? Fugo subito i tuoi dubbi: setAttribute('data_foo', 'bar') ed è fatta.
Iniziamo
Concetti di base
Former va concepito come un helper per le tue view. Questo significa che puoi utilizzare i suoi metodi all’interno delle stesse così:
method(‘GET’) ?>
required() ?>
Se utilizzi un template engine come Twig o simili, puoi usare Former all’esterno delle tue view in questo modo:
Per installare il package in un progetto esterno al framework usa comunque Composer, non scordandoti però di aggiungere, dove necessario, il
use FormerFacadesFormer;
Features
Integrazione con Bootstrap e Foundation
Finora potrai esserti chiesto se questa non sia altro che una variante della classe Form già inclusa in Laravel. Dov’è la sua potenzialità? La risposta è semplice: Former si integra con vari framework e permettendoti di riconoscere, ad esempio, se stai definendo un form orizzontale o verticale inserendolo direttamente all’interno dei tag giusti per l’inclusione nel form.
Un esempio? Il codice:
Former::select(‘clients’)->options($clients, 2)
->help(‘Pick some dude’)
->state(‘warning’)
genera questo html (con Bootstrap):
Pick some dude
Former usa come Framework di default Bootstrap ma puoi scegliere di cambiarlo con il metodo Former::framework(). Al momento Former supporta le seguenti opzioni 'TwitterBootstrap','ZurbFoundation' and 'Nude' (per non usare framework)
/ /disabilita la sintassi Bootstrap
Former::framework(‘Nude’);
// Rabilita la sintassi Bootstrap
Former::framework(‘TwitterBootstrap’);
Bene, negli esempi visti prima hai fatto un bel passo avanti: non dovrai più chiamare la funzione Form::control_group() e sarai più spedito nella costruzione del form. Ma ora ti starai chiedendo: “come faccio per la validazione? dovrò comunque validare a mano tutti i campi”.
Buone notizie: non dovrai. Il metodo withErrors() di Former accorre in tuo aiuto.
Former non solo si occupa di fare il wrapping dei tuoi campi all’interno dei control group, ma controlla anche l’oggetto Message verificando se un campo contiene o meno errori ed evidenziandolo, eventualmente, con l’attributo .help-inline. Vediamo come.
Ecco due esempi dopo una validazione non andata a buon fine:
Se renderizzi una view dopo una validazione fallita (senza redirect):
Avrai notato come nell’ultimo esempio non chiami Former. Il perché è presto detto. Ogni volta che costruisci un form con Former, questo si occuperà di controllare se nell’array Session è presente l’oggetto errors e, in caso affermativo, lo userà senza richiedere altro. Puoi disabilitare la possibilità di fetching automatico degli errori impostando l’opzione seguente: Former::config('fetch_errors', false).
Popolare i Form
Popolare un form con Former è semplice, basta usare il metodo Former::populate. Puoi farlo in due modi. Il primo è quello di passare un array di valori:
//Popolerà il campo ‘name’ con il valore ‘value’
Former::populate( array(‘name’ => ‘value’) )
Il secondo è quello di passare un Eloquent model. Se ad esempio hai un model “Client” puoi operare così:
Former::populate( Client::find(2) )
Former riconosce il model e lo popola con i suoi attributi. Supponi che il model abbia l’attributo name impostato a Foo e l’attributo firstname impostato a Bar. Bene, Former non farà altro che ripopolare i campi name e firstname rispettivamente con i valori Foo e Bar.
In alternativa puoi anche ripopolare un campo dopo aver ripopolato l’intero form (è il caso delle relazioni ad esempio) operando così:
Per il resto ripopolare un form è cosa banale grazie al comando: ->value('something'). Per generare una lista di option per una <select> chiama il metodo Former::select('foo')->options([array], [facultative: selected value]). Puoi anche usare il risultato di una query (Eloquent o Fluent) per popolare gli option di una select:
Former::select(‘foo’)->fromQuery(Client::all(), ‘name’, ‘id’)
Il secondo argomento rappresenta il testo dell’option mentre il terzo rappresenta il valore (di default il valore è id). Ma Former può fare molto di più. Supponi di passare un model Eloquent alla tua select senza aver specificato il secondo e il terzo parametro. Former tenterà di recuperare la chiave usando il metodo Eloquent get_key() e userà il metodo __toString() per bindare le variabili. Guarda questo esempio:
class Client extends Eloquent
{
public static $key = ‘code’;
public function __toString()
{
return $this->name;
}
}
Former::select(‘clients’)->fromQuery(Client::all());
Sicuro e secondo il paradigma “stay DRY”. Il seguente codice è equivalente ma molto più oneroso: usa tutte le chiavi di default del model Client e come output l’attributo name per la label delle option.
Foo
@foreach(Client::all() as $client)
@if(Input::get(‘foo’, Input::old(‘foo’)) == client->code)@else@endif
@endforeach
Former è anche in grado di popolare dei campi coinvolti in una relazione. Un esempio vale più di mille parole (a meno che il vostro esempio non superi le mille parole).
Former::populate(Client::find(2))
// Sarà popolato con $client->name
Former::text(‘name’)
//Sarà popolato con $client->store->name
Former::text(‘store.name’)
// Puoi scegliere il livello di profondità che vuoi
Former::text(‘customer.name.adress’)
// Sarà popolato con la data di tutte le date di reservation di Client
Former::select(‘reservations.date’)
// Stessa cosa di ^
Former::select(‘reservations’)->fromQuery($client->reservations, ‘date’)
// Se stai usando un testo e non una select, invece di elencare i modelli della relazione come opzioni li concatenerà
Former::text(‘customers.name’) // Mostrerà “name, name, name”
// Puoi rinominare un campo successivamente per una più facile gestione dell’Input
Former::text(‘comment.title’)->name(‘title’)
Datalist
Un’altra cosa che Former può fare è creare dei Datalist.
Sai cosa sono? Hai presente quando a volte vuoi far scegliere all’utente qualcosa da una selezione ma nello stesso tempo vuoi anche dargli la possibilità di scriverlo se quello che vuole non c’è nella lista? Bene, quello è un Datalist e crearlo con Former è molto semplice:
Former::text(‘clients’)->useDatalist($clients)
//Puoi usare un Query object, stessa sintassi poi per fromQuery()
Former::text(‘projects’)->useDatalist(Project::all(), ‘name’)
Puoi anche (se ne hai bisogno) assegnare un id al datalist appena creato in questo modo: Former::text('foo')->list('myId')->useDatalist(). In questo modo genererai il corrispondente <datalist> e lo linkerai tramite id a quel campo. Ciò significa che il tuo text input sarà popolato dai valori del tuo array fino a quando l’utente non inserisce ciò che vuole.
Live Validation
Bene, andiamo avanti. Come molti di voi sapranno, oggi praticamente tutti i browser moderni supportano la validazione lato client con il solo utilizzo di attributi html. È il caso di attributi come pattern, required, max/min ecc…. Quando validi un form lato server hai il tuo array $rules in cui definisci le regole di validazione. Non sarebbe fantastico trasferire quelle regole automaticamente anche lato client in modo da non dover ridefinire due volte le stesse cose?
Former fa proprio questo e con estrema facilità. Ecco un esempio:
Quello che Former fa è creare l’html con gli attributi in base alle regole che abbiamo definito sopra. Non ci sono molte regole per ora ma la cosa è in via di sviluppo. Ecco come diventa il codice di sopra lato client:
Inoltre puoi sempre aggiungere regole personalizzate usando espressioni Regex:
Bello vero?! E non finisce qui. Poiché Bootstrap riconosce la validazione in tempo reale, prova a fare un test scrivendo qualcosa che non faccia match con la regola alpha del campo nome e vedrai come si evidenzierà in rosso. Proprio come quando viene assegnata la classe error.
Non ti rimane che aprire il tuo Chrome/Firefox/o-quel-che-è e vedere come ogni volta si aprirà un piccolo pop up che dirà cose come: “Devi riempire quel campo” oppure “L’email non è nel formato giusto, mi credi stupido o cosa?”
Puoi anche impostare manualmente lo stato di un control group (disponibile solo per Bootstrap e Foundation) usando uno degli attributi messi a disposizione come: success, warning, error and info.
Former::text(‘name’)->state(‘error’)
Gestione dei File
In Former come in Laravel, puoi creare un campo file semplicemente così: Former::file.
Cosa c’è di nuovo in questo caso? Beh, puoi creare campi file multipli semplicemente così Former::files generando il seguente html corrispondente: <input type=""file"" name=""foo[]"" multiple="">.
Uno dei metodi speciali è ->accept() che ti permette di:
// Usare uno shortcut per accettare tipi di file (image, video o audio)
Former::files(‘avatar’)->accept(‘image’)
// Usare un’estensione che verrà converita in MIME da Laravel
Former::files(‘avatar’)->accept(‘gif’, ‘jpg’)
//O usare direttamente un MIME
Former::files(‘avatar’)->accept(‘image/jpeg’, ‘image/png’)
Puoi settare la grandezza del file esprimendola in bit o byte:
// Ciò che è valido per un checkbox vale anche per i radio button
Former::radios(‘radio’)
->radios(array(‘label’ => ‘name’, ‘label’ => ‘name’))
->stacked()
// Puoi definire checkbox e radio stacked o inline chiamando i relativi metodi
Former::inline_checkboxes(‘foo’)->checkboxes(‘foo’, ‘bar’)
Former::stacked_radios(‘foo’)->radios(‘foo’, ‘bar’)
//Impostare quali checkbox sono checkati o no in un solo comando
Former::checkboxes(‘level’)
->checkboxes(0, 1, 2)
->check(array(‘level_0’ => true, ‘level_1’ => false, ‘level_2’ => true))
Un aspetto importante: Former ti offre un’opzione per forzare il pushing dei checkbox. Cosa vuol dire?
Si tratta di quando i tuoi checkbox appaiono nell’array POST anche se non sono checked. E’ un comportamento non molto naturale. Per questo Former ti da la possibilità di cambiare il valore di un checkbox non checked nell’array POST attraverso l’opzione unchecked_value.
Quando crei dei molti campi checkabili attraverso i metodi checkboxes/radios(), per orgnuno di loro verrà utilizzato il nome che hai specificato legato ad un numero seriale tramite un underscore, ecco un esempio: <input type=""checkbox"" name=""checkme_2""> Il campo verrà anche ripopolato nel caso fosse stato “checkato”.
Helper per la Localizzazione
Former ti aiuta anche nel caso tu stessi lavorando su un progetto multilingua.
Quando crei un campo, se non hai specificato una label, Former utilizza il nome di dafault e in maniera automatica tenterà anche di tradurlo. Questo vale per le label dei checkbox, gli help text e le legend dei form. Vediamo alcuni esempi:
Non male vero? Former prima tenterà di tradurre la stringa da solo, my.text ritornerà __('my.text'), e in caso di fallimento cercherà in un posto alternativo che puoi impostare attraverso la variabile Former::config('translate_from', [boolean]) (di default validation.attributes). Ricorda: deve essere un array.
Note sulle impostazioni dei Valori dei Campi
i dati in POST hanno la precedenza su tutto il resto – se un utente digita qualcosa in un campo è anche probabile che è quello che si aspetta di vedere quando questo deve essere ripopolato;
poi ci sono i campi impostati usando il metodo ->forceValue() – è un caso particolare del metodo ->value() che forza un valore in un campo indipendentemente da ciò che accade;
successivamente ci sono i campi settati con il metodo Former::populate(). È il metodo usato per popolare un campo e, in caso di editing dello stesso, può essere sovrascritto da forceValue;
l’ultimo in ordine di priorità è il classico metodo ->value() – è usato per popolare con valori di default un campo e viene sovrascritto da tutti gli altri e dai dati in POST;
Conclusioni
Per oggi basta: abbiamo analizzato le varie feature di Former e fatto un giro completo. Vuoi saperne di più? Aspetta la prossima settimana: entreremo più nel dettaglio.