Laravel Beauty: Tìm hiểu về Facade

https://viblo.asia/p/laravel-beauty-tim-hieu-ve-facade-znVGLYLbvZOe

Laravel Beauty: Tìm hiểu về Facade

Editors' Choice

Index

Mở đầu

Thông qua các bài viết trước đây trong series Laravel Beauty, mình đã giới thiệu đến các bạn hai khái niệm cơ bản được sử dụng trong Laravel là Service ContainerService Provider. Đây là hai khái niệm trung tâm cần nắm vững khi muốn tìm hiểu sâu về những chức năng khác của Laravel.

Và trong bài viết lần này, ta sẽ cùng đi tìm hiểu về một tính năng chắc hẳn các bạn đã, và vẫn hay dùng trong các project của mình, và nó chỉ có thể giải thích bằng những hiểu biết về Service Container cũng như Service Provider. Thế nên nếu bạn còn chưa hiểu rõ về hai khái niệm trên thì hãy dành chút thời gian xem lại các bài viết trước trong serie Laravel Beauty này nhé.

Facade là gì?

Nếu bạn thích tìm hiểu về Design Patterns, và đã từng xem qua cuốn sách nổi tiếng Design Patterns: Elements of Reusable Object-Oriented Software của "Gang of Four", thì bạn có thể sẽ biết đến Facade Pattern, nằm trong nhóm Structural.

Tuy nhiên, khái niệm Facade trong Laravel ... hoàn toàn không liên quan gì đến Facade Pattern được đề cập đến trong cuốn sách của Gang of Four nhé. =)) Giới thiệu thế để các bạn đỡ nhầm lẫn sau này thôi. =))

Vậy Facade trong Laravel là gì?

Facade có thể dịch đơn giản sang tiếng Việt là bề ngoài, mặt ngoài. Nó cho phép bạn truy cập đến các hàm bên trong các service được khai báo trong Service Container bằng cách gọi các hàm static.

Giải thích như trên thì nghe có vẻ khó hiểu, nhưng thực tế có thể bạn vẫn đang sử dụng Facade nhiều mà không để ý đấy.

Hãy cùng xem qua file config/app.php một chút nhé.

Nếu để ý ở phần cuối của file config đó, bạn sẽ thấy Laravel đã khai báo sẵn một loạt các class alias, để sau này ta có thể sử dụng trong project của mình dưới cái tên ngắn gọn, thay vì phải viết đầy đủ namespace của chúng. Tức là khi có khai báo 'Auth' => Illuminate\Support\Facades\Auth::class, thì khi chúng ta sử dụng class Auth bên trong project của mình, chúng ta thực tế đã gọi đến class Illuminate\Support\Facades\Auth.

Tản mạn một chút, nếu bạn thắc mặc tại sao Laravel có thể làm một việc tưởng chừng như "thần thánh" như vậy thì thật ra cũng chả có gì đặc biệt đâu, bởi bản thân PHP đã support cho chúng ta tính năng đó thông qua hàm class_alias rồi. Công việc của bạn bên chỉ là khai báo alias dưới dạng key-value vào trong file config/app.php, còn việc register alias thông qua hàm class_alias sẽ được Laravel thực hiện. Bạn có thể tham khảo class Illuminate\Foundation\AliasLoader để hiểu rõ bản chất của quá trình này.

Quay trở lại vấn đề register alias trong file config, nó có liên qua gì đến chủ đề Facade vậy?

Bạn có để ý tên đầy đủ của các class được register alias không? Vâng, chúng đều có namespaceIlluminate\Support\Facades. Hay nói cách khác các class được register alias ở đây đều là các Facade. Và các Facade mặc định của Laravel đều nằm trong thư mục vendor\laravel\framework\src\Illuminate\Supports.

Ngoài ra, những alias mà bạn có thể hay dùng, chẳng hạn như App, Auth, DB, Route ... thì đều là Facade cả đấy.

Chẳng hạn như những dòng code quen thuộc sau:

// routes.php file
Route::get('/', function () {
    return view('welcome');
});

// Controller file
if (Auth::check()) {
    $user = Auth::user();
}

Tuy nhiên, giờ mới là lúc vấn đề trở nên phức tạp này. Hãy thử vào xem nội dung một file Facade thế nào nhé.

Ví dụ như Auth Facade chẳng hạn. Đây là tất cả những gì bạn thấy ở class Illuminate\Support\Facades\Auth

namespace Illuminate\Support\Facades;

/**
 * @see \Illuminate\Auth\AuthManager
 * @see \Illuminate\Contracts\Auth\Factory
 * @see \Illuminate\Contracts\Auth\Guard
 * @see \Illuminate\Contracts\Auth\StatefulGuard
 */
class Auth extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'auth';
    }
}

Chỉ có duy nhất 1 method trong class đó, tên là getFacadeAccessor, ngoài ra hoàn toàn không có một static method nào khác cả. Vậy thì những hàm Auth::check(), Auth::user(), hay Auth::id() chui từ đâu ra?

Facade hoạt động như thế nào?

Giờ ta sẽ tiếp tục tìm hiểu ngọn ngành của một class Facade. Hãy để ý một chút, ta thấy class Auth, hay các class Facade khác, đều kế thừa từ một abstract classIlluminate\Support\Facades\Facade. Và trong class này ta sẽ thấy có hàm getFacadeAccessor()

/**
 * Get the registered name of the component.
 *
 * @return string
 *
 * @throws \RuntimeException
 */
protected static function getFacadeAccessor()
{
    throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}

Việc khai báo method với nội dung duy nhất là throw error như thế này thì class kế thừa của bạn bắt buộc phải khai báo override hàm getFacadeAccessor() (bằng không sẽ có error). Do đó tất cả các Facade đều sẽ có một static method là getFacadeAccessor() (và thật may mắn, Laravel làm hết mọi thứ cho chúng ta, làm tận răng đến mức mỗi Facade chỉ cần có một hàm đó là đủ).

Đọc kỹ nội dung abstract class Facade bạn sẽ thấy được rằng nội dung mà method getFacadeAccessor() trả về sẽ được sử dụng để tạo ra Facade Instance, mà instance này được resolve ra từ Application Instance $app, hay nói cách khác chính là Service Container.

Như vậy làm việc với Auth Facade thực tế là làm việc với service auth trong Service Container. Việc gọi các hàm static của Auth thực tế sẽ được xử lý trong magic method __callStatic (bạn có thể tìm hiểu về magic method này ở đây), rồi chuyển qua lời gọi hàm bình thường từ một instance đã được resolve ra từ trong Service Container.

Như vậy thì ta có thể thấy các cách gọi sau sẽ là tương đương:

Auth::check();

// Bản chất của lời gọi hàm static qua Facade có thể được tóm tắt lại thành
$auth = app('auth');
$auth->check();

Facade - the Good and the Bad

Facade có thể nói là một trong những khái niệm hay tính năng gây tranh cãi nhiều nhất của Laravel. Bạn có thể google ra cả đống bài viết, ý kiến cho rằng Facade là một "Bad design", hay "Anti-pattern". Nhưng cũng có nhiều người vẫn yêu thích và sử dụng Facade hàng ngày. Nó đã và vẫn đang là một phần của Laravel.

Facade thuận tiện. Facade dễ dùng. Facade giúp bạn viết code rất ngắn gọn nhanh chóng. Trên document của Laravel, Facade được miêu tả với rất nhiều ưu điểm như "providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods".

Facade được thiết kế như một cây cầu để người lập trình tiếp cận với "Service" một cách dễ dàng. Bạn có thể sử dụng Facade gần như mọi lúc mọi nơi mà không phải mất công khởi tạo, resolve các instance từ trong Service Container. Facade về bản chất cũng không phải là một class chứa đầy những static method, nên nó không nặng nề chiếm bộ nhớ như những class như vậy.

Tuy nhiên chính vì để có được những sự tiện lợi như thế mà ta cần một đống các "phép thuật" được xử lý ngầm "behind the scene". Với một người mới bắt đầu tìm hiểu và sử dụng Laravel, có thể họ sẽ dễ dàng biết đến và sử dụng cách gọi Auth::user(), nhưng họ sẽ không thể hiểu được bản chất của hàm đó từ đâu ra. Hay khi gặp vấn đề và cần vào trong code của Framework để kiểm tra thì cũng thật khó để bạn tìm thấy được nguồn gốc của chúng.

Hơn thế nữa, với việc quá tiện dụng của Facade, nếu không cẩn thận bạn sẽ có thể làm code trở nên phức tạp và khó đoán biết hơn. Ví dụ như mình muốn kiểm tra một class hoạt động dựa vào các dependencies nào thì mình thường sẽ xem ở trong constructor của nó. Hay trong Laravel có support method injection thì mình cũng xem qua cả trong khai báo method nữa. Thế nhưng, với việc gọi Facade ở bên trong hàm thì ta đã vô hình chung che đi việc hàm, hay class, đó cần một service dependency, thứ mà thay vì ta nên inject vào thì nay được resolve ra thông qua Facade.

Lời kết

Trong khuôn khổ nội dung bài viết này, mình xin đứng ngoài cuộc về việc tranh cãi xem nên hay không nên sử dụng Facade. Nó là một tính năng tiện dụng và không phải ngẫu nhiên mà Taylor Otwell, cha đẻ của Laravel, còn giữ lại nó đến ngày hôm nay.

Bài viết này chỉ hy vọng có thể giúp các bạn có thể hiểu hơn về những gì xảy ra đằng sau những hàm static mà ta vẫn hay gọi thông qua Facade.

Còn với câu hỏi "Nếu không dùng Facade thì có thể dùng cái gì để thay thế?", thì mình xin trả lời là mình vẫn hay dùng Dependency Injection.

Hãy dành chút thời gian tham khảo về phần Facade Class Reference trên document của Laravel. Bạn sẽ thấy được bản chất của một Facade là sử dụng instance của class nào, và nếu khi nào có vấn đề gì cần phải xem code của Framework thì cũng biết được nơi nào để mà tìm. Chẳng hạn như để tìm hiểu về các hàm của Facade Request thì bạn nên tìm đến class Illuminate\Http\Request, với Auth thì là Illuminate\Auth\AuthManager hay Mail thì là Illuminate\Mail\Mailer.

Và như vậy thì ta có thể thay thế việc sử dụng Facade Mail bằng cách inject Illuminate\Mail\Mailer vào class thông qua constructor injection hay method injection.

Có một chú ý ở đây là như mình cũng đã có đề cập ở các bài trước, một class không nên để phụ thuộc vào một class khác mà chỉ nên phụ thuộc vào abstraction (hay interface). Do đó, thay vì typehint Illuminate\Mail\Mailer ta sẽ typehint contract của nó là Illuminate\Contracts\Mail\Mailer

// Facade Style
function sendMail()
{
    // Sending mail
    Mail::send($view, $data);
}

// Dependency Injection Style
class Something
{
    protected $mailer;

    public function __construct(Illuminate\Contracts\Mail\Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    public function sendMail()
    {
        // Sending mail
        $this->mailer->send($view, $data);
    }
}

PHPLaravelFacade

All rights reserved

MỤC LỤC


BÀI VIẾT THUỘC SERIES


Laravel: The Beauty1. 2. 3. 4. 5.

CÁC TỔ CHỨC ĐƯỢC ĐỀ XUẤT


Bài viết liên quan

Closure-Based Commands In Laravel 5.3Viet Anh7 phút đọc 322 0 00Laravel Beauty: Recipes & Best PracticesTran Duc Thang17 phút đọc 18725 115 13137Laravel Beauty: Tìm hiểu về Service containerTran Duc Thang15 phút đọc 27711 85 41161Laravel Beauty: Tìm hiểu về Service ProviderTran Duc Thang15 phút đọc 26386 44 2497Tìm hiểu về Service Container trong LaravelHoàng Nguyễn9 phút đọc 15740 27 1256Laravel Beauty: Tìm hiểu về ContractTran Duc Thang8 phút đọc 8274 23 1043Tìm hiểu Service Provider trong LaravelHoàng Nguyễn5 phút đọc 16507 12 032Laravel và những điều cần biết - phần 3Hoàng Hữu Hợi17 phút đọc 2831 9 03[Phần 2] Tìm hiểu các tính năng mới trong Laravel 5.5Le Van Liem4 phút đọc 511 2 03Closure-Based Commands In Laravel 5.3Viet Anh7 phút đọc 322 0 00Laravel Beauty: Recipes & Best PracticesTran Duc Thang17 phút đọc 18725 115 13137Laravel Beauty: Tìm hiểu về Service containerTran Duc Thang15 phút đọc 27711 85 41161Laravel Beauty: Tìm hiểu về Service ProviderTran Duc Thang15 phút đọc 26386 44 2497Tìm hiểu về Service Container trong LaravelHoàng Nguyễn9 phút đọc 15740 27 1256

Bài viết khác từ Tran Duc Thang

Một số Design Principles trong lập trình mà bạn nên biếtTran Duc Thang22 phút đọc 5691 42 267[Become A SuperUser] Filesystem Hierarchy StandardTran Duc Thang19 phút đọc 1090 14 244Bài toán các vị tướng Byzantine và ứng dụng trong BlockchainTran Duc Thang19 phút đọc 7096 50 1584[Become a SuperUser] Debian vs Redhat: Package Management SystemTran Duc Thang14 phút đọc 2065 19 1135[Become a SuperUser] Manage file permissions and ownershipTran Duc Thang30 phút đọc 401 4 015[Become A SuperUser] Find system filesTran Duc Thang9 phút đọc 232 3 219[Review Sách] Cracking the Coding Interview - 189 Programming Questions & SolutionsTran Duc Thang16 phút đọc 1314 6 129Tìm hiểu về giải thuật Chia để Trị (Divide and Conquer)Tran Duc Thang18 phút đọc 3110 10 019Tìm hiểu về giải thuật Đệ QuyTran Duc Thang22 phút đọc 8558 6 018Cùng ôn lại các khái niệm về Cấu trúc dữ liệu, Giải thuật, Độ phức tạp thuật toán.Tran Duc Thang19 phút đọc 3221 34 158Simple Rules to make your code Cleaner (Part 2)Tran Duc Thang22 phút đọc 862 4 212Simple Rules to make your code Cleaner (Part 1)Tran Duc Thang15 phút đọc 1133 9 019Một số Design Principles trong lập trình mà bạn nên biếtTran Duc Thang22 phút đọc 5691 42 267[Become A SuperUser] Filesystem Hierarchy StandardTran Duc Thang19 phút đọc 1090 14 244Bài toán các vị tướng Byzantine và ứng dụng trong BlockchainTran Duc Thang19 phút đọc 7096 50 1584[Become a SuperUser] Debian vs Redhat: Package Management SystemTran Duc Thang14 phút đọc 2065 19 1135[Become a SuperUser] Manage file permissions and ownershipTran Duc Thang30 phút đọc 401 4 015[Become A SuperUser] Find system filesTran Duc Thang9 phút đọc 232 3 219[Review Sách] Cracking the Coding Interview - 189 Programming Questions & SolutionsTran Duc Thang16 phút đọc 1314 6 129Tìm hiểu về giải thuật Chia để Trị (Divide and Conquer)Tran Duc Thang18 phút đọc 3110 10 019

Bình luận

Tks tác giả!

Mình cũng đọc thêm ở đây 1 chút cho những ai cần thêm thông tin: http://www.sitepoint.com/how-laravel-facades-work-and-how-to-use-them-elsewhere/

@minh.long Cảm ơn bạn nhiều (thankyou)

Bài viết rất hữu ích! Cám ơn anh. Mong anh viết được nhiều bài như thế nữa (bow)

Một bài viết rất kĩ về Facade. Tuyệt vời .

Bài viết rất sâu và chi tiết về Facade! Thực sự (baiphuc) tác giả.

ở trong ví dụ cuối hình như có nhầm lẫn thì phải.

thuộc tính @mailer của Class Something khi sử dụng ở Method sendMail() phải là: $this->mailer->send($view, $data); mới đúng. Thiếu "er" phía sau mail.

@penguinbluechip Cảm ơn bạn đã chỉ ra lỗi của mình. Đúng như ý kiến của bạn thì chỗ đó phải là $this->mailer->send($view, $data);

(thankyou)

Em thấy phần lớn core của laravel hay các framework đều dùng dependency injection. Nhưng khi chỉ tìm hiểu vài class như middleware hay providers cơ bản của laravel thì thấy lúc dùng dependency injection, lúc lại dùng facades. Theo em thì dùng facade cũng chẳng tiện hơn vì muốn dùng thì phải khai báo class, use class short name hoặc khai báo alias. Trong khi đó, có quá nhiều cách để làm một việc vô hình gây khó khăn trong việc thống nhất khi lập trình vì mỗi người thích một kiểu. Mặc dù em rất thích Laravel nhưng chủ quan em thấy đây cũng là điểm "chưa tốt" của framework này vì "Có quá nhiều cách thực hiện một công việc mà giữa chúng chẳng đem lại lợi ích gì khác biệt." Không biết quan điểm của anh thế nào?

Cho mình hỏi 1 chút về đoạn code cuối. Bình thường thì mình sẽ viết như sau:

$mailer = new Mailer();
$mailer->setFrom('email@example.com');
//...set thêm 1 số cái nữa

$something = new SomeThing($mailer);

Nhưng mình đọc ở bài Service Container thì nếu là laravel thì khi resolve cái SomeThing kia nó sẽ tự tạo 1 cái instance của Mailer. Tuy nhiên mình muốn set trước 1 số thuộc tính của mailer như ở trên(không cố định, dữ liệu cần set có thể thay đổi tùy action) thì phải set ở đâu & set lúc nào?

P/S: Khi viết code bạn nên đặt trong ` `, hoặc code block

```

```

Ví dụ: $something = $newSomeThing($mailer);

good job

thằng Laravel có cái Middleware với phần Validation trong Request class magic quá, vẫn chưa tìm hiểu được cách nó diễn ra thế nào

Này là a viết theo Laravel ver bn đây ạ

Last updated