- Introducción
- Cuándo usar facades
- Cómo funcionan las facades
- Facades en tiempo real
- Referencia de clases de facades
Introducción
Las Facades proveen una interfaz «estática» a las clases disponibles en el contenedor de servicios de la aplicación. Laravel viene con numerosas facades, las cuales brindan acceso a casi todas las características de Laravel. Las facades de Laravel sirven como «proxies estáticas» a las clases subyacentes en el contenedor de servicios, brindando el beneficio de una sintaxis tersa y expresiva, manteniendo mayor verificabilidad y flexibilidad que los métodos estáticos tradicionales.
Todas las facades de Laravel se definen en el namespace Illuminate\Support\Facades
. Entonces, podemos fácilmente acceder a una facade de esta forma:
use Illuminate\Support\Facades\Cache; Route::get('/cache', function () { return Cache::get('key'); });
A través de la documentación de Laravel, muchos de los ejemplos usarán facades para demostrar varias características del framework.
Cuándo usar facades
Las Facades tienen múltiples beneficios. Brindan una sintaxis tersa y memorizable que permite utilizar las características de Laravel sin tener que recordar nombres de clase largos que deben ser inyectados o configurados manualmente. Además, debido a su uso único de los métodos dinámicos PHP, son fáciles de probar.
Sin embargo, deben guardarse ciertas precauciones al hacer uso de facades. El peligro principal de las facades es la corrupción de alcance de clases. Como las facades son tan fáciles de usar y no requieren inyección, puede resultar fácil dejar que tus clases sigan creciendo y usar muchas facades en una sola clase. Usando inyección de dependencias, este potencial es mitigado por la retroalimentación visual que un constructor grande te da cuando tu clase está creciendo demasiado. Entonces, al usar facades, pon especial atención al tamaño de tu clase para que su alcance de responsabilidades permanezca limitado.
Cuando se construye un paquete de terceros que interactúa con Laravel, es mejor inyectar contratos de Laravel en vez de usar facades. Como los paquetes son construidos fuera de Laravel, no tendrás acceso a las funciones (helpers) de testing para facades de Laravel.
Facades vs. inyección de dependencias
Uno de los principales beneficios de la inyección de dependencias es la habilidad de intercambiar implementaciones de la clase inyectada. Esto es útil durante las pruebas debido a que puedes inyectar un mock o un stub y comprobar que esos métodos son llamados en el stub.
Típicamente, no sería posible imitar (mock) o sustituir (stub) un método de clase verdaderamente estático. Sin embargo, como las facades utilizan métodos dinámicos para hacer proxy de llamadas de método a objetos resueltos desde el contenedor de servicios, podemos de hecho probar las facades exactamente cómo 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'); });
Podemos escribir la siguiente prueba para verificar que el método Cache::get
fue llamado con el argumento esperado:
use Illuminate\Support\Facades\Cache; /** * A basic functional test example. * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $this->visit('/cache') ->see('value'); }
Facades vs. funciones helper
Además de las facades, Laravel incluye una variedad de funciones «helper», las cuales pueden realizar tareas comunes como generar vistas, disparar eventos, despachar trabajos, o mandar respuestas HTTP. Muchas de estas funciones helper realizan la misma función que su facade correspondiente. Por ejemplo, éstas llamadas facade y helper son equivalentes:
return View::make('profile'); return view('profile');
No hay diferencia práctica en lo absoluto entre facades y funciones helper. Al usar funciones helper, aún se pueden probar como se probaría la facade correspondiente. Por ejemplo, dada la siguiente ruta:
Route::get('/cache', function () { return cache('key'); });
Bajo la superficie, el helper cache
llamará al método get
en la clase subyacente a la facade Cache
. Entonces, aún cuando estamos usando 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; /** * A basic functional test example. * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $this->visit('/cache') ->see('value'); }
Cómo funcionan las facades
En una aplicación Laravel, una facade es una clase que provee acceso a un objeto desde el contenedor. La maquinaria que hace este trabajo está en la clase Facade
. Las facades de Laravel y cualquier facade personalizada que crees, extenderá la clase base Illuminate\Support\Facades\Facade
.
La clase base Facade
hace uso del método mágico __callStatic()
para aplazar las llamadas desde tu facade a un objeto resuelto desde el contenedor. En el ejemplo siguiente, se realiza una llamada al sistema de caché de Laravel. Al mirar este código, se puede suponer que se llama 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; class UserController extends Controller { /** * Show the profile for the given user. * * @param int $id * @return Response */ public function showProfile($id) { $user = Cache::get('user:'.$id); return view('profile', ['user' => $user]); } }
Nótese que cerca del inicio del archivo estamos «importando» la clase facade Cache
. Esta facade sirve como proxy para acceder a la implementación subyacente de la interfaz Illuminate\Contracts\Cache\Factory
. Cualquier llamada que hagamos usando la facade será pasada a la instancia subyacente del servicio de caché de Laravel.
Si observamos la clase Illuminate\Support\Facades\Cache
, verás que no hay método estático get
:
class Cache extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'cache'; } }
En su lugar, la facade Cache
extiende la clase Facade
y define el método getFacadeAccessor()
. El trabajo de este método es devolver el nombre de un enlace de contenedor de servicios. Cuando un usuario referencia cualquier método estático en la facade Cache
, Laravel resuelve el enlace cache
desde el contenedor de servicios y ejecuta el método solicitado (en este caso, get
) contra ese objeto.
Facades en tiempo real
Usando facades en tiempo real, puedes tratar cualquier clase en tu aplicación como si fuera una facade. Para ilustrar cómo esto puede ser utilizado, examinemos una alternativa. Por ejemplo, asumamos que nuestro modelo Podcast
tiene un método publish
. Sin embargo, para publicar el podcast, necesitamos inyectar una instancia Publisher
:
<?php namespace App; use App\Contracts\Publisher; use Illuminate\Database\Eloquent\Model; class Podcast extends Model { /** * Publish the podcast. * * @param Publisher $publisher * @return void */ public function publish(Publisher $publisher) { $this->update(['publishing' => now()]); $publisher->publish($this); } }
Inyectar una implementación de publisher dentro del método nos permite probar fácilmente el método aislado porque podemos imitar (mock) el publisher inyectado. Sin embargo, requiere que pasemos una instancia publisher cada vez que llamamos al método publish
. Usando facades en tiempo real, podemos mantener la misma capacidad de hacer las pruebas sin que se requiera pasar explícitamente una instancia Publisher
. Para generar una facade en tiempo real, se añade el prefijo Facades
al namespace de la clase importada:
<?php namespace App; use Facades\App\Contracts\Publisher; use Illuminate\Database\Eloquent\Model; class Podcast extends Model { /** * Publish the podcast. * * @return void */ public function publish() { $this->update(['publishing' => now()]); Publisher::publish($this); } }
Cuando la facade en tiempo real es utilizada, la implementación publisher será resuelta en el contenedor de servicios usando la porción de la interfaz o nombre de clase que aparece después del prefijo Facades
. Al probar, podemos usar las funciones helpers de testing para facades integradas en Laravel para imitar (mock) esta llamada de método:
<?php namespace Tests\Feature; use App\Podcast; use Facades\App\Contracts\Publisher; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class PodcastTest extends TestCase { use RefreshDatabase; /** * A test example. * * @return void */ public function test_podcast_can_be_published() { $podcast = factory(Podcast::class)->create(); Publisher::shouldReceive('publish')->once()->with($podcast); $podcast->publish(); } }
Referencia de clases de facades
A continuación encontrarás cada facade y su clase subyacente. Esta es una herramienta útil para explorar rápidamente dentro de la documentación API para cualquier raíz de facade dada. La llave service container binding también ha sido incluida donde aplica.
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.
Lección anterior Proveedores de Servicios - Documentación de Laravel 6 Lección siguiente Contratos - Documentación de Laravel 6