Facades
Introdução
Por toda a documentação, você verá exemplos de código que interagem com os recursos por meio de "facades". As facades fornecem uma interface "estática" para classes que estão disponíveis no service container. O Laravel vem com muitas facades que fornecem acesso a quase todos os recursos do Laravel.
As facades do Laravel servem como "proxies estáticos" para classes subjacentes no service container, fornecendo o benefício de uma sintaxe concisa e expressiva, mantendo mais testabilidade e flexibilidade do que os métodos estáticos tradicionais. Está tudo bem se você não entender totalmente como as facades funcionam - apenas siga em frente e continue aprendendo sobre o Laravel.
Todas as facades do Laravel são definidas no namespace Illuminate\Support\Facades
. Portanto, podemos acessar uma facade facilmente assim:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
Por toda a documentação, muitos dos exemplos usarão facades para demonstrar vários recursos do framework.
Helper Functions
Para complementar as facades, o Laravel oferece uma variedade de "funções auxiliares" globais que tornam ainda mais fácil interagir com recursos comuns do Laravel. Algumas das funções auxiliares comuns com as quais você pode interagir são view
, response
, url
, config
e muito mais. Cada função auxiliar oferecida pelo Laravel é documentada com seu recurso correspondente; no entanto, uma lista completa está disponível na documentação dedicada sobre funções auxiliares.
Por exemplo, em vez de usar a facade Illuminate\Support\Facades\Response
para gerar uma resposta JSON, podemos simplesmente usar a função response
. Como as funções auxiliares estão globalmente disponíveis, você não precisa importar nenhuma classe para usá-las:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
Quando Utilizar Facades
As facades têm muitos benefícios. Elas fornecem uma sintaxe concisa e que permite que você use os recursos do Laravel sem precisar lembrar de nomes longos de classes que devem ser injetadas ou configuradas manualmente. Além disso, devido ao uso exclusivo dos métodos dinâmicos do PHP, elas são fáceis de testar.
No entanto, é preciso ter cuidado ao usar facades. O principal perigo das facades é o "escopo da classe". Como as facades são tão fáceis de usar e não requerem injeção, pode ser fácil deixar suas classes continuarem crescendo e usar muitas facades em uma única classe. Usando injeção de dependência, esse potencial é mitigado pelo feedback visual de um grande construtor que lhe dá a impressão de que sua classe está crescendo demais. Portanto, ao usar facades, preste atenção especial ao tamanho de sua classe para que seu escopo de responsabilidade permaneça estreito. Se sua classe estiver ficando muito grande, considere dividi-la em várias classes menores.
Facades vs. Injeção de Dependência
Um dos principais benefícios da injeção de dependência é a capacidade de trocar implementações da classe injetada. Isso é útil durante os testes, pois você pode injetar um mock ou stub e afirmar que vários métodos foram chamados no stub.
Normalmente, não seria possível mockar um método de classe verdadeiramente estático. No entanto, como as facades usam métodos dinâmicos para fazer chamadas de método a objetos resolvidos do service container, na verdade podemos testar facades da mesma forma que testaríamos uma instância de classe injetada. Por exemplo, dado o seguinte roteamento:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Usando os métodos de teste de facades, podemos escrever o seguinte teste para verificar se o método Cache::get
foi chamado com o argumento que esperávamos:
use Illuminate\Support\Facades\Cache;
test('exemplo básico', function () {
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
});
use Illuminate\Support\Facades\Cache;
/**
* Um exemplo básico de teste funcional.
*/
public function test_exemplo_basico(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
Facades vs. Helper Functions
Além das facades, o Laravel inclui uma variedade de "funções auxiliares" que podem realizar tarefas comuns, como gerar views, disparar eventos, disparar jobs ou enviar responses HTTP. Muitas dessas funções auxiliares realizam a mesma função que uma facade correspondente. Por exemplo, esta chamada de facade e chamada de função auxiliar são equivalentes:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
Na prática, não há diferença entre facades e funções auxiliares. Ao usar funções auxiliares, você ainda pode testá-las exatamente como faria com a facade correspondente. Por exemplo, dado o seguinte roteamento:
Route::get('/cache', function () {
return cache('key');
});
O helper cache
vai chamar o método get
na classe subjacente da facade Cache
. Portanto, mesmo que estejamos usando a função auxiliar, podemos escrever o seguinte teste para verificar se o método foi chamado com o argumento que esperávamos:
use Illuminate\Support\Facades\Cache;
/**
* Um exemplo básico de teste funcional.
*/
public function test_exemplo_basico(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
Como as Facades Funcionam
Em uma aplicação Laravel, uma facade é uma classe que fornece acesso a um objeto do container. O mecanismo que faz isso funcionar está na classe Facade
. As facades do Laravel, e quaisquer facades personalizadas que você criar, estenderão a classe base Illuminate\Support\Facades\Facade
.
A classe base Facade
faz uso do método mágico __callStatic()
para adiar chamadas de sua facade para um objeto resolvido do container. No exemplo abaixo, uma chamada é feita ao sistema de cache do Laravel. Ao olhar para este código, alguém poderia assumir que o método estático get
está sendo chamado na classe Cache
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
Observe que no início do arquivo estamos "importando" a facade Cache. Esta facade serve como um atalho para acessar a implementação subjacente da interface Illuminate\Contracts\Cache\Factory
. Qualquer chamada feita usando a facade será direcionada para a instância real do serviço de cache do Laravel.
Se olharmos para a classe Illuminate\Support\Facades\Cache
, veremos que não há um método estático get
:
class Cache extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}
Em vez disso, a facade Cache
estende a classe base Facade
e define o método getFacadeAccessor()
. A função deste método é retornar o nome de um binding do service container. Quando um usuário referencia qualquer método estático na facade Cache
, o Laravel resolve o binding cache
do service container e chama o método solicitado no objeto retornado.
Real-Time Facades
Usando real-time facades, você pode tratar qualquer classe em sua aplicação como se fosse uma facade. Para ilustrar como isso pode ser usado, vamos primeiro examinar algum código que não usa facades em tempo real. Por exemplo, vamos assumir que nosso model Podcast
tem um método publish
. No entanto, para publicar o podcast, precisamos injetar uma instância de Publisher
:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
Injetar uma implementação de publisher no método nos permite testar facilmente o método de forma isolada, pois podemos simular o publisher injetado. No entanto, isso requer que sempre passemos uma instância de Publisher
cada vez que chamamos o método publish
. Usando real-time facades, podemos manter a mesma testabilidade sem sermos obrigados a passar explicitamente uma instância de Publisher
. Para gerar uma real-time facade, prefixe o namespace da classe importada com Facades
:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*/
public function publish(Publisher $publisher): void
public function publish(): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
Publisher::publish($this);
}
}
Quando a real-time facade é usada, a implementação do publisher será resolvida do service container usando a parte da interface ou nome da classe que aparece após o prefixo Facades
. Ao testar, podemos usar os auxiliares de teste de facade integrados do Laravel para simular essa chamada de método:
<?php
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('podcast can be published', function () {
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
});
<?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();
}
}
Referência de Classes de Facade
Abaixo você encontrará todas as facades e suas classes subjacentes. Esta é uma ferramenta útil para mergulhar rapidamente na documentação da API para uma determinada raiz de facade. A chave de binding do service container também é incluída quando aplicável.