Blade è il php template engine fornito di default dal framework Laravel. È pensato per rendere semplice la scrittura di template, mantenendo, allo stesso tempo, la possibilità di definire template non banali che possono utilizzare al loro interno codice PHP.
Lo scopo di un template engine è quello di creare uno specifico contenuto da presentare partendo da un modello e applicando ad esso dati. Il modello è salvato, abitualmente, in un file chiamato, per l’appunto, “template”, mentre i dati vengono recuperati da altre porzioni della propria applicazione e applicati al template tramite l’invocazione di un opportuno metodo.
Ciò consente di separare, nella propria applicazione, la parte di logica del recupero dei dati dalla parte di presentazione del dato (per intenderci, possiamo applicare lo stesso dato a diversi template e ottenere pagine diverse nell’aspetto, oppure possiamo scegliere cosa mostrare in base ai dati che abbiamo recuperato in quel momento).
I file di template Blade hanno estensione .blade.php e sono raccolti nella directory resources/views di una applicazione Laravel. L’elaborazione del template avviene invocando il metodo helper view, come visto nella precedente lezione.
Elementi base di un template Blade in Laravel
I template definiti con Blade fanno uso di particolari costrutti, chiamati statement o direttive, per permettere allo sviluppatore web di decidere come mostrare il contenuto.
<!-- resources/views/hello.blade.php --> <html> <body> <h1>Hello {{ $name }}</h1> <p>Today is {{ time() }}</p> </body> </html>
Lo statement più semplice e importante è {{ }}, indicato anche come echo statement. Tra le doppie parentesi graffe è possibile inserire del codice PHP che verrà elaborato e il suo risultato verrà inserito nel rendering del template. È possibile usare variabili (che verranno passate al template engine dal metodo helper view) o altri metodi del linguaggio PHP.
<!-- resources/views/hello.blade.php --> <html> <body> <h1> @if($name) Hello {{ $name }} @else Hello Laravel @endif </h1> </body> </html> // routes/web.php Route::get('/hello', function (Request $request) { $name = $request->query('name'); return view('hello', ['name' => $name]); });
Altre direttive permettono di aggiungere logica (di presentazione) al proprio template, semplificando, quindi, il codice lato route / controller. L’esempio qui sopra è una versione alternativa di quanto visto nella lezione sulle rotte, in cui la scelta di cosa mostrare, nel caso in cui non sia stato passato un name in query string, passa dal controller alla view, grazie alle funzionalità offerte dalla direttiva @if del template engine Blade.
NOTA: nel caso in esempio, l’uso della direttiva @if è esagerato. È possibile, infatti, usare l’operatore null coalescence di PHP dentro a un echo statement per ottenere lo stesso risultato in modo più elegante e chiaro: <h1>Hello {{ $name ?? ‘Laravel’ }}</h1>. Non dimenticare mai che i template Blade sono pur sempre file PHP, anche se l’esecuzione di codice PHP è permessa in particolari posizioni.
Direttive Blade di flusso
Alcune direttive di Blade risultano particolarmente utili per mostrare contenuti in base a determinate condizioni, basate ovviamente sui valori passati al template e letti da Blade come variabili PHP. Una caratteristica comune a queste direttive è che racchiudono la logica tra una direttiva d’apertura e una direttiva di “end”.
Direttiva @if in Blade
È possibile realizzare costrutti “if” usando le direttive @if, @elseif, @else e @endif. Queste direttive funzionano in maniera identica agli analoghi PHP.
@if (count($likes) === 1) You have only one like :-( @elseif (count($likes) > 1 && count($likes) < 10) You have {{ count($likes) }} likes :-) @elseif (count($likes) >= 10) People really likes you :-D @else No likes :'( @endif
Al fine di rendere il template più leggibile, Blade mette a disposizione alcune direttive di comodità che ricadono nell’ambito degli “if”:
- @unless(<condition>) … @endunless che corrisponde a un “if not”
- @isset(<variable>) … @endisset che controlla se la variabile è definita e non ha valore null
- @empty(<variable>) … @endempty che controlla è “empty”
Direttiva @switch in Blade
Le direttive @switch, @case, @break, @default e @endswitch permettono di costruire costrutti “switch”, sempre operando in maniera analoga alle controparti PHP.
@switch($variable) @case(1) First case... @break @case(2) Second case... @break @default Default case... @endswitch
Direttive di loop in Blade
Sono, ovviamente, disponibili direttive analoghe a quelle PHP per gestire con strutture di loop, che operano nello stesso modo.
@for ($value = 0; $value < 10; $value++) The current value is {{ $value }} @endfor @foreach ($fruits as $fruit) <p>Fruit {{ $fruit->$name }} in your order</p> @endforeach @forelse ($fruits as $fruit) <li>{{ $fruit->$name }}</li> @empty <p>No fruit</p> @endforelse @while (true) <p>I'm looping forever.</p> @endwhile
L’esecuzione della iterazione corrente del loop può essere saltata tramite le direttive @countinue e @break (usando una direttiva @if per definire la condizione di skip).
Una particolarità della direttiva @foreach è quella di rendere disponibile una speciale variabile $loop all’interno del loop stesso. Tale variabile permette di accedere a informazioni sul loop stesso, come, per esempio, l’iterazione corrente ($loop->iteration, che parte da 1, oppure $loop->index che parte da 0) oppure se è la prima/ultima del loop ($loop->first e $loop->last, entrambi booleani).
Direttive Blade di contenuto
Alcune direttive Blade sono state rese disponibili per semplificare l’elaborazione di determinati elementi HTML partendo dai valori passati al rendering del template
La direttiva @class permette di compilare una stringa per definire una classe CSS.
// valori passati al template engine $isActive = false; $hasError = true; <!-- file .blade.php --> <span @class([ 'font-bold' => $isActive, 'text-gray-500' => ! $isActive, 'bg-red' => $hasError, ])></span> <!-- output --> <span class="text-gray-500 bg-red"></span>
Questa direttiva accetta un array chiave/valore in cui la chiave è il nome della classe CSS da aggiungere e il valore è una espressione booleana che stabilisce se aggiungere tale classe.
La direttiva @checked può essere usata per aggiungere l’attributo checked a un elemento HTML di tipo <input> quando la condizione indicata ha valore true.
<input type="checkbox" name="newsletter" value="newsletter" @checked(@old('newsletter), $user->$newsletterOK) />
In modo analogo operano le direttive @selected per le option di una select HTML (), @disabled, @readonly e @required.
Layout e view child in Blade
Il vero punto di forza del template engine Blade risiede, però, nella sua capacità di poter separare parti comuni a vari singoli template in file separati e ricostruire, poi, il contenuto assemblando insieme le varie porzioni, con una logica basata sulla ereditarietà. Questo meccanismo prende il nome di template layout.
La possibilità di avere uno o più template base condivisi è molto importante per la creazione di siti web, poiché gran parte delle pagine di un sito o di una web app mantengono una struttura comune ed elementi ripetuti. Se si dovesse creare un file di template con l’HTML completo per ogni tipo di pagina erogata, si finirebbe con il dover modificare su più file le parti di codice che sono in comune (header, footer, ..).
Cominciamo con un pratico esempio, ipotizzando di voler realizzare un template layout in cui ci sono parti comuni (per esempio, i titoli con il nome del sito) e parti specifiche della pagina che stiamo realizzando.
Layout
Il file di template blade che rappresenterà la nostra “base” per tutte le pagine potrà essere salvato nella directory resources/views/layouts/ – quanto meno per ricordarci che è uno dei template layout di base – e avrà il seguente contenuto:
{{-- resources/views/layouts/page.blade.php --}} <html> <head> <title>@yield('title') - Yet No Name</title> </head> <body> @section('header') <p>This is the website Yet No Name</p> @show <div class="content"> @yield('content') </div> </body> </html>
In questo template abbiamo definito l’intera struttura di una pagina HTML e abbiamo utilizzato alcune direttive speciali di Blade: @section e @yeld. La direttiva @section serve a definire una sezione di un contenuto, mentre la direttiva @yeld è usata come segnaposto per dire dove andare a inserire una @section con lo stesso nome.
È possibile, a questo punto, definire una vista “child” che erediterà questo layout di base tramite la direttiva @extends:
{{-- resources/views/one.blade.php --}} @extends('layouts.page') @section('title', 'Page One') @section('header') @parent <p>This is the page one.</p> @endsection @section('content') <p>This is the content for Page One.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> @endsection
Una child view usa la direttiva @extends per indicare la view layout genitore da cui il child dovrebbe “ereditare”. All’interno della child view è possibile indicare il contenuto da iniettare nel genitore tramite le direttive @section. L’esatta composizione dipende dalla modalità con cui sono state definite le varie sezioni nel layout genitore e nella view figlia.
In particolare notare:
- @yeld(‘title’) nel layout e @section(‘title’, ‘Page One’) nel child → in questo modo nel layout abbiamo indicato un segnaposto, lasciando al child fornire il contenuto.
- @yield(‘content’)nel layout e @section(‘content’) … @endsection nel child → si comporta come il precedente, ma essendo un contenuto più complesso è compreso tra la direttiva di apertura e quella di chiusura della sezione.
- @section(‘sidebar’) … @show nel layout e @section(‘sidebar’) @parent … @endsection nel child → in questo caso, la section si apre nel layout e viene chiusa nel child, poiché è stata indicata la direttiva @show nel layout e @parent nel child.
Il file HTML generato invocando view(‘one’) sarà, quindi, il seguente
<html> <head> <title>Page One - Yet No Name</title> </head> <body> <p>This is the website Yet No Name</p> <p>This is the page one.</p> <div class="content"> <p>This is the content for Page One.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> </body> </html>
SubView incluse
NOTA: riportiamo per completezza la direttiva @include anche se il suggerimento degli sviluppatori di Laravel è quello di usare per la stessa esigenza i componenti Blade
La direttiva @include di Blade opera nel modo che il nome suggerisce: include una view all’interno di una view.
{{-- ... -- }} @section('content') @include('shared.footer')
La view inclusa riceverà tutti i dati disponibili nella view genitore, ma sarà possibile passarne altri indicando un array di coppie chiave / valore.
Sono disponibili alcune varianti della direttiva: @includeWhen, @includeUnless (condizione booleana per includere la view) e @includeFirst (la prima view trovata in un array di view).
View di collection
È disponibile una direttiva che unisce in sé le funzioni delle direttive @foreach e @include
{{-- file resources/view/page.blade.php --}} @each('shared.order', $orders, 'order') {{-- file resources/view/shared/fruit.blade.php --}} <span>{{ $order->$id }} - {{ $order->$amount }} - {{ $order->$status }}</span>
Il primo parametro della direttiva @each è il nome della view da utilizzare, il secondo parametro è l’array o la collection su cui iterare, il terzo argomento è il nome che avrà il singolo elemento su cui si sta iterando all’interno della view.