Laravelの標準ディレクトリの変更方法 まとめ

# なにがしたい？

Laravelは<font color="red">「ディレクトリ構成が自由」</font>と言われますが、それはネームスペースで管理されるクラスとそのPHPファイルのお話なんだと思います。

そうではなくて、Laravelフレームワークのシステムファイルや重要なデータが入っていそうな「標準のディレクトリ」≒「小文字のディレクトリ」は変更できるのでしょうか？

変更できるでしょ？
やっといてね！
というご要望を、最近ちらほら頂いたので、ざっくりまとめてみました。

# 仕様
- 変更できるかできないのか？ にパッと答えられるようにするもの
- 詳しい変更方法は別の記事か、Google先生におまかせします
- 調査方法は主にLaravel5.5/5.6のソースコードをチェック
- ざっくりしていますので、ヌケモレ間違いがあるかもしれません。気づかれましたら、ご指摘いただけるとありがたいです。

**参考**
[ディレクトリ構造（Laravelドキュメント）](https://readouble.com/laravel/5.6/ja/structure.html)
[Laravelのディレクトリ構造について学ぶ](https://qiita.com/shosho/items/93cbff79376c41c3a30b)

# まとめ

DIR            | 難度 | 概要 | 詳細
--- |:---:| --- | ---
/            | ✕ | 変更不可 
app			 | △ | Applicationを派生 | path()をオーバーライド<br>composer.jsonでAppの参照先を変更
&emsp;Providers  | ◎ | ネームスペース | PHPのネームスペースをディレクトリに合わせればOK<br>config/app.phpのクラス指定もそれに合わせる
&emsp;Http       | ◎ | ネームスペース | PHPのネームスペースをディレクトリに合わせればOK<br>config/app.phpのクラス指定もそれに合わせる
bootstrap	 | △ | Applicationを派生 | bootstrapPath()をオーバーライド<br>bootstrap/index.phpを修正
config		 | △ | Applicationを派生 | configPath()をオーバーライド 
database	 | 〇 | bootstrap/app.php |  Application::useDatabasePath()
public		 | △ | Applicationを派生 | publicPath()をオーバーライドするか<br>path.pulbicを置き換える
&emsp;storage	 | ◎ | シンボリックリンク | storage/app/publicへのシンボリックリンク<br>php artisan storage:link 
resources	 | ✕ | 変更困難 |  webpack,Vue などでハードコーディングされている。<br>Applicationを派生 resourcePath()をオーバーライド<br>Mail, Pagination などで使用
&emsp;assets		 | ✕ | 変更困難 |  webpack,Vue などでハードコーディングされている。
&emsp;lang		 | △ | Applicationを派生 | langPath()をオーバーライドするか path.langを置き換える
&emsp;views		 | ◎ | config | config/views.php を変更
routes		 | ◎ | RouteServiceProvider | 
storage		 | 〇 | bootstrap/app.php |  Application::useStoragePath()
&emsp;app/public  | ◎ | config | config/filesystems.php を変更
&emsp;framework   | ✕ | 変更不可 | ハードコーディングがちらほら
&emsp;&emsp;cache      | ◎ | config | /cache.php を変更
&emsp;&emsp;sessions   | ◎ | config | /session.php を変更
&emsp;&emsp;testing    | ✕ | 変更不可 | Storage.php でハードコーディングされている。
&emsp;&emsp;views      | ◎ | config | /views.php を変更
&emsp;logs        | △ | LogHandlerを置き換え
tests		 | ◎ | composer.json
vendor		 | ✕ | 変更不可 | PackageManifestなどでハードコーディング
.env		 | △ | bootstrap/app.php | Application::useEnvironmentPath()







# ✕ 変更不可・困難

Laravelのソースコードの中でハードコーディングされていたり、影響範囲が大きく、実質的に変更できないディレクトリ。

## / <br> vendor

<font color="red">「Laravelのルートディレクトリは、vendorが設置してあるディレクトリのことである」</font>と思っていても間違いなさそうなくらい、動かさないほうが良いディレクトリ。<br>vendor ディレクトリは composer.json を修正すれば変更できるのですが、この vendor という名前が PackageManifestなどでハードコーディングされていて、むやみに変更したら100％動作する保証がありません。

**参考**
[composer.jsonのvendorディレクトを変更するには（英語）](https://stackoverflow.com/questions/31518052/how-can-i-change-to-location-where-composer-creates-the-vendor-folder)
[vendorを変更したら"php artisan optimize"でエラーが発生（英語）](https://github.com/laravel/framework/issues/8637)

## resources

Application に resourcePath() というメソッドがあって変更できそうに見えますが、フロントエンド用の webpack,Vue などでハードコーディングされていたり、 resourcePath() も Mail, Pagination などの主力部隊が使っているので、事実上変更不可と思っていたほうが良さそうです。

## resources/assets

その配下の assets も、webpack,Vue などでハードコーディングされていていて、事実上変更不可。フロント用の素材はここに置くと決めてかかりましょう。

## storage/framework <br> storage/framework/testing

storageは変更できなくはないけど、その配下の framework と framework/testing が、Storage.php などでハードコーディングされていて変更が困難。


# △ いばらの道

できなくはないが、標準的でない、上級テクニックを要する。<br>
あなたは、なぜ、それでも前に進もうとするのか……。

## app 

この app の配下はネームスペースになっていて自由極まりないのですが、そのapp自体はどうなのか？ 一見、composer.json の修正だけで行けそうな気がしますが、実際にはこの app ディレクトリは Application::path() の中でハードコーディングされていて、この<font color="red">path()メソッドはいろんなところから呼ばれてます</font>。

この振る舞いを変更するには、Application::path() のオーバーライドが必須です。

他にも index.php から参照されていたり、ハードコーディングされていたり、単純な移動は困難です。それだったら、標準のappは触らずに置いといて、app_crawlers といった別ディレクトリを作って、composer.json で別のネームスペースを割り当てて好き勝手するほうが良いかと思います。

```php:composer.json
    ...
    "autoload": {
        "classmap": [
            "database/seeds",
            "database/factories"
        ],
        "psr-4": {
            "App\\": "app/",
            "Crawlers\\": "app_crawlers/" ← 別ディレクトリを追加
        },
        ...
```

## bootstrap 

Application::bootstrapPath() のオーバーライドが必須です。public/index.php の修正も必要。複数のアプリケーションやカーネルを使い分ける、なんてことをする場合でも、bootstrapは触らず、bootstrap/app2.php と複数の起動スクリプトを作ったりしたほうが良さそうです。


## storage/logs

要件としてはよくありがちなのに、意外と困難で、<font color="red">LogHandlerごと置き換える</font>必要があります（LogServiceProviderにハードコーディングされています）。

storageディレクトリは比較的簡単に移動できるのでstorageごと動かすか、単にコードベースのディレクトリが使えないなら標準エラーログを使うか、ハンドラを置き換えるならデータベースを使うなど根本から変えたほうが良いかと思います。

```php:config/logging.php
'channels' => [
    'single' => [
        'driver' => 'errorlog',
        // ...
    ],
```

**参考**
[Laravel5.6でログをデータベースに記録する](https://qiita.com/kd9951/items/35b0a0c4e16353078b70)


# 〇 なんとかなる

標準的ではないが、変更の手間や影響範囲が少ないところ。とはいえ、ほんとうにするんですか？

## config
Applicationクラスを派生して、configPath()をオーバーライド。希望のディレクトリを返すようにしておきます。

## resources/lang <br> public

Applicationクラスを派生して、それぞれ、langPath()と、publicPath()をオーバーライドするのが王道かと思いますが、それがめんどくさければ、AppServiceProviderで、<font color="red">サービスコンテナの 'path.lang'、'path.pulbic' を置き換え</font>ても動きます。ただし、これは、たまたまフレームワークでlangPathもpublicPathも、使っていないから。今後のバージョンアップで使うようになるかもしれません。

public を変更した場合は、 public/index.phpやDocument_rootを変更することも忘れずに。

**参考**
[Publicのディレクトリを移動する（ララジャパン）](https://www.larajapan.com/2016/02/27/public%E3%81%AE%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E7%A7%BB%E5%8B%95%E3%81%99%E3%82%8B/)

## .env <br> database <br> storage

それぞれ、Applicationクラスに、useEnvironmentPath()、useDatabasePath()、useStoragePath()というメソッドが用意されていて「どうぞ変更してください」という声が聞こえてきます。

ただ、注意したいのが、まずこれを使用するタイミング。

Applicationインスタンスが初期化された後、次にカーネルが初期化される中で、<font color="red">ServiceProvidersの初期化の**前**</font>に使用されているので、AppServiceProviderでは遅いです。 bootstrap/app.php で $app を受け取った直後に行ってください（この bootstrap/app.php を出た直後でも大丈夫です）。

```php:bootstrap/app.php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
*/
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

// ここで実行
$app->useStoragePath(__DIR__.'/../parent_temp/');
```

もう1つ注意は、.env の値が使えないこと。env()ヘルパー関数が使用不可。なので環境に依存するとしても、<font color="red">.envじゃない別の手段</font>を考える必要があります（OSレベルでセットしておくとか、AWSやHerokuの管理画面で入れておくとか……。って、もう.env要らないですね）。

**参考**
[Laravel5でstorageのパスを変更する](https://qiita.com/setouchi/items/689b11640bd2c1310619)


# ◎ フリーダム

開発者が自由に変更することを前提に作られているところ。変更は簡単だし影響は少ない。

## app/Providers <br> app/Http

app 以下のディレクトリはネームスペース管理なので自由に変更可能です。サービスプロバイダなどはデフォルトでいくつかファイルが入っていますが、そのファイル内のnamespaceと、参照元のクラス名を変更すれば問題ありません。

## routes <br> app\Http\Controllers

どちらも、RouteServiceProvider で設定されています。$namespaceというプロパティは他からも参照されているようなので、撤去してしまうと影響が出るかもしれません。

```php:app/Providers/RouteServiceProvider.php
    protected $namespace = 'App\Http\Controllers';
    protected function mapWebRoutes()
    {
        Route::middleware('web')
            ->namespace($this->namespace)
            ->group(base_path('routes/web.php'));
    }
```

## public/storage

標準では、storage/app/public へのシンボリックリンク。
`php artisan storage:link` で設定されます。なので、手動でリンクを貼ればOK。

## resources/views <br> storage/framework/views

VIEWの元テンプレートと、PHP化されたキャッシュ置き場ですね。
どちらも、config/views.php を変更します。
ただ、ディレクトリを作ってはくれないので、あらかじめ作っておく必要があります。
もともとそうなっていますが、.envに書いてenv()で拾ってくるようにすると良い感じです。

## storage/app/public <br> storage/framework/cache <br> storage/framework/sessions

それぞれ、configの、filesystems.php、cache.php、session.phpに書いてあるので変更すればOK。

## tests

ネームスペースで参照可能なように composer.json に記述されています。あとは、phpunit.xmlを変更すればOK。




# 感想

できるかできないか、だけであって、やるかやらないか、ではありません！
よほどの事情がない限り、無理な変更はせずにフレームワークに合わせたほうが良いと思います。合わせてください。よろしくおねがいします。