介紹
Facades
為應(yīng)用的 IoC
服務(wù)容器 的類提供了一個靜態(tài)的接口。Laravel 里面自帶了一些 Facades
,如Cache
等。Laravel 的門面作為服務(wù)容器中底層類的“靜態(tài)代理”,相比于傳統(tǒng)靜態(tài)方法伸刃,在維護時能夠提供更加易于測試、更加靈活僧叉、簡明優(yōu)雅的語法奕枝。
解釋
在 Laravel 應(yīng)用這個上下文里面棺榔,一個 Facade
就是一個類瓶堕,使用這個類可以訪問到來自容器里的一個對象,這個功能就是在 Facade
類里面定義的症歇。Laravel 的 Facades 還有任何你自己定義的 Facades郎笆,都會去繼承 Facade
這個類。
你的 Facade 類只需要實施一個的方法:getFacadeAccessor
忘晤。要在容器里 resolve
什么出來宛蚓,都是在這個方法里去做的。Facade
這個基類里面使用了__callStatic()
魔術(shù)方法设塔,可以延遲到 resolved
對象上的凄吏,來自 Facade 的調(diào)用。
所以,當你使用 Facade 調(diào)用的時候痕钢,比如像這樣:Cache:get
图柏,laravel 會從 Ioc 服務(wù)容器 里面 resolves
緩存管理類,然后再去調(diào)用這個類上面的 get 方法任连。Laravel 的 Facades 可以去定位服務(wù)蚤吹,它是一種使用 Laravel 的 Ioc 服務(wù)容器 的更方便的語法。
優(yōu)點
Facade 有諸多優(yōu)點随抠,其提供了簡單裁着、易記的語法,讓我們無需記住長長的類名即可使用 Laravel 提供的功能特性拱她,此外二驰,由于他們對 PHP 動態(tài)方法的獨到用法,使得它們很容易測試椭懊。
實際使用
下面的例子诸蚕,去調(diào)用了一下 Laravel 的緩存系統(tǒng)。先看一下下面這行代碼氧猬,你可能會覺得背犯,這是直接去調(diào)用 Cache
這個類上面的一個叫 get
的靜態(tài)的方法。
$value = Cache::get('key');
不過盅抚,如果你查看 Illuminate\Support\Facades\Cache
這個類漠魏,你會發(fā)現(xiàn)這里根本就沒有 get 這個靜態(tài)方法:
class Cache extends Facade {
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
Cache 這個類繼承了 Facade 這個基類,它里面定義了一個叫 getFacadeAccessor() 的方法妄均。注意柱锹,這個方法的干的事就是去返回一個 Ioc 綁定的名字,這里就是 cache丰包。
當用戶在引用任何在 Cache 這個 Facade
上的靜態(tài)方法的時候禁熏,Laravel 就會從 Ioc 服務(wù)容器 里面去 resolves cache
這個綁定,并且會去執(zhí)行在對象上的這個所請求的方法(這里就是 get 這個方法)邑彪。
所以瞧毙,我們在調(diào)用 Cache::get 的時候,它的真正的意思是這樣的:
$value = $app->make('cache')->get('key');
導(dǎo)入 Facades
注意寄症,在使用
facade
的時候宙彪,如果控制器里面用到了命名空間,你需要把 Facade 類導(dǎo)入到這個命名空間里有巧。所有的 Facades 都是在全局命名空間下:
<?php namespace App\Http\Controllers;
use Cache;
class PhotosController extends Controller {
/**
* Get all of the application photos.
*
* @return Response
*/
public function index()
{
$photos = Cache::get('photos');
//
}
}
創(chuàng)建 Facades
創(chuàng)建 Facade 只需要三個東西:
- 一個 IoC 綁定释漆。
- 一個 Facade 類。
- 一個 Facade 別名的配置篮迎。
在下面我們定義了一個類:PaymentGateway\Payment
男图。
namespace PaymentGateway;
class Payment {
public function process()
{
//
}
}
我們需要能在 Ioc 服務(wù)容器 里面去 resolve 這個類示姿。所以,先要去添加一個 Service Provider
綁定:
App::bind('payment', function()
{
return new \PaymentGateway\Payment;
});
去注冊這個綁定最好的方法就是去創(chuàng)建一個新的 Service Provider
逊笆,把它命名為 PaymentServiceProvider
峻凫,然后把它綁定到 register
方法上。再去配置 laravel 在 config/app.php
這個配置文件里加載你的 Service Provider
览露。
下一步就是去創(chuàng)建自己的 Facade
類:
use Illuminate\Support\Facades\Facade;
class Payment extends Facade {
protected static function getFacadeAccessor() {
return 'payment';
}
}
最后荧琼,如果你愿意,可以去給 Facade
添加一個別名差牛,放到 config/app.php
配置文件里的 aliases
數(shù)組里命锄。
可以去調(diào)用 Payment 類的一個實例上的 process
這個方法了。像這樣:
Payment::process();
何時使用 Facade
注意
在使用 Facade
也有需要注意的地方偏化,一個最主要的危險就是類范圍蠕變脐恩。由于Facade
如此好用并且不需要注入,在單個類中使用過多Facade
侦讨,會讓類很容易變得越來越大驶冒。使用依賴注入則會讓此類問題緩解,因為一個巨大的構(gòu)造函數(shù)會讓我們很容易判斷出類在變大韵卤。因此骗污,使用Facade
的時候要尤其注意類的大小,以便控制其有限職責沈条。
注:構(gòu)建與 Laravel 交互的第三方擴展包時需忿,最好注入 Laravel 契約而不是使用門面,因為擴展包在 Laravel 之外構(gòu)建蜡歹,你將不能訪問 Laravel 的門面測試輔助函數(shù)屋厘。
Facade vs. 依賴注入
依賴注入的最大優(yōu)點是可以替換注入類的實現(xiàn),這在測試時很有用月而,因為你可以注入一個模擬或存根并且在存根上斷言不同的方法汗洒。
但是在靜態(tài)類方法上進行模擬或存根卻行不通,不過父款,由于Facade
使用了動態(tài)方法對服務(wù)容器中解析出來的對象方法調(diào)用進行了代理溢谤,我們也可以像測試注入類實例那樣測試門面。例如铛漓,給定以下路由:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
我們可以這樣編寫測試來驗證 Cache::get
方法以我們期望的方式被調(diào)用:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Facade vs. 輔助函數(shù)
除了Facade
之外溯香,Laravel 還內(nèi)置了許多輔助函數(shù)用于執(zhí)行通用任務(wù)鲫构,比如生成視圖浓恶、觸發(fā)事件、分配任務(wù)结笨,以及發(fā)送 HTTP 響應(yīng)等包晰。很多輔助函數(shù)提供了和相應(yīng) Facade
一樣的功能湿镀,例如,下面這個Facade
調(diào)用和輔助函數(shù)調(diào)用是等價的:
return View::make('profile');
return view('profile');
Facade
和輔助函數(shù)之間并不存在實質(zhì)性差別伐憾,使用輔助函數(shù)的時候勉痴,可以像測試相應(yīng)門面那樣測試它們。例如树肃,給定以下路由:
Route::get('/cache', function () {
return cache('key');
});
在調(diào)用底層蒸矛, cache
方法會去調(diào)用 Cache Facade
上的 get
方法,因此胸嘴,盡管我們使用這個輔助函數(shù)雏掠,我們還是可以編寫如下測試來驗證這個方法以我們期望的方式和參數(shù)被調(diào)用:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Facade 工作原理
在 Laravel 應(yīng)用中,Facade
就是一個為容器中對象提供訪問方式的類劣像。該機制原理由 Facade
類實現(xiàn)乡话。Laravel 自帶的 Facade
,以及我們創(chuàng)建的自定義門面耳奕,都會繼承自 Illuminate\Support\Facades\Facade
基類绑青。可以參考 Facade 實現(xiàn)原理
Facade
類只需要實現(xiàn)一個方法:getFacadeAccessor
屋群。正是 getFacadeAccessor
方法定義了從容器中解析什么闸婴,然后 Facade
基類使用魔術(shù)方法 __callStatic()
從你的門面中調(diào)用解析對象。
下面的例子中芍躏,我們將會調(diào)用 Laravel
的緩存系統(tǒng)掠拳,瀏覽代碼后,也許你會覺得我們調(diào)用了 Cache
的靜態(tài)方法 get
:
<?php
namespace App\Http\Controllers;
use Cache;
use App\Http\Controllers\Controller;
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
纸肉。該門面作為代理訪問底層 Illuminate\Contracts\Cache\Factory
接口的實現(xiàn)溺欧。我們對門面的所有調(diào)用都會被傳遞給 Laravel 緩存服務(wù)的底層實例。
如果我們查看 Illuminate\Support\Facades\Cache
類的源碼柏肪,將會發(fā)現(xiàn)其中并沒有靜態(tài)方法 get
:
class Cache extends Facade
{
/**
* 獲取組件注冊名稱
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'cache';
}
}
Cache Facade
繼承 Facade
基類并定了 getFacadeAccessor
方法姐刁,該方法的工作就是返回服務(wù)容器綁定類的別名,當用戶引用 Cache
類的任何靜態(tài)方法時烦味,Laravel 從服務(wù)容器中解析 cache
綁定聂使,然后在解析出的對象上調(diào)用所有請求方法(本例中是 get
)
門面類列表
下面列出了每個門面及其對應(yīng)的底層類,這對深入給定根門面的 API 文檔而言是個很有用的工具谬俄。服務(wù)容器綁定鍵也被包含進來:
門面 Facade
|
類 class
|
服務(wù)容器綁定 |
---|---|---|
App | Illuminate\Foundation\Application | app |
Artisan | Illuminate\Contracts\Console\Kernel | artisan |
Auth | Illuminate\Auth\AuthManager | auth |
Blade | Illuminate\View\Compilers\BladeCompiler | blade.compiler |
Bus | Illuminate\Contracts\Bus\Dispatcher | |
Cache | Illuminate\Cache\Repository | cache |
Config | Illuminate\Config\Repository | config |
Cookie | Illuminate\Cookie\CookieJar | cookie |
Crypt | Illuminate\Encryption\Encrypter | encrypter |
DB | Illuminate\Database\DatabaseManager | db |
DB(Instance) | Illuminate\Database\Connection | |
Event | Illuminate\Events\Dispatcher | events |
File | Illuminate\Filesystem\Filesystem | files |
Gate | Illuminate\Contracts\Auth\Access\Gate | |
Hash | Illuminate\Contracts\Hashing\Hasher | hash |
Lang | Illuminate\Translation\Translator | translator |
Log | Illuminate\Log\Writer | log |
Illuminate\Mail\Mailer | mailer |
|
Notification | Illuminate\Notifications\ChannelManager | |
Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
Queue | Illuminate\Queue\QueueManager | queue |
Queue(Instance) | Illuminate\Contracts\Queue\Queue | queue |
Queue(Base Class) | Illuminate\Queue\Queue | |
Redirect | Illuminate\Routing\Redirector | redirect |
Redis | Illuminate\Redis\Database | redis |
Request | Illuminate\Http\Request | request |
Response | Illuminate\Contracts\Routing\ResponseFactory | |
Route | Illuminate\Routing\Router | router |
Schema | Illuminate\Database\Schema\Blueprint | |
Session | Illuminate\Session\SessionManager | session |
Session(Instance) | Illuminate\Session\Store | |
Storage | Illuminate\Contracts\Filesystem\Factory | filesystem |
URL | Illuminate\Routing\UrlGenerator | url |
Validator | Illuminate\Validation\Factory | validator |
Validator(Instance) | Illuminate\Validation\Validator | |
View | Illuminate\View\Factory | view |
View(Instance) | Illuminate\View\View |