---
# System prepended metadata

title: Laravel Facade
tags: [Laravel]

---

# 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`