聲明:本文並不是博主原創,而是來自對《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
類庫,如CacheManager
、SessionManager
。通讀這些代碼可讓你加深理解。全部的管理器類都繼承自IlluminateSupportManager
基類,他爲每一個具體的管理器提供了不少常見使用的方法。ide
咱們經過在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機制其實像擴展緩存機制同樣簡單。一樣,使用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
接口。記住,上面提供器中的retrieveById
和retrieveByCredentials
方法返回的就是本接口實現類的實例化對象:
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
中切換新的驗證驅動了。
幾乎全部Laravel框架包含的服務提供器都是做爲對象綁定到IoC容器中的。在配置文件app/config/app.php
中有詳細的列表。有時間的話,最好過一遍源碼。這樣,你能瞭解框架都加載了哪些服務,也就是容器中綁定的各式的服務。
好比,PaginationServiceProvider
把paginator
綁定到容器中,他對應的是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
實例,這能使咱們實現的定義請求類的實例可用,甚至在單元測試中也能使用。