【Laravel】開発環境では「php artisan config:cache」をするべきではない

# Q. なぜ？
A. [ドキュメント](https://laravel.com/docs/8.x/configuration#configuration-caching)にそう書いてある。

>  The command should not be run during local development as configuration options will frequently need to be changed during the course of your application's development.

（意訳）アプリケーションの開発中には設定を頻繁に変更する必要があるので、ローカル開発環境ではこのコマンドは実行するべきではありません。

#  Q. そう書いてある、では納得できない！
A. 一度`php artisan config:cache`を実行すると、それ以降（キャッシュを消去しない限り）configディレクトリ配下の設定ファイルを修正するたびに`php artisan config:cache`を実行しなければ設定が反映されなくなるため。

# 解説
とある機能を実装しようとして、サンプルコードを実装してみたところ設定が反映されずなぜか動かない。。`php artisan config:cache`もしくは`php artisan config:clear`をしたら動きました！という経験、一度はありませんか？ 
これは前述した通り、一度設定ファイルをキャッシュすると**キャッシュされた設定ファイルを優先的に読みにいく**のでconfigディレクトリ配下の設定ファイルが読み込まれないために発生します。
ドキュメントにも記載がある通り開発中は設定ファイルが頻繁に編集されるため、その度にコマンドを打って反映させるのは煩雑です。そのため開発環境では設定ファイルをキャッシュするべきではないのです。

結論から言ってしまえば以上！なのですが、この記事ではほんの少しLaravel内部のコードを追うことで設定ファイルがどう読み込まれているかを見ていきます。

## 設定ファイルの読み込み
設定ファイルの読み込みは/Illuminate/Foundation/Bootstrap/LoadConfiguration.phpのbootstrapメソッド内で行われています。

```PHP:Illuminate/Foundation/Bootstrap/LoadConfiguration.php
~~省略~~
class LoadConfiguration
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        $items = [];

        // First we will see if we have a cache configuration file. If we do, we'll load
        // the configuration items from that file so that it is very quick. Otherwise
        // we will need to spin through every configuration file and load them all.
        if (file_exists($cached = $app->getCachedConfigPath())) {
            $items = require $cached;

            $loadedFromCache = true;
        }

        // Next we will spin through all of the configuration files in the configuration
        // directory and load each one into the repository. This will make all of the
        // options available to the developer for use in various parts of this app.
        $app->instance('config', $config = new Repository($items));

        if (!isset($loadedFromCache)) {
            $this->loadConfigurationFiles($app, $config);
        }
~~省略~~
```

まず`if (file_exists($cached = $app->getCachedConfigPath()))`でキャッシュされたファイルがあるかどうか確認します。
`$app->getCachedConfigPath()`は特に設定を変更していない場合`{your-project-path}/bootstrap/cache/config.php`という文字列が返されます。

実際に`php artisan config:cache`を打つとbootstrap/cache配下にconfig.phpが生成されているのがわかるかと思いますが、これこそが上述した**キャッシュされた設定ファイル**です。このconfig.phpは全設定をArray形式で返します。このファイルをrequireして\$items変数に代入し、$item変数を元にRepositoryクラス（/Illuminate/Config/Repository.php）をnewします。これでキャッシュがある場合の読み込みが完了です。

設定値にアクセスするためのヘルパ関数であるconfig()は、このRepositoryインスタンスのgetメソッドを呼び出すことで設定値を返してくれるのです。

```PHP:helpers.php
    function config($key = null, $default = null)
    {
        if (is_null($key)) {
            return app('config');
        }

        if (is_array($key)) {
            return app('config')->set($key);
        }

        return app('config')->get($key, $default);
    }
```

`if (file_exists($cached = $app->getCachedConfigPath()))`がfalse、つまり設定ファイルが見つからない場合は\$itemsは空のままRepositoryクラスがnewされたあとに、`$this->loadConfigurationFiles()`によって設定値が読み込まれます。

```PHP:Illuminate/Foundation/Bootstrap/LoadConfiguration.php
    /**
     * Load the configuration items from all of the files.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Contracts\Config\Repository  $repository
     * @return void
     *
     * @throws \Exception
     */
    protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {
        $files = $this->getConfigurationFiles($app);

        if (!isset($files['app'])) {
            throw new Exception('Unable to load the "app" configuration file.');
        }

        foreach ($files as $key => $path) {
            $repository->set($key, require $path);
        }
    }
```

このメソッドの内部ではconfigディレクトリ配下のファイルからkeyとvalueを取り出し、repositoryインスタンス（先ほどnewしたRepositoryクラスのインスタンス）のsetメソッドによって設定値を一つづつ読み込んでいます。

要するに、キャッシュのあるなしでは読み込むファイルが異なるのです。
- **キャッシュがある**（bootsrap/cache/config.phpが存在する）場合
    - bootstrap/cache/config.phpが返す配列を読み込む
- **キャッシュがない**（bootsrap/cache/config.phpが存在しない）場合
    - configディレクトリ配下の各phpファイルから配列を読み込む

ここで、一度`php artisan config:cache`をして設定ファイルをキャッシュした後に、configディレクトリ配下のphpファイル（例えばauth.phpやmail.phpなど）の値を変更した場合はどうなるでしょうか？

Laravelは`php artisan config:cache`によって生成されたbootstrap/cache/config.phpを読み込みますが、変更前の値しか保持していないので当然変更は反映されません。この場合、再度`php artisan config:cache`を打ってbootstrap/cache/config.phpを再生成するか、`php artisan config:clear`を打ってbootstrap/cache/config.phpを削除し、configディレクトリ配下の各phpファイルを読みにいくようにすることで設定が反映されるようになります。

以上、Laravelで設定がどのように読み込まれるか、`php artisan config:cache`や`php artisan config:clear`をすることで設定ファイルが反映される流れを解説しました。

ちなみに[ドキュメント](https://laravel.com/docs/8.x/configuration#configuration-caching)によると**プロダクション環境**ではパフォーマンス向上のために`php artisan config:cache`をするべきだとしています。
>To give your application a speed boost, you should cache all of your configuration files into a single file using the config:cache Artisan command. This will combine all of the configuration options for your application into a single file which will be loaded quickly by the framework.

> You should typically run the php artisan config:cache command as part of your production deployment routine. 

# まとめ
- 開発環境で作業している場合
    - php artisan config:cacheは実行しない
    - すでにphp artisan config:cacheを実行済みでbootstrap/cache/config.phpが生成されている場合はphp artisan config:clearで削除する
- プロダクション環境にデプロイした場合
    - php artisan config:cacheで設定ファイルをキャッシュする

それではHappyなLaravelライフを🎉