マネージャーとファクトリー
ドライバーベースのコンポーネントの作成を管理する、複数のManager
クラスをLaravelは持っています。それらはキャッシュ、セッション、認証、キューコンポーネントです。Managerクラスはアプリケーションの設定に基づいて、特定のドライバーの実装を作成することに責任を持っています。例えば、CacheManager
クラスはAPC、Memcached、ファイル、その他のキャッシュの実装を生成することができます。
各マネージャーは、新しいドライバーのリゾルバーを簡単にマネージャへ登録できるように、extend
メソッドを備えています。以降では例と一緒に、各カスタムドライバーのサポートをどのように導入するかと、マネージャーについて説明していきます。
Note: どうぞ時間を取って、
CacheManager
やSessionManager
のように、Laravelに初めから用意してある様々なManager
クラスを調べてみてください。こうしたクラスを徹底的に読めば、Laravelの内部動作を完全に理解することができます。全Managerクラスは、Illuminate\Support\Manager
ベースクラスを拡張しており、各Managerに共通で便利な機能を提供しています。
キャッシュ
Laravelのキャッシュ機能を拡張するには、CacheManager
のextend
メソッドを使用します。このメソッドはマネージャーに対し、カスタムドライバーのリゾルバーを結合するために使用され、全Managerクラスで使用できます。例えば、"mongo"という名前のキャッシュドライバーを登録するには、次のようになります。
Cache::extend('mongo', function($app)
{
return Cache::repository(new MongoStore);
});
extend
メソッドの最初の引数は、ドライバーの名前です。この名前は、config/cache.php
設定ファイルの中のdriverオプションと対応しています。2つ目の引数はクロージャーで、Illuminate\Cache\Repository
インスタンスをリターンしなければなりません。クロージャーは、Illuminate\Foundation\Application
のインスタンスであり、サービスコンテナでもある、$app
インスタンスへ渡されます。
インストール後のLaravelアプリケーションに存在している、App\Providers\AppServiceProvider
中のboot
メソッドでCache::extend
は呼び出される必要があります。もしくは拡張を記述するために独自のサービスプロバイダーを作成することもできます。その場合、config/app.php
のproviders配列へ指定するのを忘れないでください。
カスタムキャッシュドライバーを作成するには、Illuminate\Contracts\Cache\Store
契約を実装する必要があります。ですから、MongoDBキャッシュの実装は、以下のようになるでしょう。
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接続を使用して実装する必要があります。実装を完了したら、カスタムドライバーの登録も完成させましょう。
Cache::extend('mongo', function($app)
{
return Cache::repository(new MongoStore);
});
カスタムキャッシュドライバーコードをどこへ公開しようか迷っているなら、Packagist
で使用できるようにすることを考えてください!もしくは、appディレクトリーの中にExtensions
名前空間を作成することもできます。しかし、Laravelは厳格なアプリケーション構造を持っていないので、好みに合わせ、自由に構造を決めて良いことを覚えておきましょう。
セッション
Laravelにカスタマイズしたセッションドライバーを拡張するのは、キャッシュシステムを拡張するのと同様に簡単です。カスタムコードを登録するため、セッションでもextend
メソッドを使用します。
Session::extend('mongo', function($app)
{
// Return SessionHandlerInterfaceの実装を返す
});
セッションをどこで拡張するか
セッションの拡張は、AppServiceProvider
のboot
メソッドの中へコードしましょう。
セッションの拡張をコーディングする
カスタムセッションドライバーはSessionHandlerInterface
を実装する必要があることに注意してください。このインターフェイスは、実装する必要のあるシンプルないくつかのメソッドを含んでいます。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には最初からfile
セッションドライバーが含まれているため、このメソッドにはほとんど何も記述する必要はないでしょう。空っぽのスタブのままにしておけます。PHPがこのメソッドを私達に実装しろと要求しているのは、実際、インターフェイスの設計がまずいからに過ぎません。(後ほど説明します。)close
メソッドは、open
メソッドと同様に、通常無視できます。ほとんどのドライバーでは、必要ありません。read
メソッドは、指定された$sessionId
に関係付けられた、文字列のセッションデーターをリターンする必要があります。ドライバーからセッションデーターを取得したり、保存したりする時に、シリアライズや他のエンコードを行う必要はありません。Laravelがシリアライズを行います。write
メソッドは$data
文字列を$sessionId
と関連付けて、MongoDBやDynamo、その他の持続的なストレージシステムに保存する必要があります。destroy
メソッドは、$sessionId
に関連付けられたデーターを持続的なストレージから削除します。gc
メソッドは、指定されたUNIXタイムスタンプの$lifetime
より古いセッションデーターを全部破棄しなくてはなりません。MemcachedやRedisのように、自分で古いデーターを破棄するシステムでは、このメソッドは空のままにします。
SessionHandlerInterface
を実装したら、セッションマネージャーに登録する準備ができました。
Session::extend('mongo', function($app)
{
return new MongoHandler;
});
セッションドライバーを登録し終えたら、mongo
ドライバーをconfig/session.php
ファイルで使用できます。
注目: カスタムセッションハンドラーを書いたら、Packagistで他の開発者と共有できることを思い出してください。
認証
キャッシュやセッション機能と同じ方法で、認証も拡張できます。既にお馴染みの、extend
メソッドを今回も使用します。
Auth::extend('riak', function($app)
{
// Illuminate\Contracts\Auth\UserProviderの実装を返す
});
UserProvider
の実装は、Illuminate\Contracts\Auth\Authenticatable
実装をMySQLやRiakなどの永続する保存システムから取得することだけに責任を持ちます。これらの2つのインターフェイスは、ユーザーデーターがどのように保存されているか、どのクラスがユーザーを表しているかにかかわらず、Laravel認証システムのメカニズムを機能させ続けてくれます。
UserProvider
契約を見てみましょう。
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
関数は、典型的にはMySQLデータベースの自動増加IDのような、ユーザーを表す数字キーを受け付けます。IDに一致するAuthenticatable
の実装が取得され、メソッドにより返されます。
retrieveByToken
関数は、一意の$identifier
と、remember_token
フィールドに保存されている"Remember
me"トークンにより、ユーザーを取得します。前のメソッドと同様に、Authenticatable
の実装を返さなくてはなりません。
updateRememberToken
メソッドは、$user
のremember_token
フィールドを新しい$token
で更新します。新しいトークンは"Remember
me"によるログインが成功した時に生成される真新しいトークンでも良いですし、もしくはユーザーがログアウトした時はnullでもかまいません。
retrieveByCredentials
メソッドは、アプリケーションにログインするために、Auth::attempt
メソッドに渡された、ログイン情報の配列を受け取ります。メソッドはユーザー情報に一致するユーザーが、裏に存在している持続的なストレージに存在しているか確認するため「クエリー」する必要があります。このメソッドは通常、"where"条件で$credentials['username']
のクエリーを実行することでしょう。このメソッドは、UserInterface
の実装を返す必要があります。このメソッド中で、パスワードのバリデーションや認証を試みてはいけません。
validateCredentials
メソッドは指定された$user
とユーザーを認証するための$credentials
を比較する必要があります。例えば、このメソッドでは
$user->getAuthPassword()
文字列と、Hash::make
した$credentials['password']
を比較する必要があるでしょう。このメソッドでは、ユーザーの認証情報の有効性を確認することだけをおこない、論理値を返す必要があります。
これでUserProvider
のメソッドをそれぞれ学習し終えました。今度は、Authenticatable
に注目していきましょう。プロバイダーはこのインターフェイスの実装をretrieveById
とretrieveByCredentials
メソッドから、返さなくてはならないことを覚えておいてください。
interface Authenticatable {
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}
このインターフェイスはシンプルです。getAuthIdentifier
メソッドは、そのユーザーの「主キー」を返さなくてはなりません。MySQLがバックエンドで使われているなら、ここでも自動増加主キーとなるでしょう。getAuthPassword
は、ユーザーのハッシュ済みのパスワードを返す必要があります。このインターフェイスにより、どんなORMやストレージの抽象クラスを使用してるのかに係わらず、認証システムをユーザークラスに対し動作させることを可能にしています。デフォルトでLaravelはこのインターフェイスを実装している、app
ディレクトリーの、User
クラスを含んでいます。ですから、実装のサンプルとして、このクラスを調べてみてください。
さて、UserProvider
を実装し、Auth
ファサードへ拡張を登録する準備が整ったとしましょう。
Auth::extend('riak', function($app)
{
return new RiakUserProvider($app['riak.connection']);
});
ドライバーをextend
メソッドで登録したら、config/auth.php
設定ファイルでその新しいドライバーに切り替えましょう。
サービスコンテナによる拡張
Laravelフレームワークに含まれている、ほとんどすべてのサービスプロバイダーは、サービスコンテナでオブジェクトを結合しています。config/app.php
設定ファイルで、アプリケーションのサービスプロバイダーのリストを見つけられます。時間があれば、プロバイダーそれぞれのソースコードを拾い読みしてください。そうすれば、それぞれのプロバイダーがフレームワークに何を付け加えているのか、さらにサービスコンテナに数々のサービスを結合するため、どんなキーが使用されているか、もっと知ることができます。
例えば、HashServiceProvider
は、サービスコンテナでhash
キーと結合されており、Illuminate\Hashing\BcryptHasher
インスタンスが依存解決されます。あなたは自分のアプリケーションの中でこの結合をオーバーライドすることにより、簡単にクラスを拡張したり、オーバーライドしたりできます。例えば:
<?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;
});
}
}
このクラスはベースクラスのデフォルトServiceProvider
ではなく、
HashServiceProvider
を拡張していることに注意してください。サービスプロバイダーを拡張したら、config/app.php
設定ファイルの中の、HashServiceProvider
をあなたが拡張したプロバーダー名へ交換してください。
ここまでコンテナで結合されたコアクラスを拡張する、一般的な方法を見てきました。基本的に、コンテナ上の全てのコアクラスは、こうした方法で結合されており、オーバーライド可能です。繰りかえしますが、フレームワークに含まれているサービスプロバイダーを全般的に読んでみてください。様々なクラスに親しめますし、どのキーで結合されているか分かります。これは、Laravelがどのように一つにまとめられているのかを詳しく理解できる、素晴らしい方法です。