外牆


##Facades 工作原理

即時Facades

Facade 參考類別

#簡介

Facades 為應用的服務容器提供了一個「靜態」 介面。 Laravel 自帶了許多 Facades,可以存取絕大部分功能。 Laravel Facades 實際上是服務容器中底層類別的 “靜態代理” ,相對於傳統靜態方法,在使用時能夠提供更加靈活、更加易於測試、更加優雅的語法。
###所有的 Laravel Facades 都定義在 ###Illuminate\Support\Facades### 命名空間下。所以,我們可以輕鬆的使用 Facade :###
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {  
  return Cache::get('key');
  });
###在 Laravel 文件中,有許多範例程式碼都會使用 Facades 來示範框架的各種功能。 #####################

何時使用 Facades

Facades 有很多優點,它提供了簡單,易記的語法,從而無需手動注入或配置長長的類別名稱。此外,由於他們對 PHP 靜態方法的獨特調用,使得測試起來非常容易。

然而,在使用 Facades 時,有些地方需要特別注意。使用 Facades 時最主要的危險就是會造成類作用範圍的膨脹。由於 Facades 使用起來非常簡單且不需要注入,就會使得我們不經意地在單一類別中使用許多 Facades ,從而導致類別變得越來越大。然而使用依賴注入的時候,使用的類別越多,構造方法就會越長,在視覺上註意到這個類別有些龐大了。因此在使用 Facades 的時候,要特別注意控制類別的大小,讓類別的作用範圍保持短小。

{tip} 在開發與 Laravel 互動的第三方擴充包時,最好選擇注入 Laravel 契約 而不使用 Facades 。因為擴充包是在Laravel 之外構建,你無法使用Laravel Facades 測試輔助函數

##Facades 相較於依賴注入

依賴注入的主要好處之一是能交換注入類別的實作。在測試的時候非常有用,因為你可以注入一個 mock 或 stub,並斷言 stub 上的各種方法。

通常,真正的靜態方法是不可能 mock 或 stub 的。但是 Facades 使用動態方法對服務容器中解析出來的物件方法的呼叫進行了代理,我們也可以像測試注入類別實例一樣測試 Facades。例如,像下面的路由:

use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {  
  return Cache::get('key');
 });

我們可以帶我們期望的參數來寫下面的測試程式碼來驗證

Cache::get 方法:

use Illuminate\Support\Facades\Cache;
/**
 * 一个基础功能的测试。
 *
 * @return void
 */
 public function testBasicExample(){ 
    Cache::shouldReceive('get')   
          ->with('key')     
          ->andReturn('value');  
    $this->visit('/cache')   
        ->see('value');
    }

Facades 相較於輔助函數

#除了Facades,Laravel 還包含各種『輔助函數』 來實現這些常用功能,例如產生視圖、觸發事件、任務調度或發送HTTP 回應。許多輔助函數都有與之對應的 Facades 。例如,下面這個 Facades 和輔助函數的作用是一樣的:

return View::make('profile');return view('profile');

Facade 和輔助函數之間沒有實際的區別。當你使用輔助函數時,你可以像測試對應的 Facade 那樣進行測試。例如,下面的路由:

Route::get('/cache', function () {    return cache('key');});

在底層實現,輔助函數

cache 實際上是呼叫 Cache 這個 Facade 的 get 方法。因此,儘管我們使用的是輔助函數,我們仍然可以帶上我們期望的參數編寫下面的測試程式碼來驗證該方法:

use Illuminate\Support\Facades\Cache;
/**
 * 一个基础功能的测试用例。
 *
 * @return void
 */
 public function testBasicExample(){ 
    Cache::shouldReceive('get')  
           ->with('key')    
           ->andReturn('value'); 
    $this->visit('/cache')  
        ->see('value');
     }

Facades 工作原理

在 Laravel 應用中,Facade 就是一個可以從容器存取物件的類別。其中核心的部件就是 Facade 類別。不管是 Laravel 自帶的 Facades,還是自訂的 Facades,都繼承自 Illuminate\Support\Facades\Facade 類別。

Facade 基底類別使用了__callStatic() 魔術方法,直到物件從容器中解析出來後,才會進行呼叫。在下面的例子中,呼叫了 Laravel 的快取系統。透過瀏覽這段程式碼,可以假定在Cache 類別中呼叫了靜態方法get

<?php
   namespace App\Http\Controllers;
   use App\Http\Controllers\Controller;
   use Illuminate\Support\Facades\Cache;
   class UserController extends Controller{   
    /**
     * 显示给定用户的信息。
     *
     * @param  int  $id
     * @return Response
     */   
      public function showProfile($id) 
         {       
           $user = Cache::get('user:'.$id); 
           return view('profile', ['user' => $user]);  
          }
      }

注意在上面這段程式碼中,我們『導入』了Cache Facade。這個 Facade 作為存取 Illuminate\Contracts\Cache\Factory 介面底層實作的代理。我們使用 Facade 進行的任何呼叫都將傳遞給 Laravel 快取服務的底層實例。

如果我們看一下Illuminate\Support\Facades\Cache 這個類,你會發現類別中根本沒有get 這個靜態方法:

class Cache extends Facade{   
    /**
     * 获取组件的注册名称。
     *
     * @return string
     */  
       protected static function getFacadeAccessor() { 
            return 'cache'; 
        }
     }

Cache Facade 繼承了Facade 類,並定義了getFacadeAccessor() 方法。這個方法的作用是傳回服務容器綁定的名稱。當使用者呼叫Cache Facade 中的任何靜態方法時,Laravel 會從服務容器中解析cache 綁定以及該物件運行所請求的方法(在這個例子中就是get 方法)。

#即時 Facades

使用即時 Facades,你可以將應用程式中的任何類別視為 Facade。為了說明這是如何使用的,讓我們來看看另一種方法。例如,假設我們的 Podcast 模型有一個 publish 方法。然而,為了發布Podcast,我們需要注入一個Publisher 實例:

<?php
   namespace App;
   use App\Contracts\Publisher;
   use Illuminate\Database\Eloquent\Model;
   class Podcast extends Model{  
     /**
     * 发布 Podcast。
     *
     * @param  Publisher  $publisher
     * @return void
     */ 
        public function publish(Publisher $publisher) 
           {     
              $this->update(['publishing' => now()]); 
              $publisher->publish($this);   
             }
        }

將發布者的實作注入到該方法中,我們可以輕鬆地測試這種方法,因為我們可以模擬注入的發布者。但是,它要求我們每次呼叫 publish 方法時都要傳遞一個發布者實例。使用即時的 Facades,我們可以保持相同的可測試性,而不需要明確地通過 Publisher 實例。若要產生即時Facade,請在匯入類別的名稱空間中加上Facades:

<?php
   namespace App;
   use Facades\App\Contracts\Publisher;
   use Illuminate\Database\Eloquent\Model;
   class Podcast extends Model{ 
    /**
     * 发布 Podcast。
     *
     * @return void
     */ 
        public function publish()  
          {   
               $this->update(['publishing' => now()]); 
                Publisher::publish($this);  
            }
       }

當使用即時Facade 時,發布者實作將透過使用Facades 前綴後出現的介面或類別名稱的部分來解決服務容器的問題。在測試時,我們可以使用 Laravel 內建的 facade 測試輔助函數來模擬這個方法呼叫:

<?php
  namespace Tests\Feature;use App\Podcast;
  use Tests\TestCase;use Facades\App\Contracts\Publisher;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  class PodcastTest extends TestCase{  
    use RefreshDatabase;   
    /**
     * 一个测试演示。
     *
     * @return void
     */  
       public function test_podcast_can_be_published()  
         {    
             $podcast = factory(Podcast::class)->create(); 
             Publisher::shouldReceive('publish')->once()->with($podcast);  
             $podcast->publish(); 
            }
      }

Facade 類別參考

在下面你可以找到每個 Facade 類別及其對應的底層類別。這是一個尋找給定 Facade 類別 API 文件的工具。服務容器綁定 的關鍵資訊也包含在內。

##Illuminate\Foundation\ApplicationArtisanIlluminate\Console\Kernel##AuthauthAuth(實例)#auth.driver# #BladeIlluminate\View\Compilers\BladeCompilerIlluminate \Contracts\Broadcasting\FactoryIlluminate\Contracts\Broadcasting\Broadcaster###################1 #########Bus######Illuminate\Contracts\Bus\Dispatcher###### ###########快取##### ##Illuminate\ Cache\CacheManager########cache##############Cache(實例)#######Illuminate\Cache\Repository### #####快取.store############Notification#。亮\Notifications\ChannelManager 密碼照亮\Auth\Passwords\PasswordBrokerManager密碼(實例)Illuminate\Auth\Passwords\PasswordBroker##Queue#Queue (實例)Illuminate\Contracts\Queue\Queue##queue.connection照亮\佇列\佇列 重新導向# 燈位\重定向器Illuminate\Redis\RedisManagerIlluminate\Redis\Connections\Connection##redis.connection請求##Session#Illuminate\ Session \SessionManager##sessionSession(實例) session.store#儲存檔案系統 URLIlluminate\Routing\UrlGenerator##url#######Validator#######Illuminate\Validation\Factory # ########validator##############Validator(實例)######Illuminate\Validation\Validator###### ##### #
FacadeClass服務容器綁定
Appapp
artisan
Illuminate\Auth\AuthManager
Illuminate\Contracts\Auth\Guard
blade.compiler##Broadcast
 #廣播(實例)
 
ConfigIlluminate\Config\Repositoryconfig
Cookie Illuminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
DB (Instance)Illuminate\Database\Connectiondb.connection
Event Illuminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate 
HashIlluminate\Contracts\Hashing\Hasherhash
Lang Illuminate\Translation\Translatortranslator
LogIlluminate\Log\LogManager紀錄
MailIlluminate\Mail\Mailermailer
##auth. password
#auth.password.broker
Illuminate\Queue\QueueManager##queue
#Queue (基底類別)
#重定向 Redis
redisRedis (實例)
#Request 照亮\Http\Request
#
ResponseIlluminate\Contracts\Routing\ResponseFactory 
Response(實例)Illuminate\ Http\Response 
RouteIlluminate\Routing\Routerrouter
##SchemaIlluminate\Database\Schema\Builder 
Illuminate\Session\Store
Illuminate\Filesystem\FilesystemManager
##儲存(實例)Illuminate\Contracts\Filesystem\Filesystem##filesystem.disk
ViewIlluminate\View\Factory#view
View (Instance) Illuminate\View\View 
#本篇文章首發在LearnKu.com 網站上。