LaravelDocs(中文)

門面 (Facades)

為應用程式的服務容器(Service Container)中的類別提供一個「靜態」存取介面。

簡介 (Introduction)

在整個 Laravel 文件中,您將看到透過「Facades」與 Laravel 功能互動的程式碼範例。Facades 為應用程式的 Service Container 中可用的類別提供「靜態」介面。Laravel 附帶了許多 Facades,可以存取幾乎所有的 Laravel 功能。

Laravel Facades 作為 Service Container 中底層類別的「靜態代理」,提供了簡潔、富有表現力的語法的好處,同時保持了比傳統靜態方法更高的可測試性和靈活性。如果您不完全了解 Facades 的運作方式也沒關係 - 順其自然,繼續學習 Laravel。

所有 Laravel 的 Facades 都在 Illuminate\Support\Facades 命名空間中定義。因此,我們可以像這樣輕鬆地存取 Facade:

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/cache', function () {
    return Cache::get('key');
});

在整個 Laravel 文件中,許多範例將使用 Facades 來示範框架的各種功能。

Helper 函式 (Helper Functions)

為了補充 Facades,Laravel 提供了各種全域「Helper 函式」,使與常見 Laravel 功能的互動變得更加容易。您可能互動的一些常見 Helper 函式包括 viewresponseurlconfig 等等。Laravel 提供的每個 Helper 函式都有相應功能的說明文件;但是,完整的清單可在專用的 Helper 文件 中找到。

例如,我們可以直接使用 response 函式,而不是使用 Illuminate\Support\Facades\Response Facade 來生成 JSON 回應。因為 Helper 函式是全域可用的,所以您不需要匯入任何類別即可使用它們:

use Illuminate\Support\Facades\Response;

Route::get('/users', function () {
    return Response::json([
        // ...
    ]);
});

Route::get('/users', function () {
    return response()->json([
        // ...
    ]);
});

何時使用 Facades (When To Use Facades)

Facades 有很多好處。它們提供了簡潔、易記的語法,讓您可以使用 Laravel 的功能,而無需記住必須手動注入或設定的長類別名稱。此外,由於它們獨特地使用了 PHP 的動態方法,因此它們很容易測試。

但是,使用 Facades 時必須小心。Facades 的主要危險是類別「範圍蔓延 (scope creep)」。由於 Facades 非常易於使用且不需要注入,因此很容易讓您的類別繼續增長並在單個類別中使用許多 Facades。使用 Dependency Injection 時,大型建構子給您的視覺回饋減輕了這種潛在問題,表明您的類別正在變得過大。因此,在使用 Facades 時,請特別注意類別的大小,以便其職責範圍保持狹窄。如果您的類別變得太大,請考慮將其拆分為多個較小的類別。

Facades 與 Dependency Injection (Facades Vs Dependency Injection)

Dependency Injection 的主要好處之一是能夠交換注入類別的實作。這在測試期間很有用,因為您可以注入模擬 (mock) 或存根 (stub) 並斷言在存根上呼叫了各種方法。

通常,不可能模擬或存根真正的靜態類別方法。但是,由於 Facades 使用動態方法將方法呼叫代理到從 Service Container 解析的物件,因此我們實際上可以像測試注入的類別實例一樣測試 Facades。例如,給定以下路由:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

使用 Laravel 的 Facade 測試方法,我們可以編寫以下測試來驗證 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');
}

Facades 與 Helper 函式 (Facades Vs Helper Functions)

除了 Facades 之外,Laravel 還包含各種「Helper」函式,可以執行常見任務,如生成 Views、觸發 Events、調度 Jobs 或發送 HTTP 回應。許多這些 Helper 函式執行與相應 Facade 相同的功能。例如,此 Facade 呼叫和 Helper 呼叫是等效的:

return Illuminate\Support\Facades\View::make('profile');

return view('profile');

Facades 和 Helper 函式之間絕對沒有實際區別。使用 Helper 函式時,您仍然可以像測試相應 Facade 一樣測試它們。例如,給定以下路由:

Route::get('/cache', function () {
    return cache('key');
});

cache Helper 將呼叫 Cache Facade 底層類別上的 get 方法。因此,即使我們使用 Helper 函式,我們也可以編寫以下測試來驗證該方法是否使用我們預期的參數被呼叫:

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');
}

Facades 如何運作 (How Facades Work)

在 Laravel 應用程式中,Facade 是一個提供從 Container 存取物件的類別。使這項工作得以進行的機制在 Facade 類別中。Laravel 的 Facades 以及您建立的任何自訂 Facades 都將繼承基礎 Illuminate\Support\Facades\Facade 類別。

Facade 基礎類別利用 __callStatic() 魔術方法將來自 Facade 的呼叫延遲到從 Container 解析的物件。在下面的範例中,對 Laravel 快取系統進行了呼叫。乍看之下,人們可能會認為正在呼叫 Cache 類別上的靜態 get 方法:

<?php

namespace App\Http\Controllers;

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]);
    }
}

請注意,在檔案頂部附近,我們正在「匯入」Cache Facade。此 Facade 作為存取 Illuminate\Contracts\Cache\Factory 介面底層實作的代理。我們使用 Facade 進行的任何呼叫都將傳遞給 Laravel 快取服務的底層實例。

如果我們查看該 Illuminate\Support\Facades\Cache 類別,您會發現沒有靜態方法 get

class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}

相反,Cache Facade 繼承了基礎 Facade 類別並定義了方法 getFacadeAccessor()。此方法的工作是返回 Service Container 綁定的名稱。當使用者參考 Cache Facade 上的任何靜態方法時,Laravel 會從 Service Container 解析 cache 綁定,並針對該物件執行請求的方法(在本例中為 get)。

即時 Facades (Real Time Facades)

使用即時 Facades,您可以將應用程式中的任何類別視為 Facade。為了說明如何使用它,讓我們先檢查一些不使用即時 Facades 的程式碼。例如,假設我們的 Podcast Model 有一個 publish 方法。但是,為了發布 Podcast,我們需要注入一個 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);
    }
}

將 Publisher 實作注入到方法中允許我們輕鬆地隔離測試該方法,因為我們可以模擬注入的 Publisher。但是,它要求我們每次呼叫 publish 方法時都必須傳遞一個 Publisher 實例。使用即時 Facades,我們可以保持相同的可測試性,而無需明確傳遞 Publisher 實例。要生成即時 Facade,請在匯入類別的命名空間前加上 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
{
    /**
     * Publish the 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]
    }
}

使用即時 Facade 時,將使用 Facades 前綴之後出現的介面或類別名稱部分從 Service Container 中解析 Publisher 實作。測試時,我們可以使用 Laravel 內建的 Facade 測試 Helpers 來模擬此方法呼叫:

<?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();
    }
}

Facade 類別參考 (Facade Class Reference)

在下面,您將找到每個 Facade 及其底層類別。這是一個用於快速深入了解給定 Facade root 的 API 文件的有用工具。Service Container 綁定 鍵也包含在適用的地方。

Facade類別

Service Container 綁定

App

Illuminate\Foundation\Application

app
Artisan

Illuminate\Contracts\Console\Kernel

artisan
Auth (Instance)

Illuminate\Contracts\Auth\Guard

auth.driver
Auth

Illuminate\Auth\AuthManager

auth
Blade

Illuminate\View\Compilers\BladeCompiler

blade.compiler
Broadcast (Instance)

Illuminate\Contracts\Broadcasting\Broadcaster

Broadcast

Illuminate\Contracts\Broadcasting\Factory

Bus

Illuminate\Contracts\Bus\Dispatcher

Cache (Instance)

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 (Instance)

Illuminate\Database\Connection

db.connection
DB

Illuminate\Database\DatabaseManager

db
Event

Illuminate\Events\Dispatcher

events
Exceptions (Instance)

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 (Instance)

Illuminate\Auth\Passwords\PasswordBroker

auth.password.broker
Password

Illuminate\Auth\Passwords\PasswordBrokerManager

auth.password
Pipeline (Instance)

Illuminate\Pipeline\Pipeline

Process

Illuminate\Process\Factory

Queue (Base Class)

Illuminate\Queue\Queue

Queue (Instance)

Illuminate\Contracts\Queue\Queue

queue.connection
Queue

Illuminate\Queue\QueueManager

queue
RateLimiter

Illuminate\Cache\RateLimiter

Redirect

Illuminate\Routing\Redirector

redirect
Redis (Instance)

Illuminate\Redis\Connections\Connection

redis.connection
Redis

Illuminate\Redis\RedisManager

redis
Request

Illuminate\Http\Request

request
Response (Instance)

Illuminate\Http\Response

Response

Illuminate\Contracts\Routing\ResponseFactory

Route

Illuminate\Routing\Router

router
Schedule

Illuminate\Console\Scheduling\Schedule

Schema

Illuminate\Database\Schema\Builder

Session (Instance)

Illuminate\Session\Store

session.store
Session

Illuminate\Session\SessionManager

session
Storage (Instance)

Illuminate\Contracts\Filesystem\Filesystem

filesystem.disk
Storage

Illuminate\Filesystem\FilesystemManager

filesystem
URL

Illuminate\Routing\UrlGenerator

url
Validator (Instance)

Illuminate\Validation\Validator

Validator

Illuminate\Validation\Factory

validator
View (Instance)

Illuminate\View\View

View

Illuminate\View\Factory

view
Vite

Illuminate\Foundation\Vite