Laravel 有幾個 Manager
類,用來管理創(chuàng)建基于驅(qū)動的組件。這些類包括緩存、session 、認(rèn)證和隊列組件。管理者類負(fù)責(zé)基于應(yīng)用程序的配置建立一個特定的驅(qū)動實現(xiàn)。例如,CacheManager
類可以建立 APC 、 Memcached 、文件和各種其他的緩存驅(qū)動實現(xiàn)。
這些管理者都擁有 extend
方法,可以簡單地用它來注入新的驅(qū)動解析功能到管理者。我們將會在下面的例子,隨著講解如何為它們注入自定義驅(qū)動支持,涵蓋這些管理者的內(nèi)容。
注意: 建議花點時間來探索 Laravel 附帶的各種
Manager
類,例如:CacheManager
和SessionManager
??催^這些類將會讓你更徹底了解 Laravel 表面下是如何運作。所有的管理者類繼承Illuminate\Support\Manager
基礎(chǔ)類,它提供一些有用、常見的功能給每一個管理者。
為了擴(kuò)展 Laravel 緩存功能,我們將會使用 CacheManager
的 extend
方法,這方法可以用來綁定一個自定義驅(qū)動解析器到管理者,并且是全部的管理者類通用的。例如,注冊一個新的緩存驅(qū)動名為「mongo」,我們將執(zhí)行以下操作:
Cache::extend('mongo', function($app){ return Cache::repository(new MongoStore);});
傳遞到 extend
方法的第一個參數(shù)是驅(qū)動的名稱。這將會對應(yīng)到你的 config/cache.php
配置文件里的 driver
選項。第二個參數(shù)是個應(yīng)該返回 Illuminate\Cache\Repository
實例的閉包。 $app
將會被傳遞到閉包,它是 Illuminate\Foundation\Application
和服務(wù)容器的實例。
Cache::extend
的調(diào)用可以在新的 Laravel 應(yīng)用程序默認(rèn)附帶的 App\Providers\AppServiceProvider
的 boot
方法中完成,或者你可以建立自己的服務(wù)提供者來放置這個擴(kuò)展 - 記得不要忘記在 config/app.php
的提供者數(shù)組注冊提供者。
要建立自定義緩存驅(qū)動,首先需要實現(xiàn) Illuminate\Contracts\Cache\Store
contract 。所以,我們的 MongoDB 緩存實現(xiàn)將會看起來像這樣:
class MongoStore implements Illuminate\Contracts\Cache\Store { 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 連接來實現(xiàn)這些方法。當(dāng)實現(xiàn)完成,就可以完成自定義驅(qū)動注冊:
Cache::extend('mongo', function($app){ return Cache::repository(new MongoStore);});
如果你正在考慮要把自定義緩存驅(qū)動代碼放在哪里,請考慮把它放上 Packagist !或者,你可以在 app
的目錄中建立 Extensions
命名空間。記得 Laravel 沒有嚴(yán)格的應(yīng)用程序架構(gòu),你可以依照喜好自由的組織應(yīng)用程序。
自定義 session 驅(qū)動來擴(kuò)展 Laravel 和擴(kuò)展緩存系統(tǒng)一樣簡單。我們將會再一次使用 extend
方法來注冊自定義代碼:
Session::extend('mongo', function($app){ // Return implementation of SessionHandlerInterface});
你應(yīng)該把 session 擴(kuò)展代碼放置在 AppServiceProvider
的 boot
方法里。
要注意我們的自定義緩存驅(qū)動應(yīng)該要實現(xiàn) SessionHandlerInterface
。這個接口只包含少數(shù)需要實現(xiàn)的簡單方法。一個基本的 MongoDB 實現(xiàn)會看起來像這樣:
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
方法通常會被用在基于文件的 session 保存系統(tǒng)。因為 Laravel 附帶一個 file
session 驅(qū)動,幾乎不需要在這個方法放任何東西。你可以讓它留空。PHP 要求我們?nèi)崿F(xiàn)這個方法,事實上明顯是個差勁的接口設(shè)計 (我們將會晚點討論它)。
close
方法,就像 open
方法,通常也可以忽略。對大部份的驅(qū)動來說,并不需要它。
read
方法應(yīng)該返回與給定 $sessionId
關(guān)聯(lián)的 session 數(shù)據(jù)的字串形態(tài)。當(dāng)你的驅(qū)動取回或保存 session 數(shù)據(jù)時不需要做任何序列化或進(jìn)行其他編碼,因為 Laravel 將會為你進(jìn)行序列化
write
方法應(yīng)該寫入給定 $data
字串與 $sessionId
的關(guān)聯(lián)到一些永久存儲系統(tǒng),例如:MongoDB、 Dynamo、等等。
destroy
方法應(yīng)該從永久存儲移除與 $sessionId
關(guān)聯(lián)的數(shù)據(jù)。
gc
方法應(yīng)該銷毀所有比給定 $lifetime
UNIX 時間戳記還舊的 session 數(shù)據(jù)。對于會自己過期的系統(tǒng)如 Memcached 和 Redis,這個方法可以留空。
當(dāng) SessionHandlerInterface
實現(xiàn)完成,我們準(zhǔn)備好要用 Session 管理者注冊它:
Session::extend('mongo', function($app){ return new MongoHandler;});
當(dāng) session 驅(qū)動已經(jīng)被注冊,我們可以在 config/session.php
配置文件使用 mongo
驅(qū)動。
注意: 記住,如果你寫了個自定義 session 處理器,請在 Packagist 分享它!
認(rèn)證可以用與緩存和 session 功能相同的方法擴(kuò)展。再一次的,使用我們已經(jīng)熟悉的 extend
方法:
Auth::extend('riak', function($app){ // 返回 Illuminate\Contracts\Auth\UserProvider 的實現(xiàn)});
UserProvider
實現(xiàn)只負(fù)責(zé)從永久存儲系統(tǒng)抓取 Illuminate\Contracts\Auth\Authenticatable
實現(xiàn),存儲系統(tǒng)例如: MySQL 、 Riak ,等等。這兩個接口讓 Laravel 認(rèn)證機制無論用戶數(shù)據(jù)如何保存或用什么種類的類來代表它都能繼續(xù)運作。
讓我們來看一下 UserProvider
contract :
interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials);}
retrieveById
函數(shù)通常接收一個代表用戶的數(shù)字鍵,例如:MySQL 數(shù)據(jù)庫的自動遞增 ID。這方法應(yīng)該取得符合 ID 的 Authenticatable
實現(xiàn)并返回。
retrieveByToken
函數(shù)用用戶唯一的 $identifier
和保存在 remember_token
字段的「記住我」 $token
來取得用戶。跟前面的方法一樣,應(yīng)該返回 Authenticatable
的實現(xiàn)。
updateRememberToken
方法用新的 $token
更新 $user
的 remember_token
字段。新 token 可以是在「記住我」成功地登錄時,傳入一個新的 token,或當(dāng)用戶注銷時傳入一個 null。
retrieveByCredentials
方法接收當(dāng)嘗試登錄應(yīng)用程序時,傳遞到 Auth::attempt
方法的憑證數(shù)組。這個方法應(yīng)該接著「查找」底層使用的永久存儲,找到符合憑證的用戶。這個方法通常會對 $credentials['username']
用「 where 」條件查找。 并且應(yīng)該返回一個 UserInterface
接口的實現(xiàn)。這個方法不應(yīng)該嘗試做任何密碼驗證或認(rèn)證。
validateCredentials
方法應(yīng)該通過比較給定的 $user
與 $credentials
來驗證用戶。舉例來說,這個方法可以比較 $user->getAuthPassword()
字串跟 Hash::make
后的 $credentials['password']
。這個方法應(yīng)該只驗證用戶的憑證數(shù)組并且返回布爾值。
現(xiàn)在我們已經(jīng)看過 UserProvider
的每個方法,接著來看一下 Authenticatable
。記住,提供者應(yīng)該從 retrieveById
和 retrieveByCredentials
方法返回這個接口的實現(xiàn):
interface Authenticatable { public function getAuthIdentifier(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName();}
這個接口很簡單。 The getAuthIdentifier
方法應(yīng)該返回用戶的「主鍵」。在 MySQL 后臺,同樣,這將會是個自動遞增的主鍵。getAuthPassword
應(yīng)該返回用戶哈希過的密碼。這個接口讓認(rèn)證系統(tǒng)可以與任何用戶類一起運作,無論你使用什么 ORM 或保存抽象層。默認(rèn),Laravel 包含一個實現(xiàn)這個接口的 User
類在 app
文件夾里,所以你可以參考這個類當(dāng)作實現(xiàn)的例子。
最后,當(dāng)我們已經(jīng)實現(xiàn)了 UserProvider
,我們準(zhǔn)備好用 Auth
facade 來注冊擴(kuò)展:
Auth::extend('riak', function($app){ return new RiakUserProvider($app['riak.connection']);});
用 extend
方法注冊驅(qū)動之后,在你的 config/auth.php
配置文件切換到新驅(qū)動。
幾乎每個 Laravel 框架引入的服務(wù)提供者都會綁定對象到服務(wù)容器中。你可以在 config/app.php
配置文件中找到應(yīng)用程序的服務(wù)提供者清單。如果你有時間,你應(yīng)該瀏覽過這里面每一個提供者的源代碼。通過這樣做,你將會更了解每一個提供者添加什么到框架,以及用什么鍵值來綁定各種服務(wù)到服務(wù)容器。
例如, HashServiceProvider
綁定 hash
做為鍵值到服務(wù)容器,它將解析成 Illuminate\Hashing\BcryptHasher
實例。你可以在應(yīng)用程序中覆寫這個 IoC 綁定,輕松地擴(kuò)展并覆寫這個類。例如:
<?php namespace App\Providers;class SnappyHashProvider extends \Illuminate\Hashing\HashServiceProvider { public function boot() { parent::boot(); $this->app->bindShared('hash', function() { return new \Snappy\Hashing\ScryptHasher; }); }}
要注意的是這個類擴(kuò)展 HashServiceProvider
,不是默認(rèn)的 ServiceProvider
基礎(chǔ)類。當(dāng)你擴(kuò)展了服務(wù)提供者,在 config/app.php
配置文件把 HashServiceProvider
換成你擴(kuò)展的提供者名稱。
這是被綁定在容器的所有核心類的一般擴(kuò)展方法。實際上,每個以這種方式綁定在容器的核心類都可以被覆寫。再次強調(diào),看過每個框架引入的服務(wù)提供者將會使你熟悉:每個類被綁在容器的哪里、它們是用什么鍵值綁定。這是個好方法可以了解更多關(guān)于 Laravel 如何結(jié)合它們。
更多建議: