Laravel 核心--Facades 門面

介紹

Facades 為應(yīng)用的 IoC 服務(wù)容器 的類提供了一個(gè)靜態(tài)的接口。Laravel 里面自帶了一些 Facades,如Cache等。Laravel 的門面作為服務(wù)容器中底層類的“靜態(tài)代理”,相比于傳統(tǒng)靜態(tài)方法,在維護(hù)時(shí)能夠提供更加易于測(cè)試、更加靈活、簡(jiǎn)明優(yōu)雅的語法。

解釋

在 Laravel 應(yīng)用這個(gè)上下文里面,一個(gè) Facade 就是一個(gè)類,使用這個(gè)類可以訪問到來自容器里的一個(gè)對(duì)象,這個(gè)功能就是在 Facade 類里面定義的。Laravel 的 Facades 還有任何你自己定義的 Facades,都會(huì)去繼承 Facade 這個(gè)類。

你的 Facade 類只需要實(shí)施一個(gè)的方法:getFacadeAccessor。要在容器里 resolve 什么出來,都是在這個(gè)方法里去做的。Facade 這個(gè)基類里面使用了__callStatic() 魔術(shù)方法,可以延遲到 resolved 對(duì)象上的,來自 Facade 的調(diào)用。

所以,當(dāng)你使用 Facade 調(diào)用的時(shí)候,比如像這樣:Cache:get,laravel 會(huì)從 Ioc 服務(wù)容器 里面 resolves 緩存管理類,然后再去調(diào)用這個(gè)類上面的 get 方法。Laravel 的 Facades 可以去定位服務(wù),它是一種使用 Laravel 的 Ioc 服務(wù)容器 的更方便的語法。

優(yōu)點(diǎn)

Facade 有諸多優(yōu)點(diǎn),其提供了簡(jiǎn)單、易記的語法,讓我們無需記住長(zhǎng)長(zhǎng)的類名即可使用 Laravel 提供的功能特性,此外,由于他們對(duì) PHP 動(dòng)態(tài)方法的獨(dú)到用法,使得它們很容易測(cè)試。

實(shí)際使用

下面的例子,去調(diào)用了一下 Laravel 的緩存系統(tǒng)。先看一下下面這行代碼,你可能會(huì)覺得,這是直接去調(diào)用 Cache 這個(gè)類上面的一個(gè)叫 get 的靜態(tài)的方法。

$value = Cache::get('key');

不過,如果你查看 Illuminate\Support\Facades\Cache 這個(gè)類,你會(huì)發(fā)現(xiàn)這里根本就沒有 get 這個(gè)靜態(tài)方法:

class Cache extends Facade {

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'cache'; }

}

Cache 這個(gè)類繼承了 Facade 這個(gè)基類,它里面定義了一個(gè)叫 getFacadeAccessor() 的方法。注意,這個(gè)方法的干的事就是去返回一個(gè) Ioc 綁定的名字,這里就是 cache。

當(dāng)用戶在引用任何在 Cache 這個(gè) Facade 上的靜態(tài)方法的時(shí)候,Laravel 就會(huì)從 Ioc 服務(wù)容器 里面去 resolves cache 這個(gè)綁定,并且會(huì)去執(zhí)行在對(duì)象上的這個(gè)所請(qǐng)求的方法(這里就是 get 這個(gè)方法)。

所以,我們?cè)谡{(diào)用 Cache::get 的時(shí)候,它的真正的意思是這樣的:

$value = $app->make('cache')->get('key');

導(dǎo)入 Facades

注意,在使用 facade 的時(shí)候,如果控制器里面用到了命名空間,你需要把 Facade 類導(dǎo)入到這個(gè)命名空間里。所有的 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 只需要三個(gè)東西:

  • 一個(gè) IoC 綁定。
  • 一個(gè) Facade 類。
  • 一個(gè) Facade 別名的配置。

在下面我們定義了一個(gè)類:PaymentGateway\Payment

namespace PaymentGateway;

class Payment {

    public function process()
    {
        //
    }

}

我們需要能在 Ioc 服務(wù)容器 里面去 resolve 這個(gè)類。所以,先要去添加一個(gè) Service Provider 綁定:

App::bind('payment', function()
{
    return new \PaymentGateway\Payment;
});

去注冊(cè)這個(gè)綁定最好的方法就是去創(chuàng)建一個(gè)新的 Service Provider ,把它命名為 PaymentServiceProvider ,然后把它綁定到 register 方法上。再去配置 laravel 在 config/app.php 這個(gè)配置文件里加載你的 Service Provider

下一步就是去創(chuàng)建自己的 Facade 類:

use Illuminate\Support\Facades\Facade;

class Payment extends Facade {

    protected static function getFacadeAccessor() {
             return 'payment'; 
    }

}

最后,如果你愿意,可以去給 Facade 添加一個(gè)別名,放到 config/app.php 配置文件里的 aliases 數(shù)組里。

可以去調(diào)用 Payment 類的一個(gè)實(shí)例上的 process 這個(gè)方法了。像這樣:

Payment::process();

何時(shí)使用 Facade

注意

在使用 Facade 也有需要注意的地方,一個(gè)最主要的危險(xiǎn)就是類范圍蠕變。由于Facade 如此好用并且不需要注入,在單個(gè)類中使用過多Facade,會(huì)讓類很容易變得越來越大。使用依賴注入則會(huì)讓此類問題緩解,因?yàn)橐粋€(gè)巨大的構(gòu)造函數(shù)會(huì)讓我們很容易判斷出類在變大。因此,使用Facade的時(shí)候要尤其注意類的大小,以便控制其有限職責(zé)。

注:構(gòu)建與 Laravel 交互的第三方擴(kuò)展包時(shí),最好注入 Laravel 契約而不是使用門面,因?yàn)閿U(kuò)展包在 Laravel 之外構(gòu)建,你將不能訪問 Laravel 的門面測(cè)試輔助函數(shù)。

Facade vs. 依賴注入

依賴注入的最大優(yōu)點(diǎn)是可以替換注入類的實(shí)現(xiàn),這在測(cè)試時(shí)很有用,因?yàn)槟憧梢宰⑷胍粋€(gè)模擬或存根并且在存根上斷言不同的方法。

但是在靜態(tài)類方法上進(jìn)行模擬或存根卻行不通,不過,由于Facade 使用了動(dòng)態(tài)方法對(duì)服務(wù)容器中解析出來的對(duì)象方法調(diào)用進(jìn)行了代理,我們也可以像測(cè)試注入類實(shí)例那樣測(cè)試門面。例如,給定以下路由:

use Illuminate\Support\Facades\Cache;

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

我們可以這樣編寫測(cè)試來驗(yàn)證 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 一樣的功能,例如,下面這個(gè)Facade調(diào)用和輔助函數(shù)調(diào)用是等價(jià)的:

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

Facade和輔助函數(shù)之間并不存在實(shí)質(zhì)性差別,使用輔助函數(shù)的時(shí)候,可以像測(cè)試相應(yīng)門面那樣測(cè)試它們。例如,給定以下路由:

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

在調(diào)用底層, cache 方法會(huì)去調(diào)用 Cache Facade上的 get方法,因此,盡管我們使用這個(gè)輔助函數(shù),我們還是可以編寫如下測(cè)試來驗(yàn)證這個(gè)方法以我們期望的方式和參數(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就是一個(gè)為容器中對(duì)象提供訪問方式的類。該機(jī)制原理由 Facade 類實(shí)現(xiàn)。Laravel 自帶的 Facade,以及我們創(chuàng)建的自定義門面,都會(huì)繼承自 Illuminate\Support\Facades\Facade 基類??梢詤⒖? Facade 實(shí)現(xiàn)原理

Facade 類只需要實(shí)現(xiàn)一個(gè)方法:getFacadeAccessor。正是 getFacadeAccessor方法定義了從容器中解析什么,然后 Facade 基類使用魔術(shù)方法 __callStatic() 從你的門面中調(diào)用解析對(duì)象。

下面的例子中,我們將會(huì)調(diào)用 Laravel 的緩存系統(tǒng),瀏覽代碼后,也許你會(huì)覺得我們調(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]);
    }
}

注意我們?cè)陧敳课恢靡肓?Cache Facade。該門面作為代理訪問底層 Illuminate\Contracts\Cache\Factory 接口的實(shí)現(xiàn)。我們對(duì)門面的所有調(diào)用都會(huì)被傳遞給 Laravel 緩存服務(wù)的底層實(shí)例。

如果我們查看 Illuminate\Support\Facades\Cache 類的源碼,將會(huì)發(fā)現(xiàn)其中并沒有靜態(tài)方法 get

class Cache extends Facade
{
    /**
     * 獲取組件注冊(cè)名稱
     *
     * @return string
     */
    protected static function getFacadeAccessor() { 
        return 'cache'; 
    }
}

Cache Facade 繼承 Facade 基類并定了 getFacadeAccessor方法,該方法的工作就是返回服務(wù)容器綁定類的別名,當(dāng)用戶引用 Cache
類的任何靜態(tài)方法時(shí),Laravel 從服務(wù)容器中解析 cache
綁定,然后在解析出的對(duì)象上調(diào)用所有請(qǐng)求方法(本例中是 get

門面類列表

下面列出了每個(gè)門面及其對(duì)應(yīng)的底層類,這對(duì)深入給定根門面的 API 文檔而言是個(gè)很有用的工具。服務(wù)容器綁定鍵也被包含進(jìn)來:

門面 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
Mail 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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Laravel 的核心概念包括:服務(wù)容器、服務(wù)提供者、門面(Facades)、契約(Contracts)。 服務(wù)容...
    胖福哥閱讀 2,872評(píng)論 0 24
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,853評(píng)論 18 399
  • Facade 布局是在面向?qū)ο缶幊讨薪?jīng)常使用的一種軟件設(shè)計(jì)布局方式。Facade 實(shí)際上是一種包括復(fù)雜函數(shù)庫(kù)的類,...
    OneAPM閱讀 1,573評(píng)論 0 15
  • 原文鏈接 必備品 文檔:Documentation API:API Reference 視頻:Laracasts ...
    layjoy閱讀 8,719評(píng)論 0 121
  • 最近項(xiàng)目中要用到AR 解決了好幾天 今天終于迎來了喜報(bào)。 先說easyAR如果ARviewcontroller ...
    slimsallen閱讀 1,409評(píng)論 0 0

友情鏈接更多精彩內(nèi)容