【環境構築】Docker+Laravel+Vite+TypeScriptの環境構築

# 1.はじめに
新卒として入社して数ヶ月経ち、新たプロジェクトの立ち上げに際して環境構築を手伝うことになり、調査等を行ったのでアウトプットを含めて残しておきます。
記事の中で至らないところもあると思いますが、何か間違い等ありましたらご指摘のほどよろしくお願いいたします。

# 2.背景
既存プロジェクトで開発の際に抱えていた2つの問題を解決する環境構築を行う
1. dockerを起動したあとに別途viteを起動する必要がある
2. ControllerやModelを作成すると、権限を変更しないと保存ができない

# 3.前提条件
+ WSLで利用するカーネルは、Ubuntu 22.04.03
+ Docker Desktopがインストール済み

# 4.システムバージョン(2024/7月時点)
## OS/ソフトウェア
+ Windows 11 Pro (実機)
+ Ubuntu 22.04.03 (WSL)
+ docker 25.0.3
## フロントエンド
+ Node 20.12.0
+ TypeScript 5.5.4
+ React 18.3.1
+ Vite 5.0.0
## バックエンド
+ PHP 8.2.21
+ Laravel 10.48.16
## データベース
+ MariaDB 10.7.8
## Webサーバー
+ Nginx 1.25

# 5.作成するコンテナ
### App
Laravelが動作するコンテナ。composerのインストールを行う。
### Node
Viteが動作するコンテナ。nodeやReactのパッケージをインストールする。
### DB
データベースを管理するコンテナ
### Nginx
Webサーバー

# 6.ディレクトリ構造
:::note warn
WSLのUbuntu内に作成すること
:::

```
プロジェクトフォルダ/
├── docker/
│   ├── mysql/
│   │   ├── Dockerfile
│   │   └── server.conf
│   │
│   ├── nginx/
│   │   ├── logs(空フォルダ)
│   │   ├── Dockerfile
│   │   └── default.conf
│   │
│   ├── app/
│   │   ├── Dockerfile
│   │   └── php.ini
│   │
│   ├── node/
│   │   └── Dockerfile
│   │
│   └──.gitignore 
│
├──www(Laravelプロジェクトフォルダ)
│
└── docker-compose.yaml
```

# 7.各種ファイル
## MySQL
```Dockerfile:docker/mysql/Dockerfile
FROM mariadb:10.7.8
COPY server.conf /etc/mysql/mariadb.conf.d/
```
```conf:docker/mysql/server.conf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
[client]
default-character-set=utf8mb4
```
## nginx
```Dockerfile:docker/nginx/Dockerfile
FROM nginx:1.25
COPY ./default.conf /etc/nginx/conf.d/default.conf
```
```conf:docker/nginx/default.conf
server {
    
    listen 80;

    client_max_body_size 1G;

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

    access_log /src/docker/nginx/logs/access.log;
    error_log  /src/docker/nginx/logs/error.log;

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

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

}
```
## PHP
```Dockerfile:docker/app/Dockerfile
FROM php:8.2-fpm
RUN apt-get update \
    && apt-get install -y \
    git \
    zip \
    unzip \
    vim \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libmcrypt-dev \
    libpng-dev \
    libfontconfig1 \
    libxrender1

RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install gd
RUN docker-php-ext-install bcmath
RUN docker-php-ext-install pdo_mysql mysqli exif
RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer

WORKDIR /src/www
ADD . /src/storage
RUN chown -R www-data:www-data /src/storage
```
```ini:php.ini
upload_max_filesize=256M
post_max_size=256M
```
## Node
```Dockerfile:docker/node/Dockerfile
FROM node:20.12.0
WORKDIR /src/www
CMD ["npm", "run", "dev"]
```
## 8.設定ファイル
```docker/.gitignore
nginx/logs/*
mysql/storage/
```
```yaml:docker-compose.yaml
services:
  app:
    build:
      context: ./docker/app
    user: "1000:1000"
    depends_on:
      - db
    volumes:
      - ./:/src
      - ./docker/app/php.ini:/usr/local/etc/php/php.ini
  
  node:
    build:
      context: ./docker/node
    tty: true
    ports:
      - 5173:5173
    volumes:
      - ./:/src
  
  db:
    build:
      context: "docker/mysql/"
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - 3306:3306
    environment:
      - MYSQL_DATABASE=database #データベース名
      - MYSQL_HOST=mysql
      - MYSQL_USER=user #ユーザ名
      - MYSQL_PASSWORD=pass #ユーザパスワード
      - MYSQL_ROOT_PASSWORD=root_pass #ルートユーザーのパスワード
      - TZ=Asia/Tokyo
    volumes:
      - "./docker/mysql/storage/:/var/lib/mysql"
      - "./docker/mysql/initdb/:/docker-entrypoint-initdb.d"
  nginx:
    build:
      context: ./docker/nginx
    depends_on:
      - app
    ports:
      - 80:80
    volumes:
      - ./:/src
      - ./docker/nginx/logs:/src/docker/nginx/logs

```

# 9.環境構築
:::note
以下の操作はUbuntuの環境で実行すること
:::
プロジェクトディレクトリに移動する
```bash
cd (プロジェクトディレクトリ)
```
laravelのプロジェクトが入るwwwのフォルダを作成する
```bash
mkdir www
```
dockerをビルドする
```docker
docker compose build
```
Laravelプロジェクトを作成する
```docker
docker compose run --rm app composer create-project "laravel/laravel=10.*" . --prefer-dist
```
:::note warn
Token (hidden):  No token given, aborting.と表示された場合。
ディレクトリの所有権の問題なので自身に変更する。
```bash
sudo chown -R $(whoami) www
```
:::
storageのシンボリックリンクを追加.
```docker
docker compose run --rm app php artisan storage:link
```
Appコンテナにパッケージをインストールする

```docker
docker compose run --rm app composer install
```
Nodeコンテナにパッケージをインストールする

```docker
docker compose run --rm node npm install
```
dockerを起動する
```docker
docker compose up -d
```
[localhost](http://localhost)にアクセスして起動確認を行う
以下の画面が表示される
![Laravelの画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2892903/1293a8ab-9200-fc17-c8f1-4c4d7f9980be.png)
vite.config.jsを編集する。(ファイル名も.jsから.tsに変更する)
```js:www/vite.config.ts→www/vite.config.ts
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [
        react(),
        laravel({
            input: [
                'resources/css/app.css',
                'resources/ts/main.tsx'
            ],
            refresh: true,
        }),
    ],
});
```
必要なパッケージをインストールする
```docker
docker compose exec node bash
```
```
npm install -D react react-dom @types/react @types/react-dom
npm install -D @vitejs/plugin-react
npm install -D typescript
npm install -D react-router-dom @types/react-router-dom
```
jsフォルダを削除し、新規にtsフォルダを作成する。
```bash
rm www/resources/js -R
mkdir www/resources/ts
```
welcome.blade.phpの編集
```html:www/resources/view/welcome.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>laravelView側タイトル</title>
    @viteReactRefresh
    @vite([
    'resources/css/app.css',
    'resources/ts/main.tsx',
    ])
</head>
<body>
    <div id="root"></div>
</body>
</html>

```
main.tsx、App.tsxを新規作成
```tsx:www/resources/ts/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
)
```
```tsx:www/resources/ts/App.tsx
import { useState } from 'react'

function App() {
  const [count, setCount] = useState(0)

  return (
    <>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  )
}

export default App
```
dockerを再起動し[localhost](http://localhost)にアクセスできることを確認する
```docker
docker compose down
docker compose up -d --build
```
以下の画面が表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2892903/6fbd2370-dae5-e3a1-645e-4c5122244299.png)





# 10.主要コマンド
dockerの起動
```docker
docker compose up -d
```
dockerの停止
```docker
docker compose stop
```
Laravel/コントローラーの作成
```docker
docker compose exec app php artisan make:controller (コントローラー名)
```
composerのインストール
```docker
docker compose exec app composer install (コンポーザー名)
```
packageのインストール
```docker
docker compose exec node npm install (パッケージ名)
```

# 11.おわりに
以上の手順でLaravelのプロジェクトの環境構築が行えます。
nodeコンテナの起動の前に、パッケージのインストールが必要だったりDockerfileのWROKDIRの定義について勉強になりました。
調べた限りNodeコンテナを立ち上げて作成している記事が見つからなかったので参考になればと思います。

# 12.参考サイト
**初期段階からViteの導入まで**
[Laravel 10 の開発環境をdockerで実現する方法](https://qiita.com/hitotch/items/869070c3a9f474a358ea)
**Laravelの編集権限関連**
[dockerのphpコンテナ内で新規作成したファイルの権限エラー](https://qiita.com/hiroshi19890606/items/73b04bbcfe65506d6e68)
**Viteを利用したReactの導入**
[LaravelにReact + TypeScript の開発環境を構築](https://qiita.com/pikimaru/items/062533c3cba6dbfe8f42)