- Introducción
- Cuándo utilizar facades
- Cómo funcionan los facades
- Facades en tiempo real
- Referencia de la clase Facade
#Introducción
A lo largo de la documentación de Laravel, verá ejemplos de código que interactúan con las características de Laravel mediante "facades". Los facades proporcionan una interfaz "estática" a las clases disponibles en el contenedor de servicios de la aplicación. Laravel incluye muchos facades que brindan acceso a casi todas las funcionalidades del framework.
Los facades de Laravel actúan como "proxies estáticos" hacia las clases subyacentes en el contenedor de servicios, ofreciendo la ventaja de una sintaxis concisa y expresiva, mientras mantienen mayor capacidad de prueba y flexibilidad que los métodos estáticos tradicionales. No es necesario entender completamente cómo funcionan los facades para usarlos; simplemente siga aprendiendo sobre Laravel.
Todos los facades de Laravel están definidos en el espacio de nombres Illuminate\Support\Facades. Por lo tanto, podemos acceder fácilmente a un facade de la siguiente manera:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
En la documentación de Laravel, muchos ejemplos utilizan facades para demostrar diversas funcionalidades del framework.
#Funciones helper
Para complementar los facades, Laravel ofrece una variedad de funciones globales "helper" que facilitan aún más la interacción con características comunes de Laravel. Algunas funciones helper comunes son view, response, url, config y más. Cada función helper está documentada junto con su característica correspondiente; sin embargo, una lista completa está disponible en la documentación dedicada a helpers.
Por ejemplo, en lugar de usar el facade Illuminate\Support\Facades\Response para generar una respuesta JSON, podemos simplemente usar la función response. Como las funciones helper están disponibles globalmente, no es necesario importar ninguna clase para usarlas:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
#Cuándo utilizar facades
Los facades tienen muchas ventajas. Proporcionan una sintaxis concisa y fácil de recordar que permite usar las funcionalidades de Laravel sin tener que memorizar nombres largos de clases que deben ser inyectadas o configuradas manualmente. Además, gracias a su uso dinámico de los métodos de PHP, son fáciles de probar.
Sin embargo, se debe tener cuidado al usar facades. El principal riesgo es el "crecimiento descontrolado" de la clase. Dado que los facades son muy fáciles de usar y no requieren inyección, puede ser sencillo dejar que una clase crezca demasiado y utilice muchos facades en una sola clase. Con la inyección de dependencias, este riesgo se mitiga porque un constructor grande indica visualmente que la clase está creciendo demasiado. Por lo tanto, al usar facades, preste especial atención al tamaño de su clase para que su responsabilidad se mantenga limitada. Si la clase se vuelve demasiado grande, considere dividirla en varias clases más pequeñas.
#Facades vs. Inyección de dependencias
Una de las principales ventajas de la inyección de dependencias es la capacidad de intercambiar implementaciones de la clase inyectada. Esto es útil durante las pruebas, ya que puede inyectar un mock o stub y verificar que se llamaron varios métodos en el stub.
Normalmente, no sería posible simular o hacer stub de un método estático verdadero. Sin embargo, dado que los facades usan métodos dinámicos para redirigir las llamadas a objetos resueltos desde el contenedor de servicios, en realidad podemos probar los facades igual que probaríamos una instancia de clase inyectada. Por ejemplo, dada la siguiente ruta:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Usando los métodos de prueba para facades de Laravel, podemos escribir la siguiente prueba para verificar que el método Cache::get fue llamado con el argumento esperado:
use Illuminate\Support\Facades\Cache;
/**
* Un ejemplo básico de prueba funcional.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
#Facades vs. funciones helper
Además de los facades, Laravel incluye varias funciones "helper" que pueden realizar tareas comunes como generar vistas, disparar eventos, despachar jobs o enviar respuestas HTTP. Muchas de estas funciones helper realizan la misma función que un facade correspondiente. Por ejemplo, esta llamada a facade y esta llamada helper son equivalentes:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
No hay ninguna diferencia práctica entre facades y funciones helper. Al usar funciones helper, aún puede probarlas exactamente como probaría el facade correspondiente. Por ejemplo, dada la siguiente ruta:
Route::get('/cache', function () {
return cache('key');
});
La función helper cache llamará al método get en la clase subyacente al facade Cache. Por lo tanto, aunque usemos la función helper, podemos escribir la siguiente prueba para verificar que el método fue llamado con el argumento esperado:
use Illuminate\Support\Facades\Cache;
/**
* Un ejemplo básico de prueba funcional.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
#Cómo funcionan los facades
En una aplicación Laravel, un facade es una clase que proporciona acceso a un objeto del contenedor. El mecanismo que hace esto posible está en la clase Facade. Los facades de Laravel, y cualquier facade personalizado que cree, extenderán la clase base Illuminate\Support\Facades\Facade.
La clase base Facade utiliza el método mágico __callStatic() para delegar las llamadas desde su facade a un objeto resuelto desde el contenedor. En el ejemplo siguiente, se realiza una llamada al sistema de caché de Laravel. Al ver este código, uno podría asumir que se está llamando al método estático get en la clase Cache:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Mostrar el perfil del usuario dado.
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
Observe que cerca del inicio del archivo estamos "importando" el facade Cache. Este facade actúa como un proxy para acceder a la implementación subyacente de la interfaz Illuminate\Contracts\Cache\Factory. Cualquier llamada que hagamos usando el facade será pasada a la instancia subyacente del servicio de caché de Laravel.
Si miramos la clase Illuminate\Support\Facades\Cache, verá que no existe un método estático get:
class Cache extends Facade
{
/**
* Obtener el nombre registrado del componente.
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}
En cambio, el facade Cache extiende la clase base Facade y define el método getFacadeAccessor(). La función de este método es devolver el nombre de un binding en el contenedor de servicios. Cuando un usuario referencia cualquier método estático en el facade Cache, Laravel resuelve el binding cache desde el contenedor de servicios y ejecuta el método solicitado (en este caso, get) en ese objeto.
#Facades en tiempo real
Usando facades en tiempo real, puede tratar cualquier clase en su aplicación como si fuera un facade. Para ilustrar cómo se puede usar esto, primero examinemos un código que no usa facades en tiempo real. Por ejemplo, supongamos que nuestro modelo Podcast tiene un método publish. Sin embargo, para publicar el podcast, necesitamos inyectar una instancia de Publisher:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publicar el podcast.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
Inyectar una implementación de publisher en el método nos permite probarlo fácilmente en aislamiento, ya que podemos simular el publisher inyectado. Sin embargo, requiere que siempre pasemos una instancia de publisher cada vez que llamamos al método publish. Usando facades en tiempo real, podemos mantener la misma capacidad de prueba sin tener que pasar explícitamente una instancia de Publisher. Para generar un facade en tiempo real, anteponga el espacio de nombres de la clase importada con Facades:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publicar el podcast.
*/
public function publish(Publisher $publisher): void
public function publish(): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
Publisher::publish($this);
}
}
Cuando se usa el facade en tiempo real, la implementación del publisher se resolverá desde el contenedor de servicios usando la parte del nombre de la interfaz o clase que aparece después del prefijo Facades. Durante las pruebas, podemos usar los helpers integrados de Laravel para probar facades y simular esta llamada al método:
<?php
namespace Tests\Feature;
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* A test example.
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
#Referencia de la clase Facade
A continuación encontrará cada facade y su clase subyacente. Esta es una herramienta útil para explorar rápidamente la documentación API de un facade dado. También se incluye la clave del binding del contenedor de servicios cuando aplica.