# Laravel Facade 絕對必看 https://laracasts.com/series/laravel-6-from-scratch/episodes/41 https://www.bilibili.com/video/BV1rk4y1r7FA/?spm_id_from=333.788.recommend_more_video.5 https://laravel.tw/docs/5.0/facades 後半段介紹 還不錯 https://www.itread01.com/content/1504719487.html https://laravel.tw/docs/5.0/facades https://mark-lin.com/posts/20181228/ https://learnku.com/laravel/t/769/containerserviceproviderfacade-in-laravel-architecture 必看 https://laracasts.com/series/laravel-6-from-scratch/episodes/40 他跟contact合同一樣 差不多概念 只是一個是用依賴注入實現 一個不用依賴注入 一樣都是解析container的 容器綁定 然後全域註冊 可以解析來看 ![](https://i.imgur.com/oJnDRo6.png) ![](https://i.imgur.com/ZdRz7UB.jpg) ## 介紹 Facades(读音:/fəˈsäd/ )为应用程序的服务容器中可用的类提供了一个「静态」接口。Laravel 自带了很多 facades ,几乎可以用来访问到 Laravel 中所有的服务。Laravel facades 实际上是服务容器中那些底层类的「静态代理」,相比于传统的静态方法, facades 在提供了简洁且丰富的语法同时,还带来了更好的可测试性和扩展性。 Facades 提供一個靜態介面給在應用程式的 服務容器(container) 中可以取用的類別。 技術長說明 facades(跳板)(做make動作(route 註冊 controller)(裡面包其他class這個不太懂) ## 解釋 在 Laravel 應用程式的環境中,facade 是個提供從容器存取物件的類別。Facade 類別是讓這個機制可以運作的原因。Laravel 的 facades 和你建立的任何客製化 facades,將會繼承基本的 Facade 類別。 你的 facade 類別只需要去實作一個方法:getFacadeAccessor。getFacadeAccessor 方法的工作是定義要從容器解析什麼。基本的 Facade 類別利用 __callStatic() 魔術方法來從你的 facade 呼叫到解析出來的物件。 所以當你對 facade 呼叫,例如 Cache::get,Laravel 從 服務容器解析快取管理類別出來,並對該類別呼叫 get 方法。用科技術語來說,Laravel Facades 是使用 Laravel 服務容器作為服務定位器的便捷語法。 在一般情況咱們如果要使用物件的某個方法可能會寫成如下 ``` <?php $userService = $app->make('UserService'); $userService->createUser(); ``` 但是有時後你會看到如下的程式碼 ``` <?php UserService::createUser(); ``` 而這就是 Laravel 所提供的 Facade 語法糖,而 Facade 實際上是一種設計模式。 ## Facade(外觀) 設計模式 Facade 設計模式基本的定義如下 `定義一個高層級的接口,客戶端只能透過它來與子系統進行溝通。 ` **程式碼範例** ``` <?php interface IMessage { public function push(); } class LineSDK implements IMessage { public function push() { var_dump('I push a message to line'); } } ``` 然後我們這裡會在寫一個 Facade 來讓我們的系統來使用。 ``` <?php class MessageFacade { private $sdk; public function __construct(IMessage $sdk) { $this->sdk = $sdk; } public function push() { $this->sdk->push(); } } ``` 最後這個時候客戶端想要使用時,就會透過 Facade 來進行發送訊息,如下程式碼。 ``` <?php // 備註,在 laravel 時這裡嚴格來說會寫成 // $app->make('Imessage') // 而不會看到 linesdk 這東西 $sdk = new MessageFacade(new LineSDK()); $sdk->push(); ``` ## 這樣有什麼好處 ? `當 sdk 進行修改時,你就不需要修改應用層的也方,只要修改 Facade 就好。` 但這樣還是要改啊 ? 只是換個地方改而以 ? 那假設你有十個地方直接使用 SDK 呢 ? 那這樣不就代表你十個地方就要改,而如果使用 Facade 就只有一個地方要修改。 ## Laravel Facade 是如何實現的呢 ? 就單來說,它就如下圖所示,主要的核心就是在 Facade 這個抽象類別,它裡面會定義一個 php 的 __callStatic 方法,它被執行到的時機為,這個類別中被呼叫靜態方法時,他就會被執行到。 ``` abstract class Facade { public static function __callStatic($method, $args) { $instance = static::getFacadeAccessor(); return $instance::$method(...$args); } } ``` ``` <?php class MessageFacade extends Facade { protected static function getFacadeAccessor(){ return new LineSDK(); } } class LineSDK { public function push() { var_dump('I push a message to line'); } } ``` ## 支持與反對的看法 首先支持使用 Facade 這一派的他們提出以下使用後的優點 : * 簡潔的程式碼。 * 乾淨的建構子。 * 更高的可測試性與彈性。 **但相對的反對派提出了一下問題 :** 相依了 Facade,這樣事實上就打破了當初建立 container 來想解決的事情。 要知道這個類別中有使用那些外部套件,那就只能一個一個找。 IDE 的不友好。 順到說一下,就我各人的看法我比較偏向不使用 Laravel Facade,主要是因為我比較在意一個類別的相依性控制,而每當我需要知道這個類別有啥相依時,不用讓我一個一個去慢慢的找,對我來說有點浪費時間。 ## 實際用法 在下面的例子,對 Laravel 快取系統進行呼叫。簡單看過去這程式碼,有人可能會以為靜態方法 get 是對 Cache 類別呼叫。 `$value = Cache::get('key');` 然而,如果我們去看 `Illuminate\Support\Facades\Cache` 類別,你將會看到它沒有靜態方法**get**: ``` class Cache extends Facade { /** * 取得元件的註冊名稱 * * @return string */ protected static function getFacadeAccessor() { return 'cache'; } } ``` Cache 類別繼承基本的 Facade 類別並定義一個 getFacadeAccessor() 方法。記住,**這個方法的工作是回傳服務容器綁定的名稱**。 當使用者在 Cache 的 facade 上參考任何的靜態方法,Laravel 會從服務容器解析被綁定的 cache ,並對該物件執行被請求的方法 (在這個例子中, get)。 所以我們的 Cache::get 呼叫可以被重寫成像這樣: $value = $app->make('cache')->get('key'); ## 匯入 Facades 記住,如果你在控制器有使用命名空間的狀況使用 facade,你會需要匯入 facade 類別進入命名空間。所有的 facades 存在於全域命名空間: ``` <?php namespace App\Http\Controllers; use Cache; class PhotosController extends Controller { /** * 取得所有的應用程式相片。 * * @return Response */ public function index() { $photos = Cache::get('photos'); // } } ``` ## 建立 Facades 為你自己的應用程式或套件建立 facade 是很簡單的。你只需要 3 個東西: ``` 一個 服務容器綁定。 一個 facade 類別。 一個 facade 別名設定。 ``` 讓我們來看個例子。這裡有一個定義為 PaymentGateway\Payment 的類別。 ``` namespace PaymentGateway; class Payment { public function process() { // } } ``` 我們需要可以從服務容器解析出這個類別。所以,讓我們來加上一個綁定到服務提供者: ``` App::bind('payment', function() { return new \PaymentGateway\Payment; }); ``` 註冊這個綁定的好方式是建立新的 服務提供者 命名為 PaymentServiceProvider,並把這個綁定加到 register 方法。再來你可以設定 Laravel 從 config/app.php 設定檔載入你的服務提供者。 接下來,我們可以建立我們自己的 facade 類別: ``` use Illuminate\Support\Facades\Facade; class Payment extends Facade { protected static function getFacadeAccessor() { return 'payment'; } } ``` 最後,如果我們希望,可以在 config/app.php 設定檔為 facade 加個別名到 aliases 陣列。現在我們可以在 Payment 類別的實例上呼叫 process 方法。 `Payment::process();` 我看facades這種幾乎都會註冊別名 **自動載入別名的附註** 在 aliases 陣列中的類別在某些實例中不能使用,因為 PHP 將不會嘗試去自動載入未定義的類型暗示類別。如果 `\ServiceWrapper\ApiTimeoutException` 命別名為 `ApiTimeoutException`,即便有例外被拋出,在 `\ServiceWrapper` 命名空間外面的 `catch(ApiTimeoutException $e)` 將永遠捕捉不到例外。類似的問題在有類型暗示的別名類別一樣會發生。唯一的替代方案就是放棄別名並用 use 在每一個檔案的最上面引入你希望暗示類型的類別。 ## route https://www.cntofu.com/book/107/Laravel%20Facade%E2%80%94%E2%80%94Facade%20%E9%97%A8%E9%9D%A2%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md 门面相对于其他方法来说,最大的特点就是简洁,例如我们经常使用的Router,如果利用服务容器的make: ``` App::make('router')->get('/', function () { return view('welcome'); }); ``` 如果利用门面: ```php Route::get('/', function () { return view('welcome'); }); ``` ## 門面實現過程分析 https://www.itread01.com/content/1504719487.html `Rice::food();` 因為 Rice 是別名,所以實際上執行的是: `\App\RiceFacade::food()` 但是我們的 RiceFacade 類裏面並沒有定義靜態方法 food 啊?怎麽辦呢?直接拋出異常嗎?不是,在 PHP 裏,如果訪問了不可訪問的靜態方法,會先調用 __callstatic,所以執行的是: `\App\RiceFacade::__callStatic()` 雖然我們在 RiceFacade 中沒有定義,但是它的父類 Facade 已經定義好了: ``` /vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php public static function __callStatic($method, $args) { // 實例化 Rice {#270} $instance = static::getFacadeRoot(); // 實例化失敗,拋出異常 if (! $instance) { throw new RuntimeException(‘A facade root has not been set.‘); } // 調用該實例的方法 return $instance->$method(...$args); } ``` 後面的解析看文章 ## 個人理解 這是設計模式 request::get 這樣用高級街口 因為可能在很多都地方都要用 所以要全域註冊 但又太亂 所以要統一街口 不然每個use 這個container很亂 統一然後 一樣returen同一個 那facad因為callstatic會跑 第一個外面的涵式 那是解析容器 你可以用::class 不要去bind做 但當你這個::class要做東西就不能了 你還是要bind(::class,function(){ return new A(aaa); }) 至於為什麼不能new 因為這邊function是解析容器 你可以 直接打::class 因為你註冊也可以寫::class 但你容器註冊總不能實立化八 ###### tags: `Laravel`