III. Laravel facade hoạt động như thế nào? (How Laravel facade work)
Trước tiên ta hãy cùng tìm hiểu một chút về facade design pattern.
1. Facade design pattern
Về khái niệm, facade design pattern hiểu đơn giản là nó sẽ giúp coder giao tiếp với source code xử lý phức tạp một cách dễ dàng thông qua một class facade trung gian.
Giả sử mình có thông tin của một user bao gồm tên, địa chỉ và học vấn. Trong đó:
Các bạn quan sát nội dung file User.php mình code để thực hiện yêu cầu trên:
<?php
class User
{
protected $user, $name, $address, $education;
public function __construct()
{
$this->user = require_once 'data.php';
$this->name = $this->user['name'];
$this->address = $this->user['address'];
$this->education = $this->user['education'];
}
public function getFullname()
{
return $this->name['fullname'];
}
public function getUsername()
{
return $this->name['username'];
}
public function getNumberHouse()
{
return $this->address['number_house'];
}
public function getStreet()
{
return $this->address['street'];
}
public function getCity()
{
return $this->address['city'];
}
public function getPrimarySchool()
{
return $this->education['primary_school'];
}
public function getJuniorHighSchool()
{
return $this->education['junior_high_school'];
}
public function getHighSchool()
{
return $this->education['high_school'];
}
public function getUniversity()
{
return $this->education['university'];
}
}
Đầu tiên ta nói về ưu điểm của cách 1. Nếu xử lý tất cả trong một class User thì sẽ giúp cho coder dễ dàng lấy thông tin user một cách nhanh chóng.
<?php
require_once 'autoload.php';
$user = new User;
$user->getFullname();
$user->getCity();
//
Nhưng xét về nhược điểm, bạn có thể nhận ra rằng file User.php quá dài, xử lý nhiều trường khác nhau. Nếu đây không phải là một ví dụ nhỏ dễ hình dung mà là một project quy mô vừa thôi thì cũng đủ làm ta thấy choáng ngợp nếu nhồi nhét các phương thức, thuộc tính vào một class như vậy.
Nhận xét cách 1:
Ưu điểm: cung cấp cú pháp dễ nhớ, nhanh chóng
Nhược điểm: class xử lý quá nhiều, khó bảo trì, nâng cấp.
⇒ Chưa thuyết phục.
Đối với cách 2, ta sẽ có cấu trúc thư mục như sau:
Nội dung mỗi class NameUser.php, AddressUser.php và EducationUser.php lần lượt là:
<?php
class NameUser
{
protected $name;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['name'];
}
public function getFullname()
{
return $this->name['fullname'];
}
public function getUsername()
{
return $this->name['username'];
}
}
<?php
class AddressUser
{
protected $address;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['address'];
}
public function getNumberHouse()
{
return $this->address['number_house'];
}
public function getStreet()
{
return $this->address['street'];
}
public function getCity()
{
return $this->address['city'];
}
}
<?php
class EducationUser
{
protected $education;
public function __construct()
{
$data = require_once 'data.php';
$this->name = $data['address'];
}
public function getPrimarySchool()
{
return $this->education['primary_school'];
}
public function getJuniorHighSchool()
{
return $this->education['junior_high_school'];
}
public function getHighSchool()
{
return $this->education['high_school'];
}
public function getUniversity()
{
return $this->education['university'];
}
}
Đối với cách này, ưu điểm của nó là đã phân chia các yêu cầu nhỏ thành từng class riêng, có tính tách rời, dễ dàng xử lý khi có vấn đề.
Nhưng với cách 2, ta lại gặp phải một nhược điểm đó chính là về cách thức lấy thông tin user.
<?php
require_once 'autoload.php';
$nameUser = new NameUser;
$addressUser = new AddressUser;
$educationUser = new Education;
$nameUser->getUsername();
$addressUser->getCity();
$educationUser->getHighSchool();
//
Ở đây chỉ có 3 class nên có thể bạn nghĩ sẽ nhớ được, không trở ngại. Nhưng bạn thử tưởng tượng nếu ứng dụng bạn "phình to" ra, class xử lý khởi tạo thêm rất nhiều, việc nhớ tên từng class với nhiệm vụ của nó thật sự là một vấn đề đáng quan tâm.
Nhận xét cách 2:
Ưu điểm: phân chia công việc cho từng class, dễ dàng bảo trì, nâng cấp
Nhược điểm: khó khăn trong việc nhớ cú pháp, tên và nhiệm vụ của từng class
⇒ Chưa thuyết phục.
Nhưng với facade design pattern, nó sẽ lấy ưu điểm của cả hai cách trên để tạo ra hiệu năng tuyệt vời.
Ưu điểm facade design pattern:
Phân chia công việc cho từng class, dễ dàng bảo trì, nâng cấp
Cung cấp cú pháp dễ nhớ, nhanh chóng
Để đáp ứng yêu cầu trên với facade design patter, chúng ta có cấu trúc thư mục như sau:
Ba file trong thư mục Facades thì mình sẽ giữ nguyên nội dung như ở cách 2, chỉ thêm namespace ở đầu file:
namespace Facades;
Bây giờ mình chỉ viết xử lý ở file User.php.
<?php
use Facades\NameUser;
use Facades\AddressUser;
use Facades\EducationUser;
class User
{
protected $name, $address, $education
public function __construct()
{
$this->name = new NameUser;
$this->address = new AddressUser;
$this->education = new EducationUser;
}
public function __call($method, $parameters)
{
if (method_exists($this->name, $method) == true) {
return $this->name->{$method}();
}
if (method_exists($this->address, $method) == true) {
return $this->name->{$method}();
}
if (method_exists($this->education, $method) == true) {
return $this->name->{$method}();
}
}
}
Nếu đã học PHP OOP, chắc bạn cũng đã quá quen thuộc với magic-method __call rồi. Method __call sẽ được thực thi khi class object chứa nó gọi một method nào đó không tồn tại. Sau đó mình sẽ kiểm tra xem method đó thuộc object nào trong 3 class object đã set ở __construct rồi thực thi nó. Như vậy bạn thấy, bây giờ User đóng vai trò là một facade, vừa đạt được hiệu năng, vừa tối ưu được cú pháp.
<?php
require_once 'autoload.php';
$user = new User;
$user->getFullname();
$user->getCity();
//
Dưới đây là mô hình hoạt động của facade design pattern: