Blade avançat

Funcionalitats avançades de Blade: directives personalitzades, service injection, stacks i fragments.

Directives personalitzades#

Blade et permet crear les teves pròpies directives per encapsular lògica que es repeteix sovint a les vistes. Les directives es registren al mètode boot() d'un service provider:

// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Blade;
 
public function boot(): void
{
    Blade::directive('datetime', function (string $expression) {
        return "<?php echo ($expression)->format('d/m/Y H:i'); ?>";
    });
 
    Blade::directive('money', function (string $expression) {
        return "<?php echo number_format($expression, 2, ',', '.') . ' €'; ?>";
    });
}

Un cop registrades, pots utilitzar-les a qualsevol vista Blade:

<p>Publicat: @datetime($article->created_at)</p>
<p>Preu: @money($product->price)</p>
 
{{-- Resultat: Publicat: 15/01/2024 14:30 --}}
{{-- Resultat: Preu: 29,99 € --}}

Les directives es compilen a PHP pur i es guarden en cache, de manera que no afecten el rendiment. Recorda que després de crear o modificar una directiva, cal esborrar la cache de vistes amb php artisan view:clear.

Directives condicionals personalitzades#

Pots crear directives condicionals que funcionin com @if però amb una semàntica més clara:

Blade::if('admin', function () {
    return auth()->check() && auth()->user()->is_admin;
});
 
Blade::if('env', function (string $environment) {
    return app()->environment($environment);
});
@admin
    <a href="/admin">Panell d'administració</a>
@endadmin
 
@env('local')
    <div class="debug-bar">Mode de desenvolupament</div>
@endenv

Stacks#

Els stacks permeten que les vistes filles o els components afegeixin contingut a zones específiques del layout. Són especialment útils per afegir scripts o estils que només necessita una pàgina o component concret.

Al layout, defineixes on es renderitzarà cada stack amb @stack:

{{-- Layout --}}
<head>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    @stack('styles')
</head>
<body>
    {{ $slot }}
 
    <script src="{{ asset('js/app.js') }}"></script>
    @stack('scripts')
</body>

Des de qualsevol vista filla o component, pots afegir contingut al stack amb @push:

@push('styles')
    <link rel="stylesheet" href="{{ asset('css/datepicker.css') }}">
@endpush
 
@push('scripts')
    <script src="{{ asset('js/datepicker.js') }}"></script>
    <script>
        initDatepicker('#birthday');
    </script>
@endpush

Si necessites que el contingut vagi al principi del stack en lloc del final, utilitza @prepend:

@prepend('scripts')
    <script src="{{ asset('js/vendor/jquery.js') }}"></script>
@endprepend

Això és útil quan un script necessita carregar-se abans que els altres scripts del stack.

@once#

Quan un component es renderitza múltiples vegades a la mateixa pàgina, potser necessites que certs scripts o estils s'incloguin només un cop. La directiva @once garanteix que el contingut es renderitzi una sola vegada:

{{-- Component que es pot renderitzar moltes vegades --}}
@once
    @push('styles')
        <link rel="stylesheet" href="{{ asset('css/tooltip.css') }}">
    @endpush
 
    @push('scripts')
        <script src="{{ asset('js/tooltip.js') }}"></script>
    @endpush
@endonce
 
<span class="tooltip" data-text="{{ $text }}">
    {{ $slot }}
</span>

Sense @once, si el component es renderitza 10 vegades, el CSS i el JavaScript s'inclourien 10 vegades. Amb @once, s'inclouen una sola vegada.

Renderització condicional de classes i estils#

La directiva @class genera una llista de classes CSS condicionals de manera neta. Les classes sense condició s'inclouen sempre, i les que tenen una condició només s'inclouen si la condició és certa:

<div @class([
    'p-4 rounded-lg border',
    'bg-green-50 border-green-300 text-green-800' => $status === 'success',
    'bg-red-50 border-red-300 text-red-800' => $status === 'error',
    'bg-yellow-50 border-yellow-300 text-yellow-800' => $status === 'warning',
    'bg-blue-50 border-blue-300 text-blue-800' => $status === 'info',
])>
    {{ $message }}
</div>

De la mateixa manera, @style permet afegir estils inline condicionals:

<div @style([
    'background-color: red' => $isHighlighted,
    'font-weight: bold' => $isImportant,
    'padding: 1rem',
])>
    {{ $content }}
</div>

Atributs condicionals#

La directiva @checked, @selected, @disabled i @readonly simplifiquen l'escriptura d'atributs HTML condicionals als formularis:

<input type="checkbox"
       name="active"
       value="1"
       @checked(old('active', $user->active)) />
 
<select name="role">
    @foreach($roles as $role)
        <option value="{{ $role->id }}" @selected(old('role') == $role->id)>
            {{ $role->name }}
        </option>
    @endforeach
</select>
 
<input type="text"
       name="email"
       value="{{ $user->email }}"
       @readonly(!$user->isAdmin()) />
 
<button type="submit" @disabled($form->hasErrors())>
    Enviar
</button>

Service Injection#

Pots injectar serveis directament a les vistes amb la directiva @inject. Això és útil quan una vista necessita accedir a un servei sense haver de passar-lo explícitament des del controlador:

@inject('metrics', 'App\Services\MetricsService')
 
<div class="dashboard">
    <p>Ingressos del mes: {{ $metrics->monthlyRevenue() }} €</p>
    <p>Usuaris actius: {{ $metrics->activeUsers() }}</p>
    <p>Comandes pendents: {{ $metrics->pendingOrders() }}</p>
</div>

El primer paràmetre és el nom de la variable que estarà disponible a la vista, i el segon és la classe o interfície del servei. Laravel resoldrà el servei des del contenidor automàticament.

Tot i que @inject és pràctic, és recomanable utilitzar-lo amb moderació. En la majoria de casos, és millor passar les dades des del controlador per mantenir les vistes el més senzilles possible.

Renderitzar strings com a Blade#

Si necessites processar un string que conté sintaxi Blade (per exemple, contingut d'una base de dades que conté directives Blade), pots fer servir el mètode Blade::render():

use Illuminate\Support\Facades\Blade;
 
$html = Blade::render('Hola, {{ $name }}', ['name' => 'Joan']);

Fer servir Blade::render() amb contingut d'usuaris pot ser perillós perquè les directives Blade executen codi PHP. Només utilitza'l amb contingut de confiança.

Cache de vistes#

Blade compila les vistes a PHP pur i les guarda en cache al directori storage/framework/views/. Aquestes vistes compilades es regeneren automàticament quan el fitxer .blade.php es modifica. En producció, pots precompilar totes les vistes per millorar el rendiment:

# Compilar totes les vistes
php artisan view:cache
 
# Esborrar la cache de vistes
php artisan view:clear

Durant el desenvolupament local, no cal preocupar-se per la cache: Blade detecta automàticament els canvis i recompila les vistes.