外觀
📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本
外觀
簡介
在整個 Laravel 文檔中,您會看到透過「外觀」與 Laravel 功能互動的代碼範例。外觀為應用程式服務容器中可用的類別提供了一個「靜態」介面。Laravel 附帶許多外觀,提供了對幾乎所有 Laravel 功能的存取。
Laravel 外觀作為服務容器中底層類別的「靜態代理」,提供了簡潔、富有表達力的語法的好處,同時保持了比傳統靜態方法更好的可測試性和靈活性。如果您不完全了解外觀如何運作也沒關係——只需隨波逐流,繼續學習 Laravel。
所有 Laravel 外觀都定義在 Illuminate\Support\Facades 命名空間中。因此,我們可以輕鬆地像這樣存取外觀:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
在整個 Laravel 文檔中,許多範例將使用外觀來演示框架的各種功能。
輔助函數
為了補充外觀,Laravel 提供了多種全域「輔助函數」,使與常見 Laravel 功能的互動變得更加容易。您可能互動的一些常見輔助函數包括 view、response、url、config 等。Laravel 提供的每個輔助函數都在其對應的功能中記錄;但是,完整的列表可在專用的輔助文檔中找到。
例如,與其使用 Illuminate\Support\Facades\Response 外觀來生成 JSON 回應,我們可以簡單地使用 response 函數。由於輔助函數是全域可用的,因此您不需要匯入任何類別即可使用它們:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
何時使用外觀
外觀有許多好處。它們提供了簡潔、易記的語法,允許您使用 Laravel 的功能而無需記住必須手動注入或配置的長類別名稱。此外,由於它們對 PHP 動態方法的獨特使用,它們很容易測試。
但是,使用外觀時必須小心。外觀的主要危險是類別「範圍蔓延」。由於外觀非常容易使用且不需要注入,因此很容易讓您的類別繼續增長並在單個類別中使用許多外觀。使用依賴注入時,大型構造函數提供的視覺回饋可以緩解這種可能性,讓您知道您的類別增長過大。因此,使用外觀時,請特別注意類別的大小,以便其職責範圍保持狹窄。如果您的類別變得太大,請考慮將其拆分為多個較小的類別。
外觀 vs. 依賴注入
依賴注入的主要好處之一是能夠交換注入類別的實現。這在測試時很有用,因為您可以注入一個模擬或存根,並斷言在存根上呼叫了各種方法。
通常,不可能模擬或存根真正的靜態類別方法。但是,由於外觀使用動態方法將方法呼叫代理到從服務容器解析的物件,因此我們實際上可以像測試注入的類別實例一樣測試外觀。例如,給定以下路由:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
使用 Laravel 的外觀測試方法,我們可以編寫以下測試來驗證 Cache::get 方法是否以我們預期的參數被呼叫:
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;
/**
* A basic functional test example.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
外觀 vs. 輔助函數
除了外觀外,Laravel 還包含多種可以執行常見任務(如生成視圖、觸發事件、派發作業或發送 HTTP 回應)的「輔助」函數。這些輔助函數中有許多執行與相應外觀相同的功能。例如,以下外觀呼叫和輔助呼叫是等效的:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
外觀和輔助函數之間 absolutely 沒有實際差異。使用輔助函數時,您仍然可以完全按照對相應外觀的測試方式來測試它們。例如,給定以下路由:
Route::get('/cache', function () {
return cache('key');
});
cache 輔助函數將呼叫 Cache 外觀底層類別上的 get 方法。因此,即使我們正在使用輔助函數,我們也可以編寫以下測試來驗證該方法是否以我們預期的參數被呼叫:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
外觀如何運作
在 Laravel 應用程式中,外觀是一個提供從容器存取物件的類別。實現此功能的機制在 Facade 類別中。Laravel 的外觀以及您建立的任何自訂外觀都將擴展底層 Illuminate\Support\Facades\Facade 類別。
Facade 基類利用 __callStatic() 魔術方法將外觀的呼叫延遲到從容器解析的物件。在下面的範例中,對 Laravel 快取系統進行了呼叫。瞥一眼這段代碼,可能會假設正在 Cache 類別上呼叫靜態 get 方法:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* 顯示給定使用者的個人資料。
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
請注意,在檔案頂部附近我們「匯入」了 Cache 外觀。此外觀作為存取 Illuminate\Contracts\Cache\Factory 介面底層實現的代理。我們使用外觀進行的任何呼叫都將被傳遞給 Laravel 快取服務的底層實例。
如果我們查看 Illuminate\Support\Facades\Cache 類別,您會看到沒有靜態方法 get:
class Cache extends Facade
{
/**
* 獲取已註冊的元件名稱。
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}
相反,Cache 外觀擴展了基底 Facade 類別並定義了 getFacadeAccessor() 方法。此方法的工作是返回服務容器綁定的名稱。當使用者引用 Cache 外觀上的任何靜態方法時,Laravel 從服務容器解析 cache 綁定,並對該物件執行請求的方法(在這種情況下是 get)。
即時外觀
使用即時外觀,您可以將應用程式中的任何類別視為外觀。為了說明如何使用它,讓我們首先檢查一些不使用即時外觀的代碼。例如,假設我們的 Podcast 模型有一個 publish 方法。但是,為了發布播客,我們需要注入一個 Publisher 實例:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* 發布播客。
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
將發布者實現注入到方法中允許我們輕鬆地單獨測試該方法,因為我們可以模擬注入的發布者。但是,它要求我們每次呼叫 publish 方法時都傳遞一個發布者實例。使用即時外觀,我們可以保持相同的可測試性,而無需明確傳遞 Publisher 實例。要生成即時外觀,請在匯入的類別的命名空間前添加 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
{
/**
* 發布播客。
*/
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]
}
}
使用即時外觀時,發布者實現將使用 Facades 前綴之後出現的介面或類別名稱部分從服務容器中解析。測試時,我們可以使用 Laravel 的內建外觀測試輔助工具來模擬此方法呼叫:
<?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;
/**
* A test example.
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
外觀類別參考
在下面,您將找到每個外觀及其底層類別。這是一個有用的工具,可以快速深入了解給定外觀根的 API 文檔。服務容器綁定鍵也已在適用的地方包含。