[Laravel 5.5] 読み込む.envファイルを環境ごとに切り替える

# 2通りの方法

方法１　サーバー環境変数APP_ENVを設定しておく。
方法２　bootstrap/app.phpで、loadEnvironmentFromメソッドで読み込む.envファイルを指定する。

どちらにしても、各環境用の.envを用意しておきましょう。

- .env (ローカル用)
- .env.dev (開発環境用)
- .env.stg (ステージング環境用)
- .env.prod (本番環境用)

また以下の例は全てapacheサーバですが、nginxでも同じことができます。

## [方法1] サーバー環境変数で切り替える

各環境の.htaccessで環境変数を設定しておきます。本番環境なら

```
SetEnv APP_ENV prod
```

SetEnvIfを使用すれば各環境ごとに.htaccessを変えなくてすみます。

```
SetEnvIf Host "production.co.jp" APP_ENV=prod
SetEnvIf Host "staging.co.jp" APP_ENV=stg
SetEnvIf Host "development.co.jp" APP_ENV=dev
```

APP_ENVの値によって、読み込む.envファイルが変わります。

## [方法2] loadEnvironmentFromメソッドで切り替える

bootstrap/app.phpで読み込むファイルを指定しておくと、サーバー環境変数がなくてもOKです。

```bootstrap/app.php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

/*
|--------------------------------------------------------------------------
| 環境によって読み込む.envファイル切り替え
|--------------------------------------------------------------------------
*/

switch ($_SERVER['SERVER_NAME'] ?? 'localhost') {
    case 'development.co.jp':
        $app->loadEnvironmentFrom('.env.dev');
        break;
    case 'staging.co.jp':
        $app->loadEnvironmentFrom('.env.stg');
        break;
    case 'production.co.jp':
        $app->loadEnvironmentFrom('.env.prod');
        break;
}
```

## artisanコマンドを使うとき

artisanコマンドを使用するときは、読み込む.envファイルを--envオプションで指定しなければなりません。
.env.stgを読み込ませるには以下のようにします。

```sh
$ php artisan migrate --seed --env=stg
```

指定しないと、デフォルトである.envが読み込まれます。

# [補足] どのように切り替えられているのか

コアのLoadEnvironmentVariables.phpで.envファイルの読み込みが行われていますが、サーバー環境変数の有無で読み込むファイル名を変更しています。
loadEnvironmentFromメソッドは、自由に読み込むファイル名を指定できるメソッドです。bootstrap/app.phpで指定しておけば、読み込むファイル名が変更された状態でこのクラスの処理が走ります。

```/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/LoadEnvironmentVariables.php
<?php

namespace Illuminate\Foundation\Bootstrap;

use Dotenv\Dotenv;
use Dotenv\Exception\InvalidPathException;
use Symfony\Component\Console\Input\ArgvInput;
use Illuminate\Contracts\Foundation\Application;

class LoadEnvironmentVariables
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        if ($app->configurationIsCached()) { // キャッシュされているとそもそも.envを読みに行かない
            return;
        }

        // コンソールから実行されているのか、サーバー環境変数がセットされているかチェックする
        $this->checkForSpecificEnvironmentFile($app);

        try {
            // .env か .env.{APP_ENV} か loadEnvironmentFrom()で指定したファイルを読み込む
            (new Dotenv($app->environmentPath(), $app->environmentFile()))->load(); 
        } catch (InvalidPathException $e) {
            //
        }
    }

    /**
     * Detect if a custom environment file matching the APP_ENV exists.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    protected function checkForSpecificEnvironmentFile($app)
    {
        // artisanコマンドの時はここで判別される
        if ($app->runningInConsole() && ($input = new ArgvInput)->hasParameterOption('--env')) {
            if ($this->setEnvironmentFilePath(
                $app, $app->environmentFile().'.'.$input->getParameterOption('--env')
            )) {
                return;
            }
        }

        if (! env('APP_ENV')) { // サーバー環境変数が設定されてなければreturn
            return;
        }

        $this->setEnvironmentFilePath( // 読み込み先を.env.{APP_ENV}とする
            $app, $app->environmentFile().'.'.env('APP_ENV')
        );
    }

    /**
     * Load a custom environment file.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  string  $file
     * @return bool
     */
    protected function setEnvironmentFilePath($app, $file)
    {
        if (file_exists($app->environmentPath().'/'.$file)) {
            $app->loadEnvironmentFrom($file);

            return true;
        }

        return false;
    }
}
```