マネージャーとファクトリー

ドライバーベースのコンポーネントの作成を管理する、複数のManagerクラスをLaravelは持っています。それらは、キャッシュ、セッション、認証、キューコンポーネントです。Managerクラスはアプリケーションの設定に基づき、特定のドライバーの実装を作成することに責任を持っています。例えば、CacheManagerクラスはAPC、Memcached、ファイル、その他のキャッシュの実装を生成することができます。

各マネージャーは、新しいドライバーのリゾルバーを簡単にマネージャへ登録できるように、extendメソッドを備えています。以降では、各カスタムドライバーのサポートを注入する例と一緒に、こうしたマネージャーを説明していきます。

Note: どうぞ時間を取り、CacheManagerSessionManagerのように、初めからLaravelに用意してある様々なManagerクラスを調べてみてください。こうしたクラスを徹底的に読めば、Laravelの内部動作を完全に理解することができます。全Managerクラスは、Illuminate\Support\Managerベースクラスを拡張しており、各Managerに共通で便利な機能を提供しています。

キャッシュ

Laravelのキャッシュ機能を拡張するには、CacheManagerextendメソッドを使用します。このメソッドはManagerへカスタムドライバーのリゾルバーを結合するために使用され、全Managerクラスで使用できます。例えば、"mongo"という名前のキャッシュドライバーを登録するには、次のようになります。

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

extendメソッドの最初の引数は、ドライバーの名前です。この名前は、config/cache.php設定ファイルの中のdriverオプションと対応しています。2つ目の引数はクロージャーで、Illuminate\Cache\Repositoryインスタンスをリターンしなければなりません。クロージャーは、Illuminate\Foundation\Applicationのインスタンスであり、IoCコンテナでもある、$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の実装を返す
});

セッションをどこで拡張するか

セッションの拡張は、AppServiceProviderbootメソッドの中へコードしましょう。

セッションの拡張をコーディングする

カスタムセッションドライバーは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ファイルで使用できます。

Note: カスタムセッションハンドラーを書いたら、Packagistで他の開発者と共有できることを思い出してください。

認証

キャッシュやセッション機能と同じ方法で、認証も拡張できます。既にお馴染みの、extendメソッドを今回も使用します。

Auth::extend('riak', function($app)
{
    // Return implementation of 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メソッドは、$userremember_tokenフィールドを新しい$tokenで更新します。新しいトークンは"Remember me"によるログインが成功した時に生成される真新しいトークンでも良いですし、もしくはユーザーがログアウトした時はnullでもかまいません。

retrieveByCredentialsメソッドは、アプリケーションにログインするために、Auth::attemptメソッドに渡された、ログイン情報の配列を受け取ります。メソッドはユーザー情報に一致するユーザーが、裏にある持続的なストレージに存在しているか「クエリー」する必要があります。典型的にメソッドは、"where"条件で$credentials['username']のクエリーを実行することでしょう。このメソッドでパスワードのバリデーションや認証を試みてはいけません。

validateCredentialsメソッドは指定された$userとユーザーを認証するための$credentialsを比較する必要があります。例えば、このメソッドでは $user->getAuthPassword()文字列と、Hash::makeした$credentials['password']を比較する必要があるでしょう。

これでUserProviderのメソッドをそれぞれ学習し終えました。今度は、Authenticatableに注目していきましょう。プロバイダーはこのインターフェイスの実装をretrieveByIdretrieveByCredentialsメソッドから、返さなくてはならないことを覚えておいてください。

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設定ファイルでその新しいドライバーに切り替えましょう。

IoCベースの拡張

Laravelフレームワークに含まれている、ほとんどすべてのサービスプロバイダーは、IoCコンテナでオブジェクトを結合しています。config/app.php設定ファイルで、アプリケーションのサービスプロバイダーのリストを見つけられます。時間があれば、プロバイダーそれぞれのソースコードを拾い読みしてください。そうすれば、それぞれのプロバイダーがフレームワークに何を付け加えているのか、さらにIoCコンテナに数々のサービスを結合するため、どんなキーが使用されているか、もっと知ることができます。

例えば、HashServiceProviderは、IoCコンテナでhashキーと結合されており、Illuminate\Hashing\BcryptHasherインスタンスが依存解決されます。あなたは自分のアプリケーションの中でIoC結合をオーバーライドすることにより、簡単にクラスを拡張したり、オーバーライドしたりできます。例えば:

<?php namespace App\Providers;

class SnappyHashProvider extends \Illuminate\Hashing\HashServiceProvider {

    public function boot()
    {
        $this->app->bindShared('hash', function()
        {
            return new \Snappy\Hashing\ScryptHasher;
        });

        parent::boot();
    }

}

このクラスはベースクラスのデフォルトServiceProviderではなく、 HashServiceProvider を拡張していることに注意してください。サービスプロバイダーを拡張したら、config/app.php設定ファイルの中の、HashServiceProviderをあなたが拡張したプロバーダー名へ交換してください。

ここまでコンテナで結合されたコアクラスを拡張する、一般的な方法を見てきました。基本的に、コンテナ上の全てのコアクラスは、こうした方法で結合されており、オーバーライド可能です。繰りかえしますが、フレームワークに含まれているサービスプロバイダーを全般的に読んでみてください。様々なクラスに親しめますし、どのキーで結合されているか分かります。これは、Laravelがどのように一つにまとめられているのかを詳しく理解できる、素晴らしい方法です。