Facades
Facades
Facades
簡(jiǎn)介
Facades 為應(yīng)用的 服務(wù)容器 提供了一個(gè)「靜態(tài)」 接口。Laravel 自帶了很多 Facades,可以訪問(wèn)絕大部分功能。Laravel Facades 實(shí)際是服務(wù)容器中底層類的 「靜態(tài)代理」 ,相對(duì)于傳統(tǒng)靜態(tài)方法,在使用時(shí)能夠提供更加靈活、更加易于測(cè)試、更加優(yōu)雅的語(yǔ)法。
所有的 Laravel Facades 都定義在 Illuminate\Support\Facades
命名空間下。所以,我們可以輕松的使用 Facade :
use Illuminate\Support\Facades\Cache; Route::get('/cache', function () { return Cache::get('key'); });
在 Laravel 文檔中,有很多示例代碼都會(huì)使用 Facades 來(lái)演示框架的各種功能。
何時(shí)使用 Facades
Facades 有很多優(yōu)點(diǎn),它提供了簡(jiǎn)單,易記的語(yǔ)法,從而無(wú)需手動(dòng)注入或配置長(zhǎng)長(zhǎng)的類名。此外,由于他們對(duì) PHP 靜態(tài)方法的獨(dú)特調(diào)用,使得測(cè)試起來(lái)非常容易。
然而,在使用 Facades 時(shí),有些地方需要特別注意。使用 Facades 時(shí)最主要的危險(xiǎn)就是會(huì)引起類作用范圍的膨脹。由于 Facades 使用起來(lái)非常簡(jiǎn)單并且不需要注入,就會(huì)使得我們不經(jīng)意間在單個(gè)類中使用許多 Facades ,從而導(dǎo)致類變得越來(lái)越大。然而使用依賴注入的時(shí)候,使用的類越多,構(gòu)造方法就會(huì)越長(zhǎng),在視覺上注意到這個(gè)類有些龐大了。因此在使用 Facades 的時(shí)候,要特別注意控制類的大小,讓類的作用范圍保持短小。
{tip} 在開發(fā)與 Laravel 進(jìn)行交互的第三方擴(kuò)展包時(shí),最好選擇注入 Laravel 契約 而不使用 Facades 。因?yàn)閿U(kuò)展包是在 Laravel 之外構(gòu)建,你無(wú)法使用 Laravel Facades 測(cè)試輔助函數(shù)
Facades 相較于依賴注入
依賴注入的主要好處之一是能交換注入類的實(shí)現(xiàn)。在測(cè)試的時(shí)候非常有用,因?yàn)槟憧梢宰⑷胍粋€(gè) mock 或者 stub,并斷言 stub 上的各種方法。
通常,真正的靜態(tài)方法是不可能 mock 或 stub 的。但是 Facades 使用動(dòng)態(tài)方法對(duì)服務(wù)容器中解析出來(lái)的對(duì)象方法的調(diào)用進(jìn)行了代理,我們也可以像測(cè)試注入類實(shí)例一樣測(cè)試 Facades。比如,像下面的路由:
use Illuminate\Support\Facades\Cache; Route::get('/cache', function () { return Cache::get('key'); });
我們可以帶上我們期望的參數(shù)編寫下面的測(cè)試代碼來(lái)驗(yàn)證 Cache::get
方法:
use Illuminate\Support\Facades\Cache; /** * 一個(gè)基礎(chǔ)功能的測(cè)試。 * * @return void */ public function testBasicExample(){ Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $this->visit('/cache') ->see('value'); }
Facades 相較于輔助函數(shù)
除了 Facades,Laravel 還包含各種 『輔助函數(shù)』 來(lái)實(shí)現(xiàn)這些常用功能,比如生成視圖、觸發(fā)事件、任務(wù)調(diào)度或者發(fā)送 HTTP 響應(yīng)。許多輔助函數(shù)都有與之對(duì)應(yīng)的 Facades 。例如,下面這個(gè) Facades 和輔助函數(shù)的作用是一樣的:
return View::make('profile');return view('profile');
Facade 和輔助函數(shù)之間沒(méi)有實(shí)際的區(qū)別。當(dāng)你使用輔助函數(shù)時(shí),你可以像測(cè)試相應(yīng)的 Facade 那樣進(jìn)行測(cè)試。例如,下面的路由:
Route::get('/cache', function () { return cache('key');});
在底層實(shí)現(xiàn),輔助函數(shù) cache
實(shí)際是調(diào)用 Cache
這個(gè) Facade 的 get
方法。因此,盡管我們使用的是輔助函數(shù),我們依然可以帶上我們期望的參數(shù)編寫下面的測(cè)試代碼來(lái)驗(yàn)證該方法:
use Illuminate\Support\Facades\Cache; /** * 一個(gè)基礎(chǔ)功能的測(cè)試用例。 * * @return void */ public function testBasicExample(){ Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $this->visit('/cache') ->see('value'); }
Facades 工作原理
在 Laravel 應(yīng)用中,F(xiàn)acade 就是一個(gè)可以從容器訪問(wèn)對(duì)象的類。其中核心的部件就是 Facade
類。不管是 Laravel 自帶的 Facades,還是自定義的 Facades,都繼承自 Illuminate\Support\Facades\Facade
類。
Facade
基類使用了__callStatic()
魔術(shù)方法,直到對(duì)象從容器中被解析出來(lái)后,才會(huì)進(jìn)行調(diào)用。在下面的例子中,調(diào)用了 Laravel 的緩存系統(tǒng)。通過(guò)瀏覽這段代碼,可以假定在 Cache
類中調(diào)用了靜態(tài)方法 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]); } }
注意在上面這段代碼中,我們『導(dǎo)入』了 Cache
Facade。這個(gè) Facade 作為訪問(wèn) Illuminate\Contracts\Cache\Factory
接口底層實(shí)現(xiàn)的代理。我們使用 Facade 進(jìn)行的任何調(diào)用都將傳遞給 Laravel 緩存服務(wù)的底層實(shí)例。
如果我們看一下 Illuminate\Support\Facades\Cache
這個(gè)類,你會(huì)發(fā)現(xiàn)類中根本沒(méi)有 get
這個(gè)靜態(tài)方法:
class Cache extends Facade{ /** * 獲取組件的注冊(cè)名稱。 * * @return string */ protected static function getFacadeAccessor() { return 'cache'; } }
Cache
Facade 繼承了 Facade 類,并且定義了 getFacadeAccessor()
方法。這個(gè)方法的作用是返回服務(wù)容器綁定的名稱。當(dāng)用戶調(diào)用 Cache
Facade 中的任何靜態(tài)方法時(shí),Laravel 會(huì)從 服務(wù)容器 中解析 cache
綁定以及該對(duì)象運(yùn)行所請(qǐng)求的方法(在這個(gè)例子中就是 get
方法)。
實(shí)時(shí) Facades
使用實(shí)時(shí) Facades,你可以將應(yīng)用程序中的任何類視為 Facade。為了說(shuō)明這是如何使用的,我們來(lái)看看另一種方法。例如,假設(shè)我們的 Podcast
模型有一個(gè) publish
方法。然而,為了發(fā)布 Podcast,我們需要注入一個(gè) Publisher
實(shí)例:
<?php namespace App; use App\Contracts\Publisher; use Illuminate\Database\Eloquent\Model; class Podcast extends Model{ /** * 發(fā)布 Podcast。 * * @param Publisher $publisher * @return void */ public function publish(Publisher $publisher) { $this->update(['publishing' => now()]); $publisher->publish($this); } }
將發(fā)布者的實(shí)現(xiàn)注入到該方法中,我們可以輕松地測(cè)試這種方法,因?yàn)槲覀兛梢阅M注入的發(fā)布者。但是,它要求我們每次調(diào)用 publish
方法時(shí)都要傳遞一個(gè)發(fā)布者實(shí)例。使用實(shí)時(shí)的 Facades,我們可以保持同樣的可測(cè)試性,而不需要顯式地通過(guò) Publisher
實(shí)例。要生成實(shí)時(shí) Facade
,請(qǐng)?jiān)趯?dǎo)入類的名稱空間中加上 Facades:
<?php namespace App; use Facades\App\Contracts\Publisher; use Illuminate\Database\Eloquent\Model; class Podcast extends Model{ /** * 發(fā)布 Podcast。 * * @return void */ public function publish() { $this->update(['publishing' => now()]); Publisher::publish($this); } }
當(dāng)使用實(shí)時(shí) Facade 時(shí),發(fā)布者實(shí)現(xiàn)將通過(guò)使用 Facades
前綴后出現(xiàn)的接口或類名的部分來(lái)解決服務(wù)容器的問(wèn)題。在測(cè)試時(shí),我們可以使用 Laravel 的內(nèi)置 facade 測(cè)試輔助函數(shù)來(lái)模擬這種方法調(diào)用:
<?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; /** * 一個(gè)測(cè)試演示。 * * @return void */ public function test_podcast_can_be_published() { $podcast = factory(Podcast::class)->create(); Publisher::shouldReceive('publish')->once()->with($podcast); $podcast->publish(); } }
Facade 類參考
在下面你可以找到每個(gè) Facade 類及其對(duì)應(yīng)的底層類。這是一個(gè)查找給定 Facade 類 API 文檔的工具。服務(wù)容器綁定 的關(guān)鍵信息也包含在內(nèi)。
Facade | Class | 服務(wù)容器綁定 |
---|---|---|
App | Illuminate\Foundation\Application | app |
Artisan | Illuminate\Contracts\Console\Kernel | artisan |
Auth | Illuminate\Auth\AuthManager | auth |
Auth (Instance) | Illuminate\Contracts\Auth\Guard | auth.driver |
Blade | Illuminate\View\Compilers\BladeCompiler | blade.compiler |
Broadcast | Illuminate\Contracts\Broadcasting\Factory | |
Broadcast (Instance) | Illuminate\Contracts\Broadcasting\Broadcaster | |
Bus | Illuminate\Contracts\Bus\Dispatcher | |
Cache | Illuminate\Cache\CacheManager | cache |
Cache (Instance) | Illuminate\Cache\Repository | cache.store |
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 | db.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\LogManager | log |
Illuminate\Mail\Mailer | mailer | |
Notification | Illuminate\Notifications\ChannelManager | |
Password | Illuminate\Auth\Passwords\PasswordBrokerManager | auth.password |
Password (Instance) | Illuminate\Auth\Passwords\PasswordBroker | auth.password.broker |
Queue | Illuminate\Queue\QueueManager | queue |
Queue (Instance) | Illuminate\Contracts\Queue\Queue | queue.connection |
Queue (Base Class) | Illuminate\Queue\Queue | |
Redirect | Illuminate\Routing\Redirector | redirect |
Redis | Illuminate\Redis\RedisManager | redis |
Redis (Instance) | Illuminate\Redis\Connections\Connection | redis.connection |
Request | Illuminate\Http\Request | request |
Response | Illuminate\Contracts\Routing\ResponseFactory | |
Response (Instance) | Illuminate\Http\Response | |
Route | Illuminate\Routing\Router | router |
Schema | Illuminate\Database\Schema\Builder | |
Session | Illuminate\Session\SessionManager | session |
Session (Instance) | Illuminate\Session\Store | session.store |
Storage | Illuminate\Filesystem\FilesystemManager | filesystem |
Storage (Instance) | Illuminate\Contracts\Filesystem\Filesystem | filesystem.disk |
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 |