laravel

好みのライブラリに入れ替える(DB編

More than 3 years have passed since last update.

Laravelはデフォルトで様々コンポーネントが用意されていますが、
それらのほとんどはユーザーの好みによって好きなものに入れ替える事が出来ます。

中には入れ替えるのに若干難易度が高いものもありますが(routerとか)、
基本的には自由に組み合わせる事が出来ます。
入れ替えについてはあまり記述を見かけないと思いますので、
ここではDBファサードを通じて利用されている
illuminate/database(Eloquent, クエリービルダー, basic)
doctrine/dbal に変更してみます

Laravelの内部までソースを追っている方はだいたい分かると思いますが、
変更してもデフォルトで用意されてるものを継承して、
使うライブラリに併せて少しだけ変更する程度ですのでビビらずに好きなものに変えてしまいましょう

まずは doctrine/dbal を入れます

composer.json
"require": {
    "php": ">=5.4.0",
    "laravel/framework": "4.2.*",
    "doctrine/dbal": "2.*"
}

次に doctrine/dbal を利用するクラスを用意しましょう
サービスプロバイダーの仕組みを理解しましょう!
アプリケーション起動時にこれらのサービスプロバイダーが読み込まれてから、
それぞれのコードが実行されます
フレームワークの実装コードを見れば分かりますが、db関連はregisterで登録されます

Illuminate\Database\DatabaseServiceProvider
    public function register()
    {
        $this->app->bindShared('db.factory', function($app) {
                return new ConnectionFactory($app);
            }
        );

        $this->app->bindShared('db', function($app) {
                return new DatabaseManager($app, $app['db.factory']);
            }
        );
    }

bindShareはLaravelのコンテナライブラリである illuminate/container で実装されている機能です
bindと同じ様にコンテナに登録する名前を指定し、
実行時に作られたインスタンスはコンテナがそのまま保持して、同じインスタンスを返します
(static $object;)

では同じ様に実装してみます

namespace App\Providers;

use App\Database\Manager as DB;
use Illuminate\Support\ServiceProvider;

class ApplicationServiceProvider extends ServiceProvider
{
    /**
     * doctrine/dbal register
     * @return void
     */
    public function register()
    {
        $this->app->bindShared('dbal', function($app) {
                return new DB($app['config']);
            }
        );
    }
}

こんな感じになります。
今回はとりあえずコンテナに登録されている'db'はそのままに、
新しく'dbal'として登録します
return で返却されるものは doctrine/dbal のインスタンスを返すクラスです

$this->app;

\Illuminate\Foundation\Application インスタンスで、
コンテナを継承しています。
それぞれのコンポーネントはコンテナにそれぞれ名前が付けられて登録されています
コンテナに登録されてる名前リスト
コンフィグ関連はconfigの Illuminate\Config\Repository を利用します
コンテナはArrayAccessが利用されていますので、
配列でもオブジェクトでもどちらでもいいです。お好みでどうぞ

doctrine/dbal のインスタンスを返すクラスは下記の様に実装してみます

namespace App\Database;

use Doctrine\DBAL\DriverManager;
use Illuminate\Config\Repository;

class Manager
{

    /** @var Repository  */
    protected $config;

    /**
     * @param Repository $config
     */
    public function __construct(Repository $config)
    {
        $this->config = $config;
    }

    /**
     * @param null $name
     * @return \Doctrine\DBAL\Connection
     * @throws \Doctrine\DBAL\DBALException
     */
    public function connection($name = null)
    {
        $configure = $this->config->get('database');
        $connection = DriverManager::getConnection($configure['connections'][$name]);
        $connection->setFetchMode($configure['fetch']);
        return $connection;
    }
} 

Illuminate\Config\Repository をタイプヒントで指定します。
Laravel4系の場合はこの様になりますが、次期バーションの5はより拡張性が強化されていますので、
コンストラクタにタイプヒントする場合は、contractsをタイプヒントで指定します
5から利用できます(現時点ではまだdevelopのみ)

public function __construct(\Illuminate\Contracts\Config\Repository $config)
{
    $this->config = $config;
}

独自のものや好みのものに簡単に自由自在に変更できる様になります
connectionは接続先をかえるためのメソッドです
これでコンテナから'dbal'でいつでも利用できる準備が整いました
これがLaravelのFacade(デザインパターンのProxy)となります
そこにアクセスするためのコードを追加します

namespace App\Database;

use Illuminate\Support\Facades\Facade;

class DatabaseFacade extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'dbal';
    }
}

これでアクセスもバッチリ!
あとはDBファサードの指定をこれに変更するだけです。

app.php
    'aliases' => [
        //'DB' => 'Illuminate\Support\Facades\DB',
        'DB' => 'App\Database\DatabaseFacade',
        // 続く
    ],

今回はDBのみを変更したので、migrateなどもそのまま利用できる状態で、
ユーザーが実装するコードのみが影響されます
これでユーザーが実装する場合に doctrine/dbal が利用できる様になります

$result = \DB::connection('dbal_mysql')->fetchAll("SELECT NOW() as now");

この様な感じでそれぞれの好きなものに入れ替える事が出来ます
使い辛いなと感じるものがあれば入れ替えて自分好みにカスタマイズしてしまいましょう!
登録されている名前との関連を学習すると、
よりテストし易いコードになりますのでオススメです