- Wprowadzenie
- Kiedy używać fasad
- Jak działają fasady
- Fasady w czasie rzeczywistym
- Dokumentacja klas fasad
W całej dokumentacji Laravel zobaczysz przykłady kodu, który wchodzi w interakcję z funkcjami Laravel za pomocą "fasad". Fasady zapewniają interfejs "statyczny" do klas, które są dostępne w kontenerze usług aplikacji. Laravel dostarcza wiele fasad, które zapewniają dostęp do niemal wszystkich funkcji Laravel.
Fasady Laravel służą jako "statyczne proxy" do podstawowych klas w kontenerze usług, zapewniając korzyści zwięzłej, wyrazistej składni przy jednoczesnym zachowaniu większej testowalności i elastyczności niż tradycyjne metody statyczne. Jest całkowicie w porządku, jeśli nie do końca rozumiesz, jak działają fasady - po prostu płyń z nurtem i kontynuuj naukę o Laravel.
Wszystkie fasady Laravel są zdefiniowane w przestrzeni nazw Illuminate\Support\Facades. Możemy więc łatwo uzyskać dostęp do fasady w następujący sposób:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});W całej dokumentacji Laravel wiele przykładów będzie używać fasad do demonstrowania różnych funkcji frameworka.
Jako uzupełnienie fasad, Laravel oferuje różnorodne globalne "funkcje pomocnicze", które ułatwiają interakcję ze wspólnymi funkcjami Laravel. Niektóre z popularnych funkcji pomocniczych, z którymi możesz się zetknąć, to view, response, url, config i wiele innych. Każda funkcja pomocnicza oferowana przez Laravel jest udokumentowana wraz z odpowiednią funkcjonalnością; jednak pełna lista jest dostępna w dedykowanej dokumentacji pomocników.
Na przykład, zamiast używać fasady Illuminate\Support\Facades\Response do generowania odpowiedzi JSON, możemy po prostu użyć funkcji response. Ponieważ funkcje pomocnicze są dostępne globalnie, nie musisz importować żadnych klas, aby ich używać:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});Fasady mają wiele korzyści. Zapewniają zwięzłą, łatwą do zapamiętania składnię, która pozwala korzystać z funkcji Laravel bez zapamiętywania długich nazw klas, które muszą być wstrzykiwane lub konfigurowane ręcznie. Co więcej, dzięki ich unikalnemu wykorzystaniu dynamicznych metod PHP, są łatwe do testowania.
Jednak należy zachować ostrożność podczas używania fasad. Głównym zagrożeniem związanym z fasadami jest "rozrost zakresu" klasy. Ponieważ fasady są tak łatwe w użyciu i nie wymagają wstrzykiwania, może być łatwo pozwolić, aby klasy rozrastały się i używały wielu fasad w jednej klasie. Przy użyciu wstrzykiwania zależności, ten potencjał jest łagodzony przez wizualną informację zwrotną, jaką daje Ci duży konstruktor, że Twoja klasa staje się zbyt duża. Dlatego podczas używania fasad zwracaj szczególną uwagę na rozmiar swojej klasy, aby jej zakres odpowiedzialności pozostał wąski. Jeśli Twoja klasa staje się zbyt duża, rozważ podzielenie jej na wiele mniejszych klas.
Jedną z głównych korzyści wstrzykiwania zależności jest możliwość zamiany implementacji wstrzykiwanej klasy. Jest to przydatne podczas testowania, ponieważ możesz wstrzyknąć mock lub stub i potwierdzić, że różne metody zostały wywołane na stubie.
Zazwyczaj nie byłoby możliwe mockowanie lub stubowanie prawdziwie statycznej metody klasy. Jednakże, ponieważ fasady używają dynamicznych metod do przekierowywania wywołań metod do obiektów rozwiązanych z kontenera usług, możemy faktycznie testować fasady tak samo, jak testowalibyśmy instancję wstrzykiwanej klasy. Na przykład, mając następującą trasę:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});Używając metod testowania fasad Laravel, możemy napisać następujący test, aby zweryfikować, że metoda Cache::get została wywołana z oczekiwanym argumentem:
use Illuminate\Support\Facades\Cache;
test('basic example', function () {
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
});use Illuminate\Support\Facades\Cache;
/**
* Podstawowy przykład testu funkcjonalnego.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}Oprócz fasad, Laravel zawiera różnorodne funkcje "pomocnicze", które mogą wykonywać typowe zadania, takie jak generowanie widoków, wywoływanie zdarzeń, wysyłanie zadań lub wysyłanie odpowiedzi HTTP. Wiele z tych funkcji pomocniczych wykonuje tę samą funkcję co odpowiadająca im fasada. Na przykład, to wywołanie fasady i wywołanie helpera są równoważne:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');Nie ma absolutnie żadnej praktycznej różnicy między fasadami a funkcjami pomocniczymi. Podczas używania funkcji pomocniczych, nadal możesz je testować dokładnie tak samo, jak odpowiadającą im fasadę. Na przykład, mając następującą trasę:
Route::get('/cache', function () {
return cache('key');
});Helper cache będzie wywoływać metodę get na klasie bazowej fasady Cache. Więc, nawet jeśli używamy funkcji pomocniczej, możemy napisać następujący test, aby zweryfikować, że metoda została wywołana z oczekiwanym argumentem:
use Illuminate\Support\Facades\Cache;
/**
* Podstawowy przykład testu funkcjonalnego.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}W aplikacji Laravel fasada jest klasą, która zapewnia dostęp do obiektu z kontenera. Mechanizm, który sprawia, że to działa, znajduje się w klasie Facade. Fasady Laravel oraz wszelkie niestandardowe fasady, które utworzysz, będą rozszerzać klasę bazową Illuminate\Support\Facades\Facade.
Klasa bazowa Facade wykorzystuje magiczną metodę __callStatic() do odraczania wywołań z Twojej fasady do obiektu rozwiązanego z kontenera. W poniższym przykładzie dokonywane jest wywołanie do systemu cache Laravel. Patrząc na ten kod, można założyć, że statyczna metoda get jest wywoływana na klasie Cache:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Pokaż profil dla danego użytkownika.
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}Zauważ, że blisko góry pliku "importujemy" fasadę Cache. Ta fasada służy jako proxy do uzyskiwania dostępu do podstawowej implementacji interfejsu Illuminate\Contracts\Cache\Factory. Wszelkie wywołania, które wykonujemy za pomocą fasady, zostaną przekazane do podstawowej instancji usługi cache Laravel.
Jeśli spojrzymy na klasę Illuminate\Support\Facades\Cache, zobaczysz, że nie ma tam statycznej metody get:
class Cache extends Facade
{
/**
* Pobierz zarejestrowaną nazwę komponentu.
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}Zamiast tego, fasada Cache rozszerza klasę bazową Facade i definiuje metodę getFacadeAccessor(). Zadaniem tej metody jest zwrócenie nazwy wiązania kontenera usług. Gdy użytkownik odwołuje się do dowolnej statycznej metody fasady Cache, Laravel rozwiązuje wiązanie cache z kontenera usług i uruchamia żądaną metodę (w tym przypadku get) na tym obiekcie.
Używając fasad w czasie rzeczywistym, możesz traktować dowolną klasę w swojej aplikacji tak, jakby była fasadą. Aby zilustrować, jak można to wykorzystać, najpierw zbadajmy kod, który nie wykorzystuje fasad czasu rzeczywistego. Na przykład załóżmy, że nasz model Podcast ma metodę publish. Jednak, aby opublikować podcast, musimy wstrzyknąć instancję Publisher:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Opublikuj podcast.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}Wstrzyknięcie implementacji publishera do metody pozwala nam łatwo testować metodę w izolacji, ponieważ możemy zmockować wstrzykniętego publishera. Jednak wymaga to, aby zawsze przekazywać instancję publishera za każdym razem, gdy wywołujemy metodę publish. Używając fasad w czasie rzeczywistym, możemy utrzymać tę samą testowalność bez konieczności jawnego przekazywania instancji Publisher. Aby wygenerować fasadę w czasie rzeczywistym, przedrostek przestrzeni nazw zaimportowanej klasy z Facades:
<?php
namespace App\Models;
use App\Contracts\Publisher; // [tl! remove]
use Facades\App\Contracts\Publisher; // [tl! add]
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Opublikuj podcast.
*/
public function publish(Publisher $publisher): void // [tl! remove]
public function publish(): void // [tl! add]
{
$this->update(['publishing' => now()]);
$publisher->publish($this); // [tl! remove]
Publisher::publish($this); // [tl! add]
}
}Gdy używana jest fasada w czasie rzeczywistym, implementacja publishera zostanie rozwiązana z kontenera usług przy użyciu części interfejsu lub nazwy klasy, która pojawia się po prefiksie Facades. Podczas testowania możemy użyć wbudowanych w Laravel helperów testowych dla fasad, aby zmockować to wywołanie metody:
<?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();
});<?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;
/**
* Przykład testu.
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}Poniżej znajdziesz każdą fasadę i jej podstawową klasę. Jest to przydatne narzędzie do szybkiego zagłębiania się w dokumentację API dla danego źródła fasady. Klucz wiązania kontenera usług jest również uwzględniony tam, gdzie ma to zastosowanie.