Laravel 10 と Vue 3 の開発環境を Docker で構築する

# はじめに
LaravelとVueを触れる環境をローカルを汚さずに作りたかったので、Dockerで作ってみました。
Laravelは毎年バージョンアップされてサポートもどんどん切れていきますし、Vue2は今年いっぱいでサポートが切れます。ですので、今から触るなら新しいバージョンを触りたいなと思ったので、それぞれLaravel10とVue3を選びました。
ミドルウェアは、nginxとPostgreSQL、PHP用にphp-fpmを使っています。

Laravel10とVue3構築の記事はあるのですが、Dockerと絡めた記事は意外と少なく、色々調べながらやったのでその辺りを書いていきたいと思います。

# 筆者のローカル環境
```
$ sw_vers
ProductName:	macOS
ProductVersion:	12.6.1
BuildVersion:	21G217
$ docker -v
Docker version 20.10.21, build baeda1f
$ docker-compose -v
docker-compose version 1.29.2, build 5becea4c
```

# Dockerfile と docker-compose.yml
LaravelやVueをインストールする前のベースとなるDockerのテンプレートは、下記です。
これを元に進めていきますので、手元で実際に作業をされる方はクローンをお願いいたします。

https://github.com/yutotakakura/laravel-vue-template

```docker-compose up -d```で、下記3つのコンテナが立ち上がります。
- php-fpmが動くコンテナ(以下 appコンテナ)
- PostgreSQLが動くコンテナ(以下 dbコンテナ)
- nginxが動くコンテナ(以下 webコンテナ)

基本的には、下記の記事をベースに色々いじって作りました。非常にわかりやすくて、筆者の方に感謝です。
- [【前編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜](https://yutaro-blog.net/2021/04/29/docker-laravel-vuejs-2/)
- [【後編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜](https://yutaro-blog.net/2021/04/30/docker-laravel-vuejs-3/)

Dockerの基礎知識や各種記載の詳細なことは上記記事で解説されているので、この記事では省略させていただきます。

今回、各種イメージの指定バージョンを上げたり、MySQLからPostgreSQLに変更したりしています。差分は[このコミット](https://github.com/tech-yuto/laravel-vue-template/commit/b8c257d0f14ec76d1e1caed726d8b79c936ff853)で確認できます。主に、下記の条件を満たすための変更です。
- Laravel10では、PHPの8.1以上が必要[（公式ドキュメント）](https://laravel.com/docs/10.x/upgrade#updating-dependencies)
- Vue3では、Node.jsのバージョン16.0 以上が必要[（公式ドキュメント）](https://ja.vuejs.org/guide/quick-start.html#creating-a-vue-application)

また、Laravel10ではフロントのバンドルツールとして[Vite](https://ja.vitejs.dev/)(ヴィートと読みます)がデフォルトになっています。
ローカル(コンテナ外)からappコンテナ内のViteの開発サーバー5173ポートにアクセスするために、```docker/php/Dockerfile```に下記の記載を追記しています。
```dockerfile
# Viteのサーバーにローカルからアクセスするために開ける
EXPOSE 5173
```
```docker-compose.yml```でもポートバインドをしています。
```docker-compose.yml
ports:
  - 5173:5173
```
筆者はこの記載の必要性に気付けず、結構ハマりました。

# 環境変数の設定
```.env.example```をコピーして、```.env```を同ディレクトリに作成します。
```.env.example
WEB_PORT=80
DB_PORT=5432

DB_NAME=
DB_USER=
DB_PASSWORD=
```
```.env(設定の例)
WEB_PORT=80
DB_PORT=5432

DB_NAME=mydb
DB_USER=yuto
DB_PASSWORD=pass
```
DBの3つの環境変数は、自由に設定していただければと思います。これがdbコンテナのデフォルトの設定値になり、appコンテナからDBアクセスする際にもこれが参照されます。Laravel内にも.envはありますが、コンテナで環境変数を定義している場合、その値が優先されます。（[公式ドキュメント](https://laravel.com/docs/10.x/configuration)）。

# Laravelの環境構築
## インストール
```docker-compose up -d```でコンテナを立ち上げたら、PHPイメージを立ち上げたappコンテナにLaravel10をインストールします。
```
$ docker-compose exec app composer create-project --prefer-dist "laravel/laravel=10.*" .
```
ローカルに```src```ディレクトリが作成され、その中にLaravelのファイルが配置されます。下記の記述でVolumeで共有しているためです。
```docker-compose.yml
volumes:
  - ./src/:/var/www/html
```
```localhost:80```にアクセスすると、Laravelの初期ページが表示されます。
![スクリーンショット 2023-02-27 19.58.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447035/28f1e075-ff61-2a34-dedb-398dddb27c3f.png)
## DBとの接続確認
appコンテナとdbコンテナの接続を確認するため、デフォルトで入っているマイグレーションを流してみます。
```
$ docker-compose exec app php artisan migrate

Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0

   INFO  Preparing database.

  Creating migration table ............................................................................................................... 15ms DONE

   INFO  Running migrations.

  2014_10_12_000000_create_users_table ................................................................................................... 29ms DONE
  2014_10_12_100000_create_password_resets_table .......................................................................................... 4ms DONE
  2019_08_19_000000_create_failed_jobs_table .............................................................................................. 6ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ................................................................................... 6ms DONE
```
無事に流れました。一応DBも確認してみると、
![スクリーンショット 2023-02-26 17.11.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447035/e5d93d21-ffae-4805-a3bb-2c54c7e0c89a.png)
無事にテーブルが作成され、レコードが入っています。

# Vueの環境構築
## 必要なパッケージのインストール
一旦、現在の```src/package.json```に記載のあるものを入れます。```src/node_modules```が作成されます。
```
$ docker-compose exec app npm install
```
Vueを動かすのに必要なパッケージをインストールします。```Vue```はVueそのもので、```@vitejs/plugin-vue```は、[Vite](https://ja.vitejs.dev/)がVueをバンドルできるようにするためのものです。[Laravel公式ドキュメント](https://laravel.com/docs/9.x/vite#vue)にも記載があります。
```
$ docker-compose exec app npm install --save-dev vue @vitejs/plugin-vue
```
ちなみに、Laravel9からフロントのバンドルツールは[Vite](https://ja.vitejs.dev/)がデフォルトになっています。筆者は[webpack](https://webpack.js.org/)しか触ったことがなかったので、今回初めて触りました。

## Viteの設定の変更
```src/vite.config.js```に、下記のように追記します。
```diff_javascript
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
+ import vue from '@vitejs/plugin-vue';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
+       vue(),
    ],
+   server: {
+       host: true,
+   },
});
```
詳しく見ていきます。
下記の箇所で、先ほどインストールしたViteのVueプラグインを有効化しています。
```javascript
import vue from '@vitejs/plugin-vue';
// (省略)
vue(),
```
そして、下記の記載の必要性に気づけず、筆者は結構ハマりました。
```javascript
server: {
    host: true,
},
```
これを記載しないと、コンテナ内では5173番ポートでViteのサーバーにアクセスできるのに、ローカル(コンテナ外)からはアクセスできないという状況になってしまいます。[Viteの公式ドキュメント](https://ja.vitejs.dev/config/server-options.html#server-host)にも記載があります。

先ほども述べましたが、同じ理由で、appコンテナは5173番を開放していて、```docker-compose.yml```でもポートバインドしています。
```Dockerfile
# Viteのサーバーにローカルからアクセスするために開ける
EXPOSE 5173
```
```docker-compose.yml
ports:
  - 5173:5173
```
## Vueがバンドルされることを確認する
ルートのルーティングに設定されている```src/resources/views/welcome.blade.php```を、下記のように変更します。
```html
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel Vue</title>
        @vite(['resources/css/app.css', 'resources/js/app.js'])
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
```
これは、```vite.config.js```内の下記の記述と合わせています。
```vite.config.js
input: ['resources/css/app.css', 'resources/js/app.js'],
```
cssやjsはViteによって上記ファイルにバンドルされるので、それを```welcome.blade.php```で読み込んでいます。

続いて、```src/recouces/js/app.js```を、下記のように変更します。Vueの初期マウントの記述です。[Vue公式ドキュメント](https://ja.vuejs.org/guide/essentials/application.html)通りの記述です。
```app.js
import { createApp } from "vue";

import App from "./App.vue";

const app = createApp(App);

app.mount("#app");
```
```src/resources/js/App.vue```を作成します。
せっかくなので、Vue3で導入された[Composition API](https://ja.vuejs.org/guide/introduction.html#api-styles)で書いてみます。
```App.vue
<script setup>
import { ref } from 'vue'

const message = ref('Hello World!')
</script>

<template>
  <h1>{{ message }}</h1>
</template>
```
appコンテナ内で、Viteの開発用サーバーを立ち上げます。
```
$ docker-compose exec app npm run dev
```
```localhost:80```にアクセスすると、```App.vue```の内容が表示されます。
![スクリーンショット 2023-02-27 20.06.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1447035/84cf3b9b-3834-99a6-28bb-5e588a5c549c.png)
Vueの構文が正しくバンドルされていることが確認できました。
少し長かったですが、お疲れ様でした。

# 終わりに
今回は開発環境の構築まででした。これから、簡単なSPAを作ってみてデバッグを試していきたいと思います。
また、動作環境としてフロントのファイルをビルドしてホストするところもやりたいと思っています。

この記事で構築した環境でSPA開発の練習をされたい方は、以下の記事が参考になるかと思います。

https://qiita.com/yutotakakura/items/88f0dfcb98beacb4725c

# 参照
- [【前編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜](https://yutaro-blog.net/2021/04/29/docker-laravel-vuejs-2/)
- [【後編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜](https://yutaro-blog.net/2021/04/30/docker-laravel-vuejs-3/)
- [Laravel9のVite環境でVue.js 3を利用する方法](https://reffect.co.jp/laravel/laravel9_vite)
- [Laravel公式ドキュメントの"Asset Bundling (Vite)"](https://laravel.com/docs/10.x/vite)
- [Viteの公式ドキュメントの"server.host"](https://ja.vitejs.dev/config/server-options.html#server-host)
- [Laravel 9 + VITEの開発環境をdockerで実現する方法](https://qiita.com/hitotch/items/aa319c49d625c2a9b65e)