Vai al contenuto principale

Cos’è una facade

Una facade fornisce un’interfaccia “statica” alle classi disponibili nel service container dell’applicazione. Laravel include molte facade che danno accesso a quasi tutte le sue funzionalità. Le facade di Laravel fungono da “proxy statici” alle classi reali all’interno del service container: offrono una sintassi concisa ed espressiva mantenendo maggiore testabilità e flessibilità rispetto ai metodi statici tradizionali. Tutte le facade di Laravel sono definite nel namespace Illuminate\Support\Facades.
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/cache', function () {
    return Cache::get('key');
});
Non è un problema se non comprendi ancora completamente il funzionamento delle facade. Impara prima come usarle e continua a studiare Laravel.

Come funzionano le facade

Nelle applicazioni Laravel, una facade è una classe che offre accesso a un oggetto proveniente dal container. Questo meccanismo è implementato dalla classe Facade. Tutte le facade di Laravel e le facade personalizzate estendono la classe base Illuminate\Support\Facades\Facade. La classe base Facade usa il metodo magico __callStatic() per delegare le chiamate alla facade all’oggetto risolto dal container.
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Mostra il profilo dell'utente specificato
     */
    public function showProfile(string $id): View
    {
        $user = Cache::get('user:'.$id);

        return view('profile', ['user' => $user]);
    }
}
All’inizio del file importi la facade Cache. Questa facade fa da proxy verso l’implementazione dell’interfaccia Illuminate\Contracts\Cache\Factory. Tutte le chiamate effettuate tramite la facade vengono passate all’istanza interna del servizio di cache di Laravel. Se guardi la classe Illuminate\Support\Facades\Cache, non troverai un metodo statico get.
class Cache extends Facade
{
    /**
     * Restituisce il nome di registrazione del componente
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}
La facade Cache estende la classe base Facade e definisce il metodo getFacadeAccessor(). Questo metodo restituisce il nome del binding nel service container. Quando l’utente chiama un metodo statico della facade Cache, Laravel risolve il binding cache dal service container ed esegue su quell’oggetto il metodo richiesto (in questo caso get).

Quando usare (o non usare) le facade

Vantaggi delle facade

Le facade offrono molti vantaggi. Forniscono una sintassi concisa e memorabile che consente di usare le funzionalità di Laravel senza dover ricordare lunghi nomi di classi da iniettare e configurare manualmente. Inoltre, grazie all’uso peculiare dei metodi dinamici di PHP, sono facili da testare.

Attenzione allo scope creep

Il rischio principale nell’uso delle facade è lo “scope creep” delle classi. Poiché sono comode e non richiedono iniezione, si tende a far crescere una classe continuando ad aggiungere facade. Con la dependency injection, un costruttore che si allunga dà un chiaro segnale visivo. Quando usi le facade, presta attenzione alla dimensione delle classi e mantieni una responsabilità ristretta.
Se senti che una classe è cresciuta troppo, valuta di suddividerla in più classi più piccole.

Facade vs dependency injection

Uno dei principali vantaggi della dependency injection è la possibilità di sostituire l’implementazione della classe iniettata. Utile nei test: puoi iniettare mock o stub e verificare che vari metodi vengano invocati sullo stub. Normalmente i metodi statici veri e propri non possono essere mockati né sostituiti, ma le facade — grazie ai metodi dinamici che fanno da proxy verso l’oggetto risolto dal service container — possono essere testate esattamente come un’istanza di classe iniettata.
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});
Per questa route puoi scrivere il seguente test per verificare che Cache::get sia stato chiamato con l’argomento atteso.
use Illuminate\Support\Facades\Cache;

test('basic example', function () {
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
});

Facade vs funzioni helper

Oltre alle facade, Laravel fornisce funzioni “helper” per svolgere compiti comuni come creare viste, emettere eventi, dispatchare job e inviare risposte HTTP. Molte funzioni helper eseguono la stessa funzione della facade corrispondente.
// Chiamata tramite facade
return Illuminate\Support\Facades\View::make('profile');

// Chiamata equivalente tramite funzione helper
return view('profile');
Non ci sono differenze sostanziali tra facade e funzioni helper. Anche usando le funzioni helper puoi testarle come le facade corrispondenti.
use Illuminate\Support\Facades\Cache;

test('cache helper test', function () {
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
});

Real-time facade

Con le real-time facade puoi trattare qualsiasi classe dell’applicazione come una facade. Per spiegare come si usano, vediamo prima un codice senza real-time facade. Supponi che il modello Podcast abbia un metodo publish. Per pubblicare il podcast è però necessario iniettare un’istanza di Publisher.
<?php

namespace App\Models;

use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * Pubblica il podcast
     */
    public function publish(Publisher $publisher): void
    {
        $this->update(['publishing' => now()]);

        $publisher->publish($this);
    }
}
Usando una real-time facade non serve più passare esplicitamente un’istanza di Publisher, pur mantenendo la stessa testabilità. Per generare una real-time facade aggiungi il prefisso Facades al namespace della classe da importare.
<?php

namespace App\Models;

use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * Pubblica il podcast
     */
    public function publish(): void
    {
        $this->update(['publishing' => now()]);

        Publisher::publish($this);
    }
}
Quando si usa una real-time facade, l’implementazione del publisher viene risolta dal service container usando la parte di interfaccia o classe che appare dopo il prefisso Facades.
<?php

use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()->use(RefreshDatabase::class);

test('podcast can be published', function () {
    $podcast = Podcast::factory()->create();

    Publisher::shouldReceive('publish')->once()->with($podcast);

    $podcast->publish();
});
Le real-time facade sono comode quando vuoi eliminare la necessità di passare l’oggetto come argomento pur semplificando la creazione dei mock nei test.

Testare le facade

Per testare le facade usa il metodo shouldReceive, che restituisce un’istanza mock di Mockery. Poiché le facade sono realmente risolte e gestite dal service container, sono molto più testabili delle classi statiche tradizionali.
use Illuminate\Support\Facades\Cache;

test('mostra il profilo utente', function () {
    Cache::shouldReceive('get')
        ->once()
        ->with('user:1')
        ->andReturn(['name' => 'Taylor']);

    $response = $this->get('/users/1');

    $response->assertSee('Taylor');
});
I metodi di mock più comuni sono i seguenti.
MetodoDescrizione
shouldReceive('method')Ci si aspetta che il metodo venga chiamato
once()Ci si aspetta che venga chiamato una sola volta
times(n)Ci si aspetta che venga chiamato n volte
with(args)Ci si aspetta che venga chiamato con determinati argomenti
andReturn(value)Restituisce il valore indicato
andReturnNull()Restituisce null

Elenco delle facade principali

Tabella di corrispondenza tra le facade più usate, la classe reale e il nome del binding nel service container.
FacadeClasseBinding
AppIlluminate\Foundation\Applicationapp
AuthIlluminate\Auth\AuthManagerauth
CacheIlluminate\Cache\CacheManagercache
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate
HashIlluminate\Contracts\Hashing\Hasherhash
HttpIlluminate\Http\Client\Factory
LogIlluminate\Log\LogManagerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager
QueueIlluminate\Queue\QueueManagerqueue
RateLimiterIlluminate\Cache\RateLimiter
RedirectIlluminate\Routing\Redirectorredirect
RequestIlluminate\Http\Requestrequest
RouteIlluminate\Routing\Routerrouter
SchemaIlluminate\Database\Schema\Builder
SessionIlluminate\Session\SessionManagersession
StorageIlluminate\Filesystem\FilesystemManagerfilesystem
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
ViewIlluminate\View\Factoryview

Prossimi passi

Contracts (contratti)

Impara i concetti dei Contracts, controparte delle facade, e quando preferire l’uno o l’altro.
Ultima modifica il 13 luglio 2026