Laravel深刻學習7 - 框架的擴展

聲明:本文並不是博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,固然也不是原汁原味的翻譯,能保證90%的原汁性,另外由於是理解翻譯,確定會有錯誤的地方,歡迎指正。php

歡迎轉載,轉載請註明出處,謝謝!數據庫

框架的擴展

介紹

Laravel爲咱們提供了不少自定義系統組件的擴展點,你甚至能夠徹底的替換掉他們。好比,哈希結構是由HasherInterface接口約定的,你能夠根據你的應用來實現本身的需求。你也能夠擴展本身的Request對象,並添加本身的「輔助」方法。甚至咱們徹底添加本身的認證、緩存、SESSION驅動。bootstrap

Laravel組件擴展一般有兩種方法:向IoC容器中綁定本身的接口實現;痛過使用「工廠模式」實現的Manager類註冊本身的擴展。本章將探索多種擴展框架的方法,以及必要的測試代碼。數組

擴展方式緩存

牢記Laravel組件擴展的兩種方式:IoC容器綁定和使用Manager類註冊。類庫管理類以工廠模式實現,負責諸如緩存、session等驅動的實例化。session

類庫管理類和工廠方法

Laravel有諸多Manager類庫來管理建立那些基於驅動的組件。它包括緩存、session、認證、隊列組件。管理類的職責是根據應用的配置來建立特殊的驅動實例。例如,CacheManager能夠建立APC、Memcached、Native、以及其餘緩存驅動系統的實現。閉包

全部這些管理器都有個extend方法,能夠輕易的將新的驅動功能注入到其中。對於上面全部的管理器,咱們經過實例來演示如何注入咱們自定義的那些服務驅動。app

學習咱們本身的管理器框架

請花一些事件來熟悉下Laravel提供的Manager類庫,如CacheManagerSessionManager。通讀這些代碼可讓你加深理解。全部的管理器類都繼承自IlluminateSupportManager基類,他爲每一個具體的管理器提供了不少常見使用的方法。ide

緩存 Cache

咱們經過在CacheManager類中的extend方法綁定自定義的驅動解析器來擴展Laravel的緩存機制。好比,註冊一個「mongo」驅動到緩存驅動中,代碼以下:

Cache::extend('mongo', function($app)
{
    // Return IlluminateCacheRepository instance...
});

傳入method方法的第一個參數是驅動名稱。一般和app/config/app.php配置文件中的driver選項匹配。第二個參數是返回IlluminateCacheRepository實例的閉包。閉包需要傳入繼承自IlluminateFoundationApplication和IoC容器的實例化對象$app

對於咱們自定義的緩存驅動,要實現IlluminateCacheStoreInterface接口的約定。所以,咱們的MongoDB緩存實現代碼爲這樣:

class MongoStore implements IlluminateCacheStoreInterface {

    public function get($key) {}
    public function put($key, $value, $minutes) {}
    public function increment($key, $value = 1) {}
    public function decrement($key, $value = 1) {}
    public function forever($key, $value) {}
    public function forget($key) {}
    public function flush() {}

}

咱們只須要使用MongoDB鏈接來實現上述類中的各個方法便可。當完成上述實現,就可完成咱們自定義的驅動註冊:

use IlluminateCacheRepository;

Cache::extend('mongo', function($app)
{
    return new Repository(new MongoStore);
}

如上可見,咱們可以使用IlluminateCacheRepository來直接建立咱們自定義的緩存驅動,而無需建立本身的(Repository)倉庫類。

若是不知道該把代碼放在哪裏,能夠考慮放到Packagist!或者在應用主目錄下建立一個Extensions命名空間的目錄存放。例如你的應用起名叫個Snappy,能夠把這個擴展放到app/Snappy/Extensions/MongoStore.php。Laravel建立的應用沒有死板的各類結構的要求,根據你的喜愛本身來組織就好。

何處引入擴展

若是你還在考慮該在何處引入擴展,不妨繼續使用服務提供器。咱們已經討論過這是組織咱們代碼的一個利器,要善用利器。

Session 會話

擴展Session機制其實像擴展緩存機制同樣簡單。一樣,使用extend方法來註冊咱們本身的驅動:

Session::extend('mongo', function($app)
{
    // Return implementation of SessionHandlerInterface
});

注意,咱們自定義的驅動需要實現SessionHandlerInterface接口。此接口是被包含在PHP5.4+核心中的。若是你在使用PHP5.3,Laravel也是向前兼容的,Laravel已經爲您定義好此接口。接口中包含了一些咱們需要實現的方法。一個基於MongoDB實現的驅動代碼以下:

class MongoHandler implements SessionHandlerInterface {

    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}

}

這些方法不像StoreInterface接口那樣秒懂,讓咱們來快速過一遍這些方法:

  • open方法通常是在基於文件系統的實現中被用到。自從Laravel基於以PHP自身的本地存儲實現了native會話驅動方式,你基本上不須要在本方法中添加任何東西了。你能夠把他留空。PHP這種接口設計很顯然是一種很差的設計方式(咱們後續討論)。

  • close方法同open方法,一般不用理會。大多數驅動都不須要他。

  • read方法返回根據$sessionId變量關聯session數據的字符串。這裏不須要進行序列化或者其餘類型的轉義,Laravel已經幫你進行了處理。

  • write方法根據$sessionId關聯session數據將$data數據寫入持久化存儲系統中,如MongoDB、Dynamo等。

  • gc方法將根據$lifetime指定UNIX時間戳銷燬以前全部的session數據。對於能夠自動刪除過時數據的系統好比Memcached或者Redis,該方法留空便可。

當實現SessionHandlerInterface以後,咱們就能夠向Session管理器中註冊他了:

Session::extend('mongo', function($app)
{
    return new MongoHandler;
});

當會話驅動註冊以後,在配置文件app/config/session.php中指定mongo配置就能使用咱們自定義的session驅動了。

分享你的成果

記住,若是你實現了本身的session驅動,能夠在Packagist上分享給你們!

認證

相似緩存和會話擴展,咱們繼續從method方法開始:

Session::extend('mongo', function($app)
{
    return new MongoHandler;
});

接口UserProviderInterface的實現指責是從各類持久化存儲系統如MySQL、Riak等中獲取數據,並返回接口UserInterface實現的對象。這兩個接口可讓Laravel專一於驗證自己,而無需關心用戶數據的存儲實現,以及用戶對象是那種類來表示的問題。

讓咱們先看一下UserProviderInterface接口:

interface UserProviderInterface {

    public function retrieveById($identifier);
    public function retrieveByCredentials(array $credentials);
    public function validateCredentials(UserInterface $user, array $credentials);

}

retrieveById方法一般接收的參數是一個惟一標識符,例如MySQL數據庫中的主鍵自增索引ID。該方法將獲取$identifier對應的數據,並返回接口UserInterface實現類的實例化對象。

當用戶試圖登陸系統時,retrieveByCredentials方法接收參數是一個驗證數組,同時他也是傳入Auth::attempt的參數。該方法會「查詢」給定的用戶驗證數據,一般,它會根據$credentials['username']運行一個「查詢」條件語句來匹配數據。請不要用本方法進行任何密碼的校驗或驗證。

方法validateCredentials會經過對比$user$credentials來驗證用戶。例如,方法中會對$user->getAuthPassword();獲得的字符串和$credentials['password']經過Hash::make加密後的結果進行對比。

上面咱們探索了UserProviderInterface中的各個方法,接下來看一看UserInterface接口。記住,上面提供器中的retrieveByIdretrieveByCredentials方法返回的就是本接口實現類的實例化對象:

interface UserInterface {

    public function getAuthIdentifier();
    public function getAuthPassword();

}

接口很簡單。getAuthIdentifier返回用戶的「主鍵索引」。在MySQL後臺存儲系統中,這裏就指代用戶表的主鍵自增索引。getAuthPassword返回用戶密碼的哈希值。這種接口的實現方式使認證系統和User類徹底分離,並能在各類類型的實現下正常工做,而不用關心咱們用的是ORM方式仍是其餘存儲層實現的方式。在app/models下,Laravel已經實現了該接口的User類,你能夠參考下這個類。

實現接口UserProviderInterface後,咱們就作好了將咱們的擴展註冊進Auth門面的準備:

Auth::extend('riak', function($app)
{
    return new RiakUserProvider($app['riak.connection']);
});

method註冊以後,咱們就能在app/config/auth.php中切換新的驗證驅動了。

基於IoC容器的擴展

幾乎全部Laravel框架包含的服務提供器都是做爲對象綁定到IoC容器中的。在配置文件app/config/app.php中有詳細的列表。有時間的話,最好過一遍源碼。這樣,你能瞭解框架都加載了哪些服務,也就是容器中綁定的各式的服務。

好比,PaginationServiceProviderpaginator綁定到容器中,他對應的是IlluminatePaginationEnvironment的實例。你能夠很容易的經過擴展重寫這些類並從新綁定到容器中。又如,咱們來擴展下基礎的Environment類:

namespace SnappyExtensionsPagination;

class Environment extends IlluminatePaginationEnvironment {

    //

}

擴展完成以後,在建立一個新的SnappyPaginationProvider服務提供器,並在boot方法中替換掉原有的paginator:

class SnappyPaginationProvider extends PaginationServiceProvider {

    public function boot()
    {
        App::bind('paginator', function()
        {
            return new SnappyExtensionsPaginationEnvironment;
        }

        parent::boot();
    }

}

注意,本類繼承的PaginationServiceProvider,並非默認的ServiceProvider。當完成擴展以後要記住app/config/app.php中替換成本身的擴展名稱。

這就是對容器中已經綁定的核心類庫進行擴展的方法。實際上,全部核心類庫都能用這種方式進行重寫。深刻閱讀源碼,能幫你熟練知曉Laravel是怎麼將各個部分組織在一塊兒運轉的。

請求的擴展

由於在每一個請求的生命週期中,請求是很早就會被實例化的一個最基礎的部分,因此Request的擴展會有稍許不一樣。

首先,仍是像往常同樣實現一個子類:

namespace QuickBillExtensions;

class Request extends IlluminateHttpRequest {

    // Custom, helpful methods here...

}

而後,打開bootstrap/start.php,它是請求到達應用時最先被包含的文件。注意這裏被執行的第一個動做就是建立Laravel的實例化對象$app

$app = new IlluminateFoundationApplication;

當對象被建立,同時也會建立IlluminateHttpRequest實例,並以request鍵綁定到IoC容器中。這裏,咱們應該另闢途徑實現一個類來做爲「默認的」請求類型,對不對?還好,requestClass方法就能幫咱們那實現它!因此咱們須要在bootstrap/start.php文件開頭加上這樣一行:

use IlluminateFoundationApplication;

Application::requestClass('QuickBillExtensionsRequest');

當添加玩自定義的請求類後,Laravel在任何地方都能用到咱們的這個自定義的Request實例,這能使咱們實現的定義請求類的實例可用,甚至在單元測試中也能使用。

相關文章
相關標籤/搜索