Laravelでスタブる

投稿日:

あっという間に3月も10日過ぎました。

さて、前回に引き続きLaravelの話。
HelloWorld出来たし、何か書こうかなぁと。

んで、今回は開発環境ではスタブを使って値を返したい時について。
例えば、お客さんの環境でしか動かないAPIを使わなきゃいけない時、そんな時どうするか。
自分なりに、調べたり考えてみたので残しときます。

環境設定

まずはこれ。開発環境、本番環境、etc…と環境を区別出来る様にします。

bootstrap/start.phpを見てみます。

$env = $app->detectEnvironment(array(

    'local' => array('your-machine-name'),

));

この

'local' => array('your-machine-name'),

の部分はarrayで渡された文字列に、hostnameの値と一致するものがあれば、環境を’local’とする的な感じです。何も引っかからなければデフォルトの’production’となるみたい。

なので、

$env = $app->detectEnvironment(array(

    'local' => array('your-machine-name', 'your-machine-name2'), // ローカル環境
    'test' => array('test-machine'), // テスト環境
    'staging' => array('stage-machine'), // ステージング環境

));

こんな感じで書けます。
※’testing’はPHPUnitでのテスト実行時に使われるのでNGみたい

で、配列じゃなくてクロージャーも使えるのでもっと柔軟に使うことも出来ます。
これの何が便利かって言うと、VirtualHostでコンテンツを分けている場合とか(hostnameで切り分けられないケース)かなぁって思います。

$env = $app->detectEnvironment(function()
{
    //例えばこうやって、ファイルパスで切り替えたりとか色々可能
    if (strpos(__DIR__, '/home/www/docroot_dev') !== false) {
        // ここに来たら'local'となる
        return 'local';
    }
});

で、確認してみる。

app/routes.php

Route::get('/', function()
{
    // localと表示されていればlocalとなる
    echo App::environment();
});

うん。これでララベルちゃんは環境が分かるようになりました。

設定ファイル

環境設定が済んだら何が出来る様になるかというと、設定ファイルを環境毎に用意する事が出来ます。
上記でlocalの設定が済んだら、app/config/local/ディレクトリを作ってみましょう。
このディレクトリに、app/config/以下にあるファイルと同名のファイルを置くと、ララベルが勝手に環境設定から該当するファイルを読み込んでくれます。
local環境だったら、app/config/local/〜から
test環境だったらapp/config/test/〜から
みたいな具合です。
でも、app/config/以下のファイルも見てくれているから大丈夫。以下で見てみます。

それでは、app.phpを作ってみます。

app/config/local/app.php

<?php
return array(
	'debug' => true,
);

として、

app/config/app.php

<?php
return array(

	/*
	|--------------------------------------------------------------------------
	| Application Debug Mode
	|--------------------------------------------------------------------------
	|
	| When your application is in debug mode, detailed error messages with
	| stack traces will be shown on every error that occurs within your
	| application. If disabled, a simple generic error page is shown.
	|
	*/

	'debug' => false,
        ・
        ・
        ・

とすると、ローカル環境だとdebug機能が有効になって、本番環境だとdebug機能が無効となります。
ポイントは、上書きたい設定だけを記述するって所です。
debug以外の設定は、app/config/app.phpの設定を見てくれます。
※だからハッシュで設定しているんでしょうね〜
database.phpとかmail.phpとかは環境で設定分ける系だと思います。

app/start/〜

環境で分けられるのはapp/config以下だけじゃあないんです奥さん。
app/start/以下を見てみますと、多分デフォルトでlocal.phpがあると思います。
勘が鋭い人はその通り!
これも環境によってララベルがファイルを読み込んでくれます。

今回はこのファイルにautoloaderに重要な事を書いていきます。

オートローダー

一旦オートローダーの話をします。
一番自分がハマったのが実はこれ。新しく書いたモデルとかコントローラーを認識してくれなくて、結構悩んだ経緯があります。でも、分かっちゃうと簡単。

composer.jsonを見てみます。

"autoload": {
	"classmap": [
		"app/commands",
		"app/controllers",
		"app/models",
		"app/database/migrations",
		"app/database/seeds",
		"app/tests/TestCase.php"
	]
},

このautoload.classmapにパスを書くと、その下にあるファイルをオートロードしてくれます。
/vendor/composer/autoload_classmap.phpを見てみると分かります。
こんな感じでマッピングしてくれてます。

<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'BaseController' => $baseDir . '/app/controllers/BaseController.php',
    ・
    ・
    ・

はい簡単。でも、自分はここで躓きました。app/models以下にファイルを置いて呼んでもエラーが出てしまう。。で、どうしたか。

オートロード対象ディレクトリにファイルを作ったら、composer.jsonと同じディレクトリで、これや。

composer dump-autoload

こうすると、/vendor/composer/autoload_classmap.phpに書き込んでくれて、マッピングしてくれるので、もう大丈夫。
※はじめは面倒臭いなぁと思ったけど、慣れた。けど、他に方法あるんかなぁ。。

例えば、HogeControllerをapp/controllers/HogeController.phpに作ったら、composer dump-autoloadすると、/vendor/composer/autoload_classmap.php'HogeController' => $baseDir . '/app/controllers/HogeController.php', と書かれているはず。

スタブる

そろそろスタブの話に戻りたいと思います。
ララベルは IoCコンテナ※1 が簡単に利用できますので、IoCコンテナを使ってやれよって言われそうな気がするんですが、いちいち何というか環境を見て書くのが面倒臭いなぁって自分は思いました※2。
※1 依存性の注入ってやつ。こちらが詳しいです。Laravel IoC コンテナの使い方
※2 もちろん、IoCコンテナは凄い便利なので活用すべきです

で、自分がどうしたかっていうと最初に戻って、app/start/global.phpに書かれている、

ClassLoader::addDirectories(array(

	app_path().'/commands',
	app_path().'/controllers',
	app_path().'/models',
	app_path().'/database/seeds',

));

これを利用する事にしました。
これは何かって言うと、これもautoloaderの為の設定で、この下にあるファイルも最初に読み込みますよ〜って事だと思います(多分)。
※インクルードパスの設定をやってるのかな、多分。

それでですね、app/start/local.phpがありますので、このファイルにこう書けるわけです。

あ、自分が何をしたいかって言うとlibディレクトリを置いて、その中に、アプリ特有のクラスファイルとかを置きたいと思っているわけです。ビジネスロジック郡とかAPIとかですね。
それで、スタブを返したいクラスはlibdevディレクトリ以下に置いて、ローカル環境からはそのスタブを返すようにしたいなって思います。

app/start/local.php

<?php

/*
|--------------------------------------------------------------------------
| Register The Laravel Class Loader
|--------------------------------------------------------------------------
*/
ClassLoader::addDirectories(array(
	app_path().'/libdev/',
	app_path().'/lib/',
));

には、こう書きます。
で、app/start/global.phpには

ClassLoader::addDirectories(array(

	app_path().'/commands',
	app_path().'/controllers',
	app_path().'/models',
	app_path().'/database/seeds',
	app_path().'/lib/',
));

こう書きます。

こうすると、ローカル環境だとlibdevからファイルを見にいって、ファイルがあればローカル環境のクラスを読み込み、無ければlibからクラスを読み込んでくれます。

やってみる

上記の設定(app/start/global.php | local.php)をしてから、
app/lib/Hoge.php

class Hoge
{
    public function echo_()
    {
        echo 'libだよ';
    }
}

app/libdev/Hoge.php

class Hoge
{
    public function echo_()
    {
        echo 'libdevだよ';
    }
}

とクラスを作っておきます。

app/routes.php

まずは、ローカルのテスト

Route::get('/', function()
{
    if (strpos(__DIR__, '/home/www/docroot_dev/')!==false) {
        $Hoge = new Hoge();
        return $Hoge->echo_(); // 結果は'libdevだよ'
    }
});

ローカル以外のテスト

Route::get('/', function()
{
    if (strpos(__DIR__, '/home/www/docroot_dev/')===false) { // あくまでもテスト
        $Hoge = new Hoge();
        return $Hoge->echo_(); // 結果は'libだよ'
    }
});

※bootstrap/start.php を弄ってやるべきだけど時間無かったんで

うん。大丈夫かな。
これでスタブを返したい時はlib以下の同名クラスをlibdev以下に置けばOKかと。
APIとか作る時に重宝すると思います。

ちなみに、lib以下にディレクトリを掘る場合はPSR-0準拠でファイルを作ればOKです。

例) app/lib/Hoge/Piyo.php だったら、

class Hoge_Piyo 
{
}

まとめ

というわけで、ララベルでスタブって見ました。
が、他にもやり方はあると思いますので色々試して頂ければと思います。

参考にさせて頂きました

投稿日:
カテゴリー: PHP タグ:

作成者: shimabox

Web系のプログラマをやっています。 なるべく楽しく生きていきたい。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください