跳到主要內容

外觀

📝 此頁面為 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 功能的互動變得更加容易。您可能互動的一些常見輔助函數包括 viewresponseurlconfig 等。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 文檔。服務容器綁定鍵也已在適用的地方包含。

外觀 類別 服務容器綁定
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth (實例) Illuminate\Contracts\Auth\Guard auth.driver
Auth Illuminate\Auth\AuthManager auth
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast (實例) Illuminate\Contracts\Broadcasting\Broadcaster  
Broadcast Illuminate\Contracts\Broadcasting\Factory  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache (實例) Illuminate\Cache\Repository cache.store
Cache Illuminate\Cache\CacheManager cache
Config Illuminate\Config\Repository config
Context Illuminate\Log\Context\Repository  
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
Date Illuminate\Support\DateFactory date
DB (實例) Illuminate\Database\Connection db.connection
DB Illuminate\Database\DatabaseManager db
Event Illuminate\Events\Dispatcher events
Exceptions (實例) Illuminate\Contracts\Debug\ExceptionHandler  
Exceptions Illuminate\Foundation\Exceptions\Handler  
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Http Illuminate\Http\Client\Factory  
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password (實例) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Pipeline (實例) Illuminate\Pipeline\Pipeline  
Process Illuminate\Process\Factory  
Queue (基底類別) Illuminate\Queue\Queue  
Queue (實例) Illuminate\Contracts\Queue\Queue queue.connection
Queue Illuminate\Queue\QueueManager queue
RateLimiter Illuminate\Cache\RateLimiter  
Redirect Illuminate\Routing\Redirector redirect
Redis (實例) Illuminate\Redis\Connections\Connection redis.connection
Redis Illuminate\Redis\RedisManager redis
Request Illuminate\Http\Request request
Response (實例) Illuminate\Http\Response  
Response Illuminate\Contracts\Routing\ResponseFactory  
Route Illuminate\Routing\Router router
Schedule Illuminate\Console\Scheduling\Schedule  
Schema Illuminate\Database\Schema\Builder  
Session (實例) Illuminate\Session\Store session.store
Session Illuminate\Session\SessionManager session
Storage (實例) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
Storage Illuminate\Filesystem\FilesystemManager filesystem
URL Illuminate\Routing\UrlGenerator url
Validator (實例) Illuminate\Validation\Validator  
Validator Illuminate\Validation\Factory validator
View (實例) Illuminate\View\View  
View Illuminate\View\Factory view
Vite Illuminate\Foundation\Vite