【Docker】Laravel + Vue.js + MySQL + nginx + phpMyAdminの開発環境構築【LEMP環境】

# 0. はじめに
こんにちは、kazumakishimoto([@kazuma_dev](https://twitter.com/kazuma_dev))です。
関西のLaravelエンジニア『ゆーたろーさん([@shimotaroo](https://twitter.com/shimotaroo))』の記事をアレンジして、`Docker`で`Laravel + Vue.js + MySQL + nginx + phpMyAdmin`の開発環境を構築しました！

https://github.com/shimotaroo/docker-laravel-vuejs-sample

## 0-1. 全体の流れ
> [1.環境構築](#1-環境構築)
> [2.初期設定](#2-初期設定)
> [3.ライブラリ](#3-ライブラリ)
> [4.補足](#4-補足)
> [Reference](#reference)

## 0-2. 本記事の対象者
- DockerでLEMP環境構築(Laravel + Vue.js + MySQL + nginx + phpMyAdmin)

## 0-3. 事前準備
- ①`MySQL`は5.7系を選定②`phpMyAdmin`のコンテナを追加

https://qiita.com/furu8ma/items/50718efebee20fd24517

# 1. 環境構築
## 1-1. docker-compose.yml
- セキュリティの観点から環境変数の値は`.env`から呼び出し
```yaml:docker-compose.yml
version: '3.8'

volumes:
  mysql-volume:

services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    volumes:
      - ./src/:/var/www/html
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=db
      - DB_PORT=3306
      - DB_DATABASE=${DB_NAME}
      - DB_USERNAME=${DB_USER}
      - DB_PASSWORD=${DB_PASSWORD}

  web:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports:
      - ${WEB_PORT}:80
    depends_on:
      - app
    volumes:
      - ./src/:/var/www/html

  db:
    image: mysql:5.7
    platform: linux/x86_64
    build:
      context: .
      dockerfile: ./docker/mysql/Dockerfile
    ports:
      - ${DB_PORT}:3306
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      TZ: 'Asia/Tokyo'
    volumes:
      - mysql-volume:/var/lib/mysql

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
      - PMA_USER=${DB_USER}
      - PMA_PASSWORD=${DB_PASSWORD}
    links:
      - db
    ports:
      - 8080:80
    volumes:
      - ./docker/phpmyadmin/sessions:/sessions
```

## 1-2. Dockerfile（app）
```dockerfile:docker/php/Dockerfile
FROM php:7.4.1-fpm

# COPY php.ini
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

# Composer install
COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer

# install Node.js
COPY --from=node:10.22 /usr/local/bin /usr/local/bin
COPY --from=node:10.22 /usr/local/lib /usr/local/lib

RUN apt-get update && \
  apt-get -y install \
  git \
  zip \
  unzip \
  vim \
  && docker-php-ext-install pdo_mysql bcmath && \
  pecl install xdebug && \
  docker-php-ext-enable xdebug

WORKDIR /var/www/html
```

## 1-3. php.ini
```ini:docker/php/php.ini
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
[xdebug]
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.log=/tmp/xdebug.log
xdebug.log_level=0
```

## 1-4. Dockerfile（web）
```dockerfile:docker/nginx/Dockerfile
FROM nginx:1.18-alpine

ENV TZ=UTC

# nginx config file
COPY ./docker/nginx/*.conf /etc/nginx/conf.d/

WORKDIR /var/www/html
```

## 1-5. default.conf
```conf:docker/nginx/default.conf
server {
    listen 80;

    root /var/www/html/public;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.php index.html index.htm;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
```

## 1-6. Dockerfile（db）
```dockerfile:docker/mysql/Dockerfile
FROM --platform=linux/x86_64 mysql:5.7

ENV TZ=UTC

COPY ./docker/mysql/my.cnf /etc/my.cnf
```

## 1-7. my.cnf
```cnf:docker/mysql/my.cnf
[mysqld]
user=mysql
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

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

## 1-8. .env
```.env:.env
WEB_PORT=80
DB_PORT=3306

DB_NAME=データベース名
DB_USER=ユーザー名
DB_PASSWORD=パスワード
DB_ROOT_PASSWORD=ルートパスワード
```

## 1-9. .gitignore
```.gitignore:.gitignore
.env
```

## 1-10. Laravelインストール
```terminal:local
$ docker compose build
$ docker compose up -d
$ docker-compose exec app bash
# composer create-project --prefer-dist "laravel/laravel=6.*" .
```

## 1-11. Vue.jsインストール
```terminal:docker
# npm install -D vue
# npm install -D vue-template-compiler
```


# 2. 初期設定
## 2-1. APP_NAME
```.env:.env
APP_NAME=アプリケーション名
```
```php:config/app.php
'name' => env('APP_NAME', 'アプリケーション名'),
```

## 2-2. タイムゾーン
```diff:config/app.php
- 'timezone' => 'UTC',
+ 'timezone' => 'Asia/Tokyo',
```

## 2-3. 言語設定
```diff:config/app.php
- 'locale' => 'en',
- 'faker_locale' => 'en_US',
+ 'locale' => 'ja',
+ 'faker_locale' => 'ja_JP',
```

## 2-4. DB文字コード
```diff:config/database.php
'mysql' => [
- 'charset' => 'utf8mb4',
- 'collation' => 'utf8mb4_unicode_ci',
+ 'charset' => 'utf8',
+ 'collation' => 'utf8_unicode_ci',
],
```

## 2-5. Modelsのディレクトリ構成変更
```terminal:local
$ mkdir app/Models
```
```diff:app/Models/User.php
- namespace App;
+ namespace App\Models;
```
```diff:/app/Http/Controllers/Auth/RegisterController.php
use App\Http\Controllers\Controller;
- use App\User;
+ use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
```
```diff:config/auth.php
     'providers' => [ 
         'users' => [ 
             'driver' => 'eloquent', 
-             'model' => App\User::class, 
+             'model' => App\Models\User::class, 
         ], 
```
```diff:/config/services.php
    'stripe' => [
-        'model' => App\User::class,
+        'model' => App\Models\User::class,
        'key' => env('STRIPE_KEY'),
        'secret' => env('STRIPE_SECRET'),
        'webhook' => [
            'secret' => env('STRIPE_WEBHOOK_SECRET'),
            'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
        ],
    ],
```
```diff:database/factories/UserFactory.php
- $factory->define(App\User::class, function (Faker $faker) {
+ $factory->define(App\Models\User::class, function (Faker $faker) {
    return [
```
```diff:composer.json
    "autoload": {
        "psr-4": {
            "App\\": "app/",
-           "Models\\": "app/"
+           "Models\\": "app/Models/"
        },
```

https://qiita.com/10mi8o/items/6329d3d4e2f5b6dd26d5

https://qiita.com/vrvr/items/4755e758f5d4b2e07579

## 2-6. エラーメッセージ日本語化
- `config/app.php`の`locale`が`ja`になっていることを確認
```terminal:local
$ cd resources/lang
$ mkdir ja
$ cd resources/lang/ja
```
```terminal:local
$ curl -OL https://raw.githubusercontent.com/rito-nishino/Laravel-Japanese-Language-fileset/master/auth.php
$ curl -OL https://raw.githubusercontent.com/rito-nishino/Laravel-Japanese-Language-fileset/master/pagination.php
$ curl -OL https://raw.githubusercontent.com/rito-nishino/Laravel-Japanese-Language-fileset/master/passwords.php
$ curl -OL https://raw.githubusercontent.com/rito-nishino/Laravel-Japanese-Language-fileset/master/validation.php
```

https://qiita.com/samuraibrass/items/d71c08e144dbbf98e348

## 2-7. Xdebug3
```diff:docker/php/Dockerfile
FROM php:7.4.1-fpm-alpine

# COPY php.ini
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini

# Composer install
COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer

# install Node.js
COPY --from=node:10.22 /usr/local/bin /usr/local/bin
COPY --from=node:10.22 /usr/local/lib /usr/local/lib

# alpine ベースのイメージでなければ autoconf gcc g++ make は不要
RUN apk update && \
    apk add \
    git \
    zip \
    unzip \
    vim \
    autoconf gcc g++ make && \
    docker-php-ext-install pdo_mysql bcmath && \
+   pecl install xdebug && \
+   docker-php-ext-enable xdebug

WORKDIR /var/www/html
```
```diff:docker/php/php.ini
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"
+ [xdebug]
+ xdebug.mode=debug
+ xdebug.start_with_request=yes
+ xdebug.client_host=host.docker.internal
+ xdebug.client_port=9003
+ xdebug.log=/tmp/xdebug.log
+ xdebug.log_level=0
```
```terminal:local
$ docker-compose up -d --build
```
```json:.vscode/launch.json
{
  // IntelliSense を使用して利用可能な属性を学べます。
  // 既存の属性の説明をホバーして表示します。
  // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Listen for Xdebug",
      "type": "php",
      "request": "launch",
      "port": 9003,
      "pathMappings": {
        "/var/www/html": "${workspaceRoot}/src"
      },
    },
  ]
}
```

https://yutaro-blog.net/2021/07/08/laravel-docker-xdebug-vscode/


# 3. ライブラリ
```terminal:local
$ docker-compose exec app bash
```

## 3-1. debagbar
```terminal:docker
# composer require --dev barryvdh/laravel-debugbar --dev
# php artisan vendor:publish --provider="Barryvdh\Debugbar\ServiceProvider"
# php artisan cache:clear
# php artisan config:clear
```

## 3-2. doctrin/dbal
- Laravel-IDE-HelperでModel のプロパティ補完を行う場合は [doctrine/dbal](https://github.com/doctrine/dbal) が必要なのでインストール。
```terminal:docker
# composer require "doctrine/dbal:2.*"
```

https://qiita.com/PruneMazui/items/74034913bcd4af7a4eaf

## 3-3. IDE-Helper
```terminal:docker
# composer require --dev barryvdh/laravel-ide-helper:2.8.*
# php artisan ide-helper:generate
# php artisan ide-helper:models -N
```

## 3-4. Laravel Socialite
```terminal:docker
# composer require laravel/socialite
```

```diff:config/services.php
<?php

return [
    // 略
    'ses' => [
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
    ],
+
+   'google' => [
+       'client_id' => env('GOOGLE_CLIENT_ID'),
+       'client_secret' => env('GOOGLE_CLIENT_SECRET'),
+       'redirect' => env('APP_URL') . '/login/google/callback',
+   ],
+   'twitter' => [
+       'client_id' => env('TWITTER_CLIENT_ID'),
+       'client_secret' => env('TWITTER_CLIENT_SECRET'),
+       'redirect' => env('APP_URL') . '/login/twitter/callback',
+   ],
];
```

## 3-5. Laravel/ui vue
- Laravel6系の場合、`ver1.0`をインストール
```terminal:docker
# composer require laravel/ui:^1.0 --dev
# php artisan ui vue --auth
# npm install && npm run dev
```
```diff:app/Http/Controllers/Auth/RegisterController.php
- protected $redirectTo = '/home';
+ protected $redirectTo = '/';
```
```diff:app/Http/Controllers/Auth/LoginController.php
- protected $redirectTo = '/home';
+ protected $redirectTo = '/';
```

https://qiita.com/daisu_yamazaki/items/b946594896179abcd203

## 3-6. Laravel Mix
```diff:webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
-   .sass('resources/sass/app.scss', 'public/css');
-   .sass('resources/sass/app.scss', 'public/css')
+   .version();
```
```diff:.gitignore
/node_modules 
+ /public/css
/public/hot 
+ /public/js
+ /public/mix-manifest.json
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
```
```terminal:docker
# npm run watch-poll
```
```diff:resources/views/app.blade.php
<body>
+ <div id="app">
    @yield('content')
+ </div>
+
+ <script src="{{ mix('js/app.js') }}"></script>
  <!-- JQuery -->
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>  
```


# 4. 補足
## 4-1. 開発環境（FW/ツールのバージョンなど）
|ツール|バージョン|
|:--|:--|
|Vue.js|2.6.14|
|jQuery|3.4.1|
|PHP|7.4.1|
|Laravel|6.20.43|
|MySQL|5.7.36|
|Nginx|1.18.0|
|Composer|2.0.14|
|npm|6.14.6|
|Git|2.33.1|
|Docker|20.10.11|
|docker-compose|v2.2.1|
|PHPUnit|8.0|
|CircleCI|2.1|
|heroku|7.59.4|
|MacBook Air|M1,2020|
|macOS|Monterey 12.3|
|Homebrew|3.3.8|

## 4-2. ディレクトリ構造
```
【ルートディレクトリ】
├─ .circleci
│   └─ config.yml
├─ aws / CloudFormation
│   └─ ec2.yml
├─ docker
│   └─ mysql
│   └─ nginx
│   └─ php
│   └─ phpmyadmin
├─ src
│   └─ 【Laravelのパッケージ】
│─ .env
│─ .gitignore
└─ docker-compose.yml
```

# Reference

- shimotaroo/docker-laravel-vuejs-sample: Laravel6×Vue.js2のDocker環境サンプル(M1 Mac対応)

https://github.com/shimotaroo/docker-laravel-vuejs-sample

- 【導入編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜 | yutaro blog

https://yutaro-blog.net/2021/04/28/docker-laravel-vuejs-1/

- 【前編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜 | yutaro blog

https://yutaro-blog.net/2021/04/29/docker-laravel-vuejs-2/

- 【後編】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS Intel Chip対応〜 | yutaro blog

https://yutaro-blog.net/2021/04/30/docker-laravel-vuejs-3/

- 【M1 Mac版】絶対に失敗しないDockerでLaravel + Vue.jsの開発環境（LEMP環境）を構築する方法〜MacOS M1 Chip対応〜 | yutaro blog

https://yutaro-blog.net/2021/05/25/docker-laravel-vuejs-m1/

- phpMyAdmin on docker が便利すぎる - Qiita

https://qiita.com/furu8ma/items/50718efebee20fd24517

- M1Mac環境でDockerのMySQLを動かす - Qiita

https://qiita.com/toyoyuto618/items/f225e1b2ab22ca5adf33

- Laraveの知識をアウトプットして、資産化してます - Qiit

https://qiita.com/gyu_outputs/items/eaff3072e04bc7cb44e6

- Laravelをインストールしたらやっていること一覧 - Qiita

https://qiita.com/schrosis/items/a41c132ad002a9eebf02

- Docker X Laravel 初期設定 - Qiita

https://qiita.com/ucan-lab/items/8eab84e37421f907dea0

- 【Laravel】モデルのディレクトリ構成変更についてのメモ - Qiita

https://qiita.com/vrvr/items/4755e758f5d4b2e07579

- [Laravel 6] Modelsディレクトリを作る - Qiita

https://qiita.com/10mi8o/items/6329d3d4e2f5b6dd26d5

- Laravelのバリデーションエラーメッセージを5分で日本語化する - Qiita

https://qiita.com/samuraibrass/items/d71c08e144dbbf98e348

- 【Laravel】Docker環境にXdebug3を導入&VSCodeでデバッグ(M1対応) | yutaro blog

https://yutaro-blog.net/2021/07/08/laravel-docker-xdebug-vscode/

- [laravel] doctrine/dbalを入れたのに「Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found」と言われる - Qiita

https://qiita.com/snbk/items/803aea10773052629a45

- 【Laravel8】IDE補完ファイル作成パッケージ「barryvdh/laravel-ide-helper」の導入

https://zenn.dev/shimotaroo/articles/1d1a1e7d6e2206

- [Laravel] IDEによるコード補完まとめ - Qiita

https://qiita.com/PruneMazui/items/74034913bcd4af7a4eaf

- Laravel+Socialiteで簡単ソーシャルログイン実装! (テスト付き) - Qiita

https://qiita.com/KeisukeKudo/items/18dd8a342a4bdd43913c

- Laravel SocialiteでGoogleログインを実現する

https://zenn.dev/naoki_oshiumi/articles/80ab178d958447

- Laravel8 (laravel/ui)でのLogin機能の実装方法メモ - Qiita

https://qiita.com/daisu_yamazaki/items/b946594896179abcd203

- Laravel Mixとは？webpackをより便利に、簡単に。Laravel以外でも使えるよ。 - Qiita

https://qiita.com/minato-naka/items/bfc3bbd9a388084e6f17

- PHPとLaravelでEvernote風のメモアプリを作ってみよう！ | Techpit

https://www.techpit.jp/courses/132

- Laravel(+Vue.js)でSNS風Webサービスを作ろう！ | Techpit

https://www.techpit.jp/courses/11