Laravel Cafè #2 - Dietro le Facade

Laravel Cafè N.2 - 2 Novembre 2016 - Filippo ci parla delle Facade, di come funzionano e di come crearne una.
francesco
Filippo Galante
02/11/2016 in Tutorial


Benvenuto al Laravel Cafè! Ogni settimana proporremo un nuovo argomento sul mondo Laravel, quindi trova un posto libero, prendi un caffè e condividi le tue opinioni con la comunità! L'idea è di creare un punto di discussione. Se hai qualche perplessità sull'argomento trattato, leggi fino alla fine e fai una domanda usando il forum! Cercheremo di risponderci a vicenda e di aiutarci, ed il confronto ci farà crescere tutti un po' di più.

Di mercoledì in mercoledì parleremo di qualcosa di diverso, quindi tornaci a trovare! Potresti dare una mano a qualcuno in difficoltà, o ricevere tu un aiuto in caso di problemi! Dai, siediti, il primo caffè lo offriamo noi.

L'Argomento

Laravel e le Facade... La prima cosa che ho pensato è "ok, come si legge?" Ecco, adesso lo sai!

Possiamo quindi passare alla parte di teoria: direi di partire da quanto riportato nella documentazione ufficiale.

Facades provide a "static" interface to classes that are available in the application's service container. Laravel ships with many facades which provide access to almost all of Laravel's features. Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

Traduco per tutti:

Le Facade forniscono un'interfaccia "statica" alle classi che sono disponibili nel service container dell'applicazione. Laravel è fornito di molte facade che forniscono accesso praticamente a tutte le funzionalità dell'applicazione. Le facade di Laravel agiscono da "proxy statici" per sottolineare classi nel service container, fornendo i benefit di una sintassi concisa ed espressiva mantenendo nel mentre maggiore testabilità e flessibilità dei tradizionali metodi statici.

Per rendere il tutto più semplice, partirei da questo schema, che spiega molto bene l'idea generale: Facade.

Fondamentalmente il container dell'applicazione attraverso le Facade ci peremente di utilizzare metodi di classi più complesse richiamando però un singolo metodo statico. Laravel utilizza massivamente le facade (richiamate in alcuni casi anche dagli helper del framework). Un esempio classico può essere il seguente:

Route::get([...]);

Instanziamo tramite il metodo statico get() una nuova rotta, in maniera semplice e rapida, ma quanto andiamo a richiamare è ben più complesso! Per chi volesse approfondire l'argomento consiglio questo articolo che spiega molto bene il "dietro le quinte". Ehi, è in inglese!

Il Codice

Svilupperemo l'esempio con la versione 5.3 di Laravel, ma qualunque versione a partire dalla 5.0 non presenta incompatibilità con la procedura applicata. Partiamo dal filesystem che contiene la struttura base utilizzata per questo episodio di Laravel cafè:

app/
 |-- Facades/                       <-- Cartella base delle facade
     |-- QuoteFacade.php            <-- La nostra facade d'esempio
 |-- Utilities/                     <-- Cartella base delle classi implementate dalle facade
     |-- Quote.php                  <-- La vera implementazione della facade
 |-- Providers/                     <-- Cartella base dei providers dell'applicazione
     |-- QuoteServiceProvider.php   <-- Il service provider della classe Quote

Come potete intuire, il tutto si traduce in una classe che generi delle quotazioni randomiche del famoso meme "Be Like Bill" da inglobare all'interno di una view. In verità questo esempio è abbastanza fine a sé stesso... però dai, a chi non piace "Be Like Bill"?

P.S.: Just for fun, Potrete trovare i sorgenti su github e provare qui il billgen.

Partiamo esaminando la classe Quote.php

<?php

namespace App\Utilities;

class Quote
{

    /**
     * Get the bill quote image tag.
     *
     * @param string $name
     * @param string $sex
     * @param array $img_attributes
     * @return string
     */
    public function getBillQuote($name = null, $sex = null, $img_attributes = [])
    {
        $url = 'http://belikebill.azurewebsites.net/billgen-API.php?default=1';
        $url .= $name !== null ? '&name=' . $name : '';
        $url .= $sex !== null ? '&sex=' . $sex : '';

        $image = '<img src="' . $url . '"';
        foreach ($img_attributes as $key => $val) {
            $image .= ' ' . $key . '="' . $val . '"';
        }
        $image .= ' />';

        return $url;
    }
}

Creiamo quindi l'apposito service provider che ci permette di registrare la classe Quote e poterla quindi aggiungere all'interno del contenitore dell'applicazione. Ci basta lanciare il comando

php artisan make:provider QuoteServiceProvider

Una volta creata la classe, ci basta modificare il metodo register per potere effettuare il bind della classe Quote.

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class QuoteServiceProvider extends ServiceProvider
{

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        app()->bind('quote', function() {
            return new \App\Utilities\Quote;
        });
    }
}

Per ora abbiamo solamente "inserito un oggetto in uno scatolone", dobbiamo far si che questo "oggetto" venga aggiunto al container dell'applicazione per poter essere riutilizzato quando necessario. Dobbiamo quindi modificare il file config/app.php, aggiungendolo all'array providers la classe appena creata.

    'providers' => [
        [...]
        
        /*
         * Quote Service Providers...
         */
        App\Providers\QuoteServiceProvider::class,
    ],

Ora l'applicazione ci permette di richiamare la classe Quote: tuttavia, manca ancora il bind alla sua facade. Come abbiamo visto prima nello schema del filesystem, dobbiamo creare la classe App\Facades\QuoteFacade.php che conterrà il seguente codice:

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * Quote facade
 *
 * @author ilgala
 */
class QuoteFacade extends Facade
{

    protected static function getFacadeAccessor()
    {
        return 'quote'; // IoC bind
    }

}

Ce l'abbiamo fatta! Neanche il tempo di finire il caffè che abbiamo già a disposizione tutto quello che ci serve per poter richiamare staticamente la nostra classe Quote. Possiamo dunque richiamare la nostra facade importandola nelle classi di nostro interesse utilizzando un semplice

use App\Facades\QuoteFacade as Quote

oppure aggiungendo la nostra facade all'array degli alias del file config/app.php

    'aliases' => [
        [...]
        'Quote' => App\Facades\QuoteFacade::class,
    ],

e richiamando (dove necessario) un semplice use Quote;. Questa seconda soluzione la preferisco, anche perché (a parte in questo esempio) il più delle volte genero delle facade per poter richiamare metodi ridondanti all'interno di controller o di viste, quindi avere un alias per non dover richiamare la Facade risulta essere particolarmente utile.

Infine, per far si che le nostre classi vengano caricate automaticamente dall'applicazione, rendendo operativa al 100% la facade appena creata, vanno lanciati i comandi

composer dump-autoload

// oppure

php artisan clear-compiled
php artisan optimize

Il Test

Per provare l'effettivo funzionamento della facade, ci basta creare una rotta apposita

Route::get('/quote', function() {
    return Quote::getBillQuote();
});

Se tutto è andato a buon fine, il risultato finale che vedremo all'interno della nostra view è una vignetta random del meme "Be Like Bill".

... ed ora?

Vi siete mai creati delle Facade custom? Per cosa le utilizzate?

Molti dicono che le facade siano un pattern pericoloso e, a lungo termine, persino dannoso per l'applicazione, siete della stessa idea? Spero che la discussione continui in tutti i canali di Laravel Italia!

Fateci sapere cosa ne pensate, e non dimenticate che c'è sempre il nostro bellissimo Slack, pieno di gente che discute del framework più bello del mondo!