DockerでLaravel-Mix(Laravel8×Vue3)環境構築

#はじめに
今回は、Laravel8をメインとして、その中でVue3を使う環境の構築方法をご紹介します。

#環境
- Docker version 20.10.12
- Docker Compose version v2.2.3
- Laravel Framework 8.81.0
- Vue 3.2.29


#構成
以下の構成を想定しています。


```
project
├── environment
│   └── development
│       ├── docker
│       │   ├── mysql
│       │   │   ├── Dockerfile
│       │   │   ├── my.cnf
│       │   │   └── sql
│       │   │       └── create-database.sql
│       │   ├── nginx
│       │   │   └── default.conf
│       │   ├── node
│       │   │   └── Dockerfile
│       │   └── php
│       │       ├── Dockerfile
│       │       └── php.ini
│       └── docker-compose.yml
└── server
```

#構築

##1. 全体(Docker)

### 1-1. docker-compose.yml作成
今回は、PHP, nginx, mysql, nodeの4つのコンテナを想定しておりますので、以下のようになります。

```docker-compose.yml
version: '3.9'

services:
  php:
    container_name: php
    build: docker/php
    volumes:
      - ./../../server:/var/www:cached

  node:
    container_name: node
    build: docker/node
    volumes:
      - ./../../server:/project:cached
    tty: true
#    command: bash -c "
#      npm install;
#      npm run watch;
#      "

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - 80:80
    volumes:
      - ./../../server:/var/www:cached
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:cached
    depends_on:
      - php
      - node

  mysql:
    build: docker/mysql
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: on-failure
    volumes:
      - mysql:/var/lib/mysql:cached
      - ./docker/mysql/sql:/docker-entrypoint-initdb.d:cached
    ports:
      - 3306:3306

volumes:
  mysql:
```

### 1-2. PHP
#### Dockerfile作成
phpコンテナ構築時に使われるDockerfileをdocker/php/に作成します。

```dockerfile:Dockerfile
FROM php:8.0-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
    && apt-get install -y zlib1g-dev libzip-dev mariadb-client libpng-dev unzip
RUN docker-php-ext-install zip pdo_mysql gd

#Composer install
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin

WORKDIR /var/www

RUN composer global require "laravel/installer"
```

#### PHP設定ファイル作成
PHPの設定ファイルをdocker/php/に作成します。以下、最低限の設定ですのでプロジェクトに応じて変更してください。

```php.ini
zend.exception_ignore_args = off
expose_php = on
max_execution_time = 60
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8

[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.language = "Japanese"
```

### 1-3. nginx
#### 設定ファイル作成
niginxの設定ファイルをdocker/nginx直下に作成します。以下はLaravelでの運用を前提に記載しています。

```nginx:default.conf
server {
  listen 80;

  root  /var/www/public;
  index index.php;

  location / {
    try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass   php:9000;
    fastcgi_index  index.php;

    include        fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO $fastcgi_path_info;
  }
}

```

### 1-4. MySQL
#### Dockerfile
```Dockerfile
FROM mysql:8
COPY my.cnf /etc/mysql/conf.d/
RUN chmod 644 /etc/mysql/conf.d/my.cnf
```

#### 設定ファイル
```my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4
```

#### SQL
`docker/mysql/sql/create-database.sql`にてデータベースを作成するSQLを用意します。

```sql
CREATE DATABASE IF NOT EXISTS sample
CREATE DATABASE IF NOT EXISTS sample_test
```

### 1-5. Node
#### Dockerfile
```Dockerfile
FROM node:16.0.0
WORKDIR /project

RUN npm install -g agentkeepalive --save
RUN npm install -g npm@8.4.0
RUN npm cache clean -f
```

### 1-6. Docker起動
dockerを起動して、Laravelプロジェクトを作成してみましょう。
docker-compose.ymlがあるディレクトリに移動して下記でdockerを起動してみましょう。

```sh
cd environment/development
docker-compose up -d
```

## 2. Laravel
### 2-1. プロジェクト作成
コンテナに入って、Laravelプロジェクトを作成します。

```console
cd environment/development

# phpコンテナに入ります
docker-compose exec php bash

# Laravelプロジェクト作成
composer create-project --prefer-dist laravel/laravel ./
```
server以下にLaravelの各ディレクトリやファイルができています。

### 2-2. 動作確認
ブラウザからlocalhostにアクセスしてみましょう。以下のようにLaravelの画面が立ち上がっていれば成功です。
![スクリーンショット 2022-02-01 10.24.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/50b389ee-f580-b4bb-835b-a5deb646a9c8.png)

## 3. MySQL
MySQLのコンテナへ接続しましょう。

### 3-1. .env設定
MySQLコンテナ及びデータベースへの接続情報を.envに記載します。

```.env
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=sample
DB_USERNAME=root
DB_PASSWORD=root
```

### 3-2. マイグレーションの実行

```console
cd environment/development

# phpコンテナに入ります
docker-compose exec php bash

# マイグレーションの実行
root@de1e51270b2b:/var/www# php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (38.74ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (26.03ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (29.34ms)
Migrating: 2019_12_14_000001_create_personal_access_tokens_table
Migrated:  2019_12_14_000001_create_personal_access_tokens_table (40.38ms)
```

## 4. Vue

### 4-1. Vueインストール
```sh
cd environment/development

# nodeコンテナに入る
docker-compose exec node bash

# vueをインストール
npm install vue@next vue-loader@next @vue/compiler-sfc
```

### 4-2. webpack.min.jsを修正
下記のように`vue()`を追加します。

```js:server/webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [
        //
    ]).vue();
```

### 4-3. Vueコンポーネント作成
`server/resources/js`の配下に`components`のディレクトリを作成して、`resources/js/components/TestVue.vue`を作成します。

```vue:server/resources/js/components/TestVue.vue
<template>
  Hello Vue !!
</template>
<script>
import { onMounted } from 'vue'
export default ({
  setup() {
    console.log("start vue")

    onMounted(() => {
      console.log('start mounted !')
    })
  },
})
</script>
```

### 4-4. Vueのマウント
`server/resources/js/app.js`を下記のように編集します。

```js:server/resources/js/app.js
require('./bootstrap');

import { createApp } from 'vue'
import TestVue from './components/TestVue.vue';

const app = createApp({})
app.component('test-vue', TestVue);
app.mount('#app')
```

### 4-5. ビルド
```sh
cd environment/development
docker-compose exec node bash -c "npm run dev"
```

### 4-6. Laravelビューファイルを作成する
`server/resources/views/app.blade.php`を作成します。

```php:server/resources/views/app.blade.php
<!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</title>
        <link href="{{ asset('css/app.css') }}" rel="stylesheet"/>
        <script src="{{ asset('js/app.js') }}" defer="defer"></script>
    </head>
    <body>
        <div id="app">
            <test-vue></test-vue>
        </div>
    </body>
</html>
```

### 4-7. Laravelのルーティング修正
`server/routes/web.php`を以下のように修正します。

```php
Route::get('/', function () {
    return view('app');
});
```

### 4-8. Laravelキャッシュクリア
上記の手順で一度ビューやルーティングのキャッシュファイルが作成されていますので、以下のコマンドでキャッシュクリアしましょう。（不要なものも含まれていますが。。。）

```sh
cd environment/development
docker-compose exec php bash -c "
php artisan cache:clear &&
php artisan config:clear &&
php artisan config:cache &&
php artisan route:clear &&
php artisan view:clear &&
php artisan clear-compiled &&
php artisan optimize &&
composer dump-autoload &&
rm -f bootstrap/cache/config.php"
```

### 4-9. 動作確認
|![スクリーンショット 2022-02-01 11.00.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/d7853b6f-f5fd-a04c-9820-0524b63035c5.png)|
|---|

## おまけ
### 
nodeコンテナのコマンドのコメントアウトを外すと、ファイル変更のたびに`npm run dev`をする必要がなくなるので開発は楽になります！

```docker-compose.yml
version: '3.9'

services:
  php:
    container_name: php
    build: docker/php
    volumes:
      - ./../../server:/var/www:cached

  node:
    container_name: node
    build: docker/node
    volumes:
      - ./../../server:/project:cached
    tty: true
    command: bash -c "
      npm install;
      npm run watch;
      "

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - 80:80
    volumes:
      - ./../../server:/var/www:cached
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:cached
    depends_on:
      - php
      - node

  mysql:
    build: docker/mysql
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: sample
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: on-failure
    volumes:
      - mysql:/var/lib/mysql:cached
      - ./docker/mysql/sql:/docker-entrypoint-initdb.d:cached
    ports:
      - 3306:3306

volumes:
  mysql:
```

### Laravel Mixで環境に合わせて出力ファイルを変える方法
Laravel Mixを利用する場合に、本番環境と開発環境で出力されるJSファイルやCSSファイルを分けたいと思います。
その方法については、以下の記事で紹介しています。
[詳細](https://xn--qiita-543doa.com/A-Kira/items/f6b5b091b271b6229c6b)

#おわりに
SPAはSEOに弱いということで、SSRで構築することがありますが、個人的にはかなり構築しづらかったので、SEOこだわるなら今回紹介した構成でいいのでは？と、思っていますがみなさんはいかがでしょうか？

成果物は[Github](https://github.com/Akira-Demizu/developments/tree/main/laravel8_vue3)にありますので、必要な方はご覧ください。

# 参考
- [LaravelでVue.js(v3)を使う方法！](https://codelikes.com/laravel-use-vue3/)