Giới thiệu

Facades cung cấp một "static" interface cho các class có trong service container của application. Laravel có sẵn rất nhiều facade cung cấp các quyền truy cập vào hầu hết các tính năng của Laravel. Facade của Laravel đóng vai trò như là một "static proxies" cho các class cơ bản nằm trong service container, mang lại lợi ích của một cú pháp ngắn gọn, hàm ý trong khi vẫn duy trì khả năng kiểm thử và tính linh hoạt cao so với các phương thức static truyền thống.

Tất cả các facade của Laravel được định nghĩa trong namespace Illuminate\Support\Facades. Vì vậy, chúng ta có thể dễ dàng truy cập vào một facade như sau:

use Illuminate\Support\Facades\Cache;

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

Trong suốt tài liệu của Laravel, nhiều ví dụ sẽ sử dụng các facade để thực hiện các tính năng khác nhau của framework.

Khi nào dùng Facade

Facade có nhiều lợi ích. Chúng cung cấp một cú pháp ngắn gọn, dễ nhớ cho phép bạn sử dụng các tính năng của Laravel mà không cần phải nhớ các tên class dài sẽ phải khai báo hoặc phải tự cấu hình. Hơn nữa, do cách sử dụng độc đáo của các phương thức động của PHP, chúng rất dễ để test.

Tuy nhiên, một số lưu ý phải được thực hiện khi sử dụng facade. Mối nguy hiểm chính của facade là class bị quá giới hạn. Vì facade rất dễ sử dụng và không cần phải khai báo, nên nó rất dễ để các class của bạn lớn lên và sử dụng nhiều facade trong một class. Việc dùng nhiều khai báo phụ thuộc, làm cho việc phát triển các dòng code trong class của bạn ngày càng lớn hơn. Và vì vậy, khi sử dụng facade, đặc biệt chú ý đến giới hạn của class của bạn để giới hạn của nó ở trong giới hạn cho phép.

{tip} Khi phát triển một package third-party tương tác với Laravel, tốt hơn là bạn nên khai báo Laravel contracts thay vì sử dụng facade. Vì các package này được xây dựng ở bên ngoài của Laravel, nên bạn sẽ không có quyền truy cập vào các facade testing helper của Laravel.

Facades và khai báo phụ thuộc

Một trong những lợi ích chính của khai báo phụ thuộc là khả năng để hoán đổi implementation của class được khai báo. Điều này rất hữu ích trong quá trình testing vì bạn có thể tích hợp một giả lập hoặc một khai báo và kiểm tra các hàm khác trên giả lập đó.

Thông thường, không thể giả lập hoặc khai báo một phương thức static class thực sự. Tuy nhiên, vì các facade sử dụng các phương thức động để gọi các phương thức proxy đến các đối tượng được resolve từ service container, nên chúng ta có thể kiểm thử các facade giống như chúng ta đang kiểm tra một instance class đã được tích hợp. Ví dụ: hãy xem route sau:

use Illuminate\Support\Facades\Cache;

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

Chúng ta có thể viết test sau để kiểm tra phương thức Cache::get đã được gọi với đối số mà chúng ta mong muốn hay chưa:

use Illuminate\Support\Facades\Cache;

/**
 * A basic functional test example.
 *
 * @return void
 */
public function testBasicExample()
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $this->visit('/cache')
         ->see('value');
}

Facades và Helper Functions

Ngoài facade, Laravel còn chứa nhiều hàm "helper" để có thể thực hiện các task phổ biến như tạo views, kích hoạt event, gửi job hoặc gửi response HTTP. Nhiều hàm của helper này thực hiện giống với facade tương ứng. Ví dụ: facade này và helper này là tương đương:

return View::make('profile');

return view('profile');

Hoàn toàn không có sự khác biệt giữa facade và helper. Khi sử dụng các helper, bạn vẫn có thể kiểm tra chúng như là bạn làm với facade. Ví dụ, hãy xem route sau:

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

Ở trong route cache ở trên, hàm helper cache sẽ gọi phương thức get trong class facade Cache. Vì vậy, mặc dù chúng ta đang sử dụng hàm helper, nhưng chúng ta có thể viết bài kiểm tra như ở dưới để kiểm tra phương thức đã được gọi với đối số mà chúng ta mong muốn hay chưa:

use Illuminate\Support\Facades\Cache;

/**
 * A basic functional test example.
 *
 * @return void
 */
public function testBasicExample()
{
    Cache::shouldReceive('get')
         ->with('key')
         ->andReturn('value');

    $this->visit('/cache')
         ->see('value');
}

Facade làm việc như thế nào

Trong một application Laravel, facade là một class chuyên cung cấp các quyền truy cập các một đối tượng từ container. Điều này được thực hiện bởi cơ chế trong class Facade. Và các Facade của Laravel hay bất kỳ facade nào mà bạn đã tạo sẽ phải extend từ class Illuminate\Support\Facades\Facade.

Class Facade sử dụng phương thức magic __callStatic() để trì hoãn gọi từ facade của bạn đến một đối tượng được resolve từ container. Trong ví dụ ở dưới đây, sẽ thực hiện gọi đến cache system của Laravel. Nếu bạn chỉ liếc qua, bạn có thể nghĩ rằng phương thức static get đang được gọi trong classCache:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    /**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return Response
     */
    public function showProfile($id)
    {
        $user = Cache::get('user:'.$id);

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

Nhưng hãy lưu ý rằng, ở đầu file chúng ta đã "importing" một facade Cache. Facade này đóng vai trò như là một proxy để truy cập vào implementation của interface Illuminate\Contracts\Cache\Factory. Bất kỳ hàm gọi nào mà chúng ta thực hiện bằng cách sử dụng facade đều sẽ được chuyển đến instance cache service của Laravel.

Nếu chúng ta nhìn vào class Illuminate\Support\Facades\Cache, bạn sẽ thấy rằng không có một phương thức static get nào cả:

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

Thay vào đó, facade Cache sẽ được extend từ class Facade và định nghĩa một phương thức là getFacadeAccessor(). Công việc của phương thức này là trả về tên của một liên kết đã có trong service container. Khi người dùng tham chiếu bất kỳ phương thức tĩnh nào trên facade Cache, Laravel sẽ resolve một liên kết có tên là cache từ trong service container và chạy phương thức được gọi (trong trường hợp này là get) trên đối tượng đó.

Real-Time Facades

Sử dụng real-time facade, bạn có thể coi bất kỳ class nào trong ứng dụng của bạn như là một facade. Để minh họa cách sử dụng này, hãy xem một ví dụ. Ví dụ: giả sử model Podcast của chúng ta có một phương thức là publish. Tuy nhiên, để publish một podcast, chúng ta cần khai báo một instance Publisher:

<?php

namespace App;

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

class Podcast extends Model
{
    /**
     * Publish the podcast.
     *
     * @param  Publisher  $publisher
     * @return void
     */
    public function publish(Publisher $publisher)
    {
        $this->update(['publishing' => now()]);

        $publisher->publish($this);
    }
}

Việc khai báo một implementation của publisher vào trong phương thức này cho phép chúng ta dễ dàng kiểm tra phương thức này một cách độc lập vì chúng ta có thể giả định publisher đã được khai báo. Tuy nhiên, nó đòi hỏi chúng ta phải luôn truyền vào một instance publisher cho mỗi lần chúng ta gọi phương thức publish. Sử dụng các real-time facade, chúng ta có thể duy trì khả năng kiểm tra tương ứng trong khi không bắt buộc phải truyền vào instance của Publisher. Để tạo real-time facade, thêm tiền tố namespace của class được import với Facades:

<?php

namespace App;

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

class Podcast extends Model
{
    /**
     * Publish the podcast.
     *
     * @return void
     */
    public function publish()
    {
        $this->update(['publishing' => now()]);

        Publisher::publish($this);
    }
}

Khi real-time facade được sử dụng, việc implementation của publisher sẽ được resolve từ service container bằng cách sử dụng phần interface hoặc tên class xuất hiện phía sau tiền tố Facades. Khi testing, chúng ta có thể sử dụng helper built-in facade testing của Laravel để giả lập phương thức này được gọi:

<?php

namespace Tests\Feature;

use App\Podcast;
use Tests\TestCase;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;

class PodcastTest extends TestCase
{
    use RefreshDatabase;

    /**
     * A test example.
     *
     * @return void
     */
    public function test_podcast_can_be_published()
    {
        $podcast = factory(Podcast::class)->create();

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

        $podcast->publish();
    }
}

Tham khảo Class Facade

Dưới đây bạn có thể tìm thấy mọi facade và class cơ sở nó. Đây là một công cụ hữu ích để nhanh chóng để đào sâu vào tài liệu API cho một facade gốc. Service container binding key cũng được kèm theo nếu trong trường hợp bạn cần dùng.

Facade Class Service Container Binding
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory  
Broadcast (Instance) Illuminate\Contracts\Broadcasting\Broadcaster  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\CacheManager cache
Cache (Instance) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Password (Instance) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue.connection
Queue (Base Class) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis (Instance) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
Response (Instance) Illuminate\Http\Response  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Builder  
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
Storage (Instance) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (Instance) Illuminate\Validation\Validator  
View Illuminate\View\Factory view
View (Instance) Illuminate\View\View  
Service Providers Contracts
© 2023 by Logo page doc-vn