Skip to content

Facades

Giới thiệu (Introduction)

Xuyên suốt tài liệu Laravel, bạn sẽ thấy code tương tác với các tính năng Laravel qua "facades". Facades cung cấp giao diện "static" cho các classes có sẵn trong service container. Laravel ships với nhiều facades cung cấp quyền truy cập hầu hết tính năng của Laravel.

Laravel facades đóng vai trò như "static proxies" cho các underlying classes trong service container, mang lại lợi ích của cú pháp ngắn gọn, biểu cảm trong khi duy trì khả năng testability và flexibility cao hơn so với static methods truyền thống.

Tất cả facades được định nghĩa trong namespace Illuminate\Support\Facades:

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

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

Helper Functions

Bổ sung cho facades, Laravel cung cấp nhiều "helper functions" toàn cục giúp tương tác dễ dàng hơn. Các helpers phổ biến bao gồm view, response, url, config, v.v.

Ví dụ, thay vì dùng Illuminate\Support\Facades\Response facade, bạn có thể đơn giản dùng function response. Vì helper functions có phạm vi toàn cục, bạn không cần import bất kỳ class nào:

php
use Illuminate\Support\Facades\Response;

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

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

Khi nào sử dụng Facades (When to Utilize Facades)

Facades có nhiều ưu điểm: cú pháp ngắn gọn, dễ nhớ, không cần nhớ tên class dài. Nhờ cách sử dụng PHP dynamic methods, chúng dễ test.

Tuy nhiên, cần cẩn thận. Nguy hiểm chính là "scope creep" (phình to phạm vi class). Vì facades dễ dùng, class có thể phình ra và sử dụng nhiều facades. Khi dùng dependency injection, constructor lớn cho bạn tín hiệu trực quan rằng class đang phình. Vì vậy, khi dùng facades, hãy chú ý kích thước class để phạm vi trách nhiệm luôn hẹp.

GỢI Ý

Nếu class phình quá lớn, cân nhắc chia thành nhiều classes nhỏ hơn.

Facades so với Dependency Injection

Một lợi ích chính của dependency injection là khả năng swap implementations. Điều này hữu ích khi testing vì bạn có thể inject mock hoặc stub.

Thông thường, không thể mock static method. Nhưng vì facades dùng dynamic methods để proxy method calls đến objects trong service container, chúng ta thực sự có thể test facades như test injected class instance:

php
use Illuminate\Support\Facades\Cache;

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

Test với facade testing methods:

php
use Illuminate\Support\Facades\Cache;

test('basic example', function () {
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
});
php
use Illuminate\Support\Facades\Cache;

public function test_basic_example(): void
{
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
}

Facades so với Helper Functions

Nhiều helper functions thực hiện cùng chức năng với facade tương ứng:

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

return view('profile');

Không có sự khác biệt thực tế giữa facades và helper functions. Khi dùng helpers, bạn vẫn test được giống hệt facade tương ứng.

Cách Facades hoạt động (How Facades Work)

Trong ứng dụng Laravel, facade là class cung cấp quyền truy cập đến object từ container. Cơ chế nằm trong class Facade. Tất cả facades kế thừa base class Illuminate\Support\Facades\Facade.

Class Facade sử dụng magic method __callStatic() để defer (chuyển tiếp) các lời gọi từ facade đến object được phân giải từ container:

php
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;

class UserController extends Controller
{
    /**
     * Hiển thị profile của user.
     */
    public function showProfile(string $id): View
    {
        $user = Cache::get('user:'.$id);

        return view('profile', ['user' => $user]);
    }
}

Nhìn vào class Illuminate\Support\Facades\Cache, không có static method get:

php
class Cache extends Facade
{
    /**
     * Lấy tên đăng ký của component.
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}

Cache facade kế thừa Facade và định nghĩa method getFacadeAccessor(). Method này trả về tên binding trong service container. Khi user gọi static method trên Cache facade, Laravel phân giải binding cache từ service container và chạy method được yêu cầu (trong trường hợp này là get) trên object đó.

Real-Time Facades

Với real-time facades, bạn có thể coi bất kỳ class nào trong ứng dụng như facade. Để tạo real-time facade, thêm prefix Facades vào namespace của class:

php
<?php

namespace App\Models;

use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * Xuất bản podcast.
     */
    public function publish(): void
    {
        $this->update(['publishing' => now()]);

        Publisher::publish($this);
    }
}

Khi real-time facade được sử dụng, implementation sẽ được phân giải từ service container dựa trên phần interface/class name sau prefix Facades. Khi testing, dùng Laravel facade testing helpers:

php
<?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
<?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;

    public function test_podcast_can_be_published(): void
    {
        $podcast = Podcast::factory()->create();

        Publisher::shouldReceive('publish')->once()->with($podcast);

        $podcast->publish();
    }
}

Bảng tham chiếu Facade Class (Facade Class Reference)

Bảng dưới đây liệt kê mọi facade và underlying class tương ứng. Key binding trong service container cũng được bao gồm khi áp dụng.

FacadeClassService Container Binding
AppIlluminate\Foundation\Applicationapp
ArtisanIlluminate\Contracts\Console\Kernelartisan
AuthIlluminate\Auth\AuthManagerauth
Auth (Instance)Illuminate\Contracts\Auth\Guardauth.driver
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BroadcastIlluminate\Contracts\Broadcasting\Factory
BusIlluminate\Contracts\Bus\Dispatcher
CacheIlluminate\Cache\CacheManagercache
Cache (Instance)Illuminate\Cache\Repositorycache.store
ConfigIlluminate\Config\Repositoryconfig
ContextIlluminate\Log\Context\Repository
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DateIlluminate\Support\DateFactorydate
DBIlluminate\Database\DatabaseManagerdb
DB (Instance)Illuminate\Database\Connectiondb.connection
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate
HashIlluminate\Contracts\Hashing\Hasherhash
HttpIlluminate\Http\Client\Factory
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\LogManagerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager
PasswordIlluminate\Auth\Passwords\PasswordBrokerManagerauth.password
Password (Instance)Illuminate\Auth\Passwords\PasswordBrokerauth.password.broker
PipelineIlluminate\Pipeline\Pipeline
ProcessIlluminate\Process\Factory
QueueIlluminate\Queue\QueueManagerqueue
Queue (Instance)Illuminate\Contracts\Queue\Queuequeue.connection
RateLimiterIlluminate\Cache\RateLimiter
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\RedisManagerredis
Redis (Instance)Illuminate\Redis\Connections\Connectionredis.connection
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory
RouteIlluminate\Routing\Routerrouter
ScheduleIlluminate\Console\Scheduling\Schedule
SchemaIlluminate\Database\Schema\Builder
SessionIlluminate\Session\SessionManagersession
Session (Instance)Illuminate\Session\Storesession.store
StorageIlluminate\Filesystem\FilesystemManagerfilesystem
Storage (Instance)Illuminate\Contracts\Filesystem\Filesystemfilesystem.disk
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator (Instance)Illuminate\Validation\Validator
ViewIlluminate\View\Factoryview
View (Instance)Illuminate\View\View
ViteIlluminate\Foundation\Vite