Laravel Beauty: Tìm hiểu về Facade
https://viblo.asia/p/laravel-beauty-tim-hieu-ve-facade-znVGLYLbvZOe
Last updated
https://viblo.asia/p/laravel-beauty-tim-hieu-ve-facade-znVGLYLbvZOe
Last updated
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 Container và Service 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é.
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ó namespace
là Illuminate\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:
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
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?
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 class
là Illuminate\Support\Facades\Facade
. Và trong class này ta sẽ thấy có hàm getFacadeAccessor()
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:
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.
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
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
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
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
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:
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 ạ
Còn nếu bạn đã sẵn sàng thì ta hãy cùng bắt đầu nào
Vâng, việc bạn gọi các hàm Route::get()
, hay Auth::user()
như trên đều là đang sử dụng Facade. Và hãy nhìn lại phần định nghĩa mà mình viết ở phần đầu nhé. Có đúng là bạn đang sử dụng các hàm static
không
Hy vọng qua bài viết này mọi người có thể hiểu thêm về một tính năng vốn rất quen thuộc của Laravel. Và xin hẹn gặp lại vào bài viết tới, với những đào sâu khác về Framework phổ biến này
Viblo 9 14 135 45.4KSun* R&D Product Development 2 39 68 2.1KHUST & PI 4 1 8 18.1K
Đăng nhập để bình luậnĐev Đít Đen @minh.longthg 3 11, 2016 1:42 SA
0 |Trả lờiChia sẻTran Duc Thang @thangtd90thg 3 13, 2016 8:45 SA
0 |Trả lờiChia sẻLê Tình @xuthanh95thg 4 13, 2016 2:51 CH
hay quá . mong rằng bọn anh sẽ ra 1 tutorial demo project thật sâu đề mọi người học hỏi tóm tắt kiến thức đã học
0 |Trả lờiChia sẻPhung The Tai @phung.the.taithg 4 23, 2016 12:41 SA
0 |Trả lờiChia sẻTu Chu Quang @tucq88thg 7 7, 2016 1:59 CH
0 |Trả lờiChia sẻNgo Duy Son @ngo.duy.sonthg 7 25, 2016 5:14 SA
0 |Trả lờiChia sẻChip PenguinBlue @penguinbluechipthg 10 31, 2016 1:38 SA
0 |Trả lờiChia sẻTran Duc Thang @thangtd90thg 10 31, 2016 7:00 SA
0 |Trả lờiChia sẻNguyễn Xuân Quỳnh @quynh001thg 6 6, 2017 4:49 CH
0 |Trả lờiChia sẻ Xem thêm (3)Tran Duc Thang @thangtd90thg 6 7, 2017 12:45 CH
uh, việc bên trong Facade không có gì thật sự gây rất nhiều khó khăn khi tiếp cân =)) Facade tiện lợi cho nhiều người, nhưng cũng nhiều người không thích dùng nó Quan trọng là trong team thống nhất với nhau về việc sử dụng là được rồi, cũng không nên quá khắt khe.
+2 |Trả lờiChia sẻTran Duc Thang @thangtd90thg 6 7, 2017 12:46 CH
Anh thấy việc dùng alias tiện lợi hơn nhiều mà, không phải nhớ chính xác đường dẫn. Nhất là khi vào tinker gõ =)) Anh thì toàn dùng alias khi có thể thôi
0 |Trả lờiChia sẻphạm bình @binhpvthg 8 21, 2017 4:03 CH
0 |Trả lờiChia sẻTran Duc Thang @thangtd90thg 8 21, 2017 5:25 CH
Nếu SomeThing
của bạn là class cần dependency là Mailer $mailer
, trong khi bạn lại cần đặt giá trị cho các thuộc tính của $mailer
thì bạn nên tự tạo ra và tự set giá trị thôi. Service Container không phải là viên đạn bạc để giúp bạn có thể giải quyết tất cả các vấn đề được. Như ở comment trong bài này https://viblo.asia/p/laravel-beauty-tim-hieu-ve-service-container-3KbvZ1wLGmWB mình đã từng viết, bạn có thể đặt giá trị mặc định để Service Container có thể resolve ra instance được, nhưng với giá trị cho các property là không cố định thì mình phải tự làm thôi.
thì sẽ không bị lỗi render như comment của bạn hiện tại.
0 |Trả lờiChia sẻphạm bình @binhpvthg 8 22, 2017 1:29 CH
Hihi. Cám ơn bạn. Mình đã sửa lại comment rồi
+1 |Trả lờiChia sẻDat Dao @datagitthg 1 23, 2019 2:30 CH
0 |Trả lờiChia sẻPham Tri Trung @Plumpboythg 7 26, 2019 11:25 SA
0 |Trả lờiChia sẻduongricky @duongrickythg 12 18, 2019 3:17 CH
0 |Trả lờiChia sẻHuynh Nhut Phi @huynhnhutphithg 3 17, 2020 3:04 CH
cám ơn tác giả, bài viết rất hay !!!