こんにちは缶コーラです。
今回は完全に個人用のメモです。
開発環境
技術名 | バージョン | この記事での用途 |
---|---|---|
Node | 18.19.0 | Nuxt用のコンテナに使用されるNode.js |
Nuxt | 3.10.0 | フロント用のフレームワーク(APIサーバーとは分離想定) |
Vue | 3.4.15 | Javascriptライブラリ |
Laravel | 10.43.0 | APIサーバー |
Docker | 24.0.5 | ローカル開発環境の構築 |
Nginx | 1.20.2 | フロント・サーバーそれぞれのリバースプロキシ |
ディレクトリ構成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | - filament-post-project - frontend # フロントエンドリソース - backend # バックエンドリソース - docker - front Dockerfile - php Dockerfile php.ini - php-fpm.d zzz-www.conf - nginx Dockerfile default.conf - db docker-compose.yml |
docker-composeファイルの作成
filament-post-projectディレクトリの中にdocker-compose.ymlを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | volumes: php-fpm-socket: psysh-store: services: app: build: context: . dockerfile: ./docker/php/Dockerfile container_name: sample_project_app volumes: - type: volume source: php-fpm-socket target: /var/run/php-fpm volume: nocopy: true - type: bind source: ./backend target: /work/backend - type: volume source: psysh-store target: /root/.config/psysh volume: nocopy: true web: build: context: . dockerfile: ./docker/nginx/Dockerfile container_name: sample_project_nginx ports: - ${WEB_PORT}:80 - "9000:9000" volumes: - type: volume source: php-fpm-socket target: /var/run/php-fpm volume: nocopy: true - type: bind source: ./backend target: /work/backend db: image: mysql:8.0.33 container_name: sample_project_db environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: database MYSQL_USER: db-user MYSQL_PASSWORD: db-pass TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - ./docker/db/data:/var/lib/mysql - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf - ./docker/db/sql:/docker-entrypoint-initdb.d ports: - 3306:3306 front: build: context: . dockerfile: ./docker/front/Dockerfile container_name: sample_project_front volumes: - ./frontend:/var/www/nuxt ports: - "${FRONT_PORT}:3000" - "24678:24678" tty: true # command: sh -c "yarn && cd nuxt-project && yarn dev" |
appコンテナがLaravel、webコンテナがnginx、dbコンテナがmysql、frontコンテナがnuxtとなります。
1 2 3 | ports: - ${WEB_PORT}:80 - "9000:9000" |
上記のようにnginxでポートを2つ指定しているのはフロント、サーバー共にリバースプロキシさせるためです。(80番アクセスでフロント、9000番アクセスでAPI)
PHPコンテナ
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | FROM php:8.2-fpm SHELL ["/bin/bash", "-oeux", "pipefail", "-c"] # timezone environment ENV TZ=UTC \ # locale LANG=en_US.UTF-8 \ LANGUAGE=en_US:en \ LC_ALL=en_US.UTF-8 \ # composer environment COMPOSER_ALLOW_SUPERUSER=1 \ COMPOSER_HOME=/composer COPY --from=composer:2.2 /usr/bin/composer /usr/bin/composer RUN apt-get update && \ apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && \ locale-gen en_US.UTF-8 && \ localedef -f UTF-8 -i en_US en_US.UTF-8 && \ mkdir /var/run/php-fpm && \ docker-php-ext-install intl pdo_mysql zip bcmath && \ composer config -g process-timeout 3600 && \ composer config -g repos.packagist composer https://packagist.org COPY ./docker/php/php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf COPY ./docker/php/php.ini /usr/local/etc/php/php.ini WORKDIR /work/backend |
php.ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [Date] #タイムゾーンの設定 date.timezone = "Asia/Tokyo" [mbstring] #文字コードの設定 ;mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese" [opcache] #phpの動作に関する設定(PHP公式おすすめ設定) opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 |
zzz-www.conf
1 2 3 4 | listen = /var/run/php-fpm/php-fpm.sock listen.owner = www-data listen.group = www-data listen.mode = 0666 |
nginxコンテナ
Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | FROM node:16-alpine as node FROM nginx:1.20-alpine SHELL ["/bin/ash", "-oeux", "pipefail", "-c"] ENV TZ=UTC RUN apk update && \ apk add --update --no-cache --virtual=.build-dependencies g++ # node command COPY --from=node /usr/local/bin /usr/local/bin # npm command COPY --from=node /usr/local/lib /usr/local/lib # yarn command COPY --from=node /opt /opt # nginx config file COPY ./docker/nginx/*.conf /etc/nginx/conf.d/ WORKDIR /work/backend |
default.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | access_log /dev/stdout main; error_log /dev/stderr warn; server { listen 80; server_name localhost; charset utf-8; location / { proxy_pass http://host.docker.internal:3000/; proxy_redirect off; } } server { listen 9000; server_name localhost; root /work/backend/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.html index.htm index.php; 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 unix:/var/run/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } |
リバースプロキシの設定は主にこのファイルで行っています。localhost:80にアクセスするとlocalhost:3000にリバースプロキシされるようになっております。
1 2 3 4 | location / { proxy_pass http://host.docker.internal:3000/; proxy_redirect off; } |
Nuxtコンテナ
Dockerfile
1 2 3 4 5 6 7 8 9 10 | FROM node:18-slim ENV TZ Asia/Tokyo WORKDIR /var/www/nuxt RUN apt-get update \ && apt-get install -y \ git \ vim |
Laravelのインストール
1 2 3 4 5 6 7 8 | cd filament-post-project mkdir -p backend docker compose build --no-cache --force-rm docker compose up -d docker compose exec app composer create-project --prefer-dist laravel/laravel . docker compose exec app php artisan key:generate docker compose exec app php artisan storage:link docker compose exec app chmod -R 777 storage bootstrap/cache |
Laravelのインストールが完了したらlocalhost:9000にアクセスしてwelcomeページが表示されることを確認します。
LaravelからDBコンテナに接続するには、.envファイル(Laravelで使う環境変数を定義しているファイル)の設定を変えます。
1 2 3 4 5 6 | DB_CONNECTION=mysql DB_HOST=filament-post-project_db DB_PORT=3306 DB_DATABASE=database DB_USERNAME=db-user DB_PASSWORD=db-pass |
すべてのMySQLのテーブルを削除後にマイグレーション&seed
1 | docker compose exec app php artisan migrate:fresh --seed |
Nuxtのインストール
1 2 3 4 | docker compose exec front bash npx nuxi init nuxt-project # nuxt-project の部分にはアプリの名前(任意)を入れます cd nuxt-project yarn dev # サーバーの立ち上げ |
いよいよ画面構築
今回はPostsというテスト用のテーブルを作り、ブログ記事のように入稿できるシステムを構築していきます。
filamentのインストール
FilamentはTALL技術を基盤とするLaravelの拡張パッケージの1つで、簡単に、ほぼノーコードでSPAライクな管理画面を実装することが可能です。
詳しくFilament Docから
1 2 3 4 5 | docker compose exec app bash composer require filament/filament:"^3.2" -W php artisan filament:install --panels |
ユーザーを作成する
コマンドを使用して、プロジェクトの新しいユーザーアカウントを作成します。
1 | php artisan make:filament-user |
名前とメールアドレス、パスワードを入力してユーザーを作成したら、ブラウザでlocalhost:9000/adminを開くと、ログイン画面が表示されるはずです。
先ほど作成したアカウントでサインインできます。
マイグレーションファイルの作成
1 | php artisan make:migration create_posts_table |
上記コマンドでapp/database/migrationsにマイグレーションファイル「【日付】_create_posts_table.php」が作成されるので以下のように編集します。
1 2 3 4 5 6 7 8 9 10 11 12 | public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('body')->nullable(); $table->integer('type')->comment('0:blog 1:Tips 2:Other'); $table->dateTime('published')->nullable(); $table->integer('delete_flg')->default(0)->comment('削除フラグ'); $table->timestamps(); }); } |
1 | php artisan migrate |
maigrateコマンドを実行して、MySQLデータベースにテーブルを作っておきます。
テーブルが出来たか汎用SQLクライアントツール【A5:SQL Mk-2】を使ってDocker上のMySQLに接続して確認しましょう。
下記がA5M2のデータベース接続情報です。(docker-compose.ymlで設定した値)
dokcerが同一端末上で動いていない場合、またはmysqlコンテナ特定のIPからのみ接続可能な設定の場合、SSH2トンネルタブより設定を行い踏み台を経由してください。
モデルの作成
以下のコマンド実行してモデルを作成します。
1 | php artisan make:model Post |
「リソース」を作る
Postというテーブルを、Filamentで呼べるようにするための必要なファイル=Resource リソースを自動作成する コマンドがあり、実行します。
1 | php artisan make:filament-resource Post |
下記のようにapp/Filament/Reosources以下に
1 2 3 4 5 6 7 | . +-- PostResource.php +-- PostResource | +-- Pages | | +-- CreatePost.php | | +-- EditPost.php | | +-- ListPost.php |
管理画面によくある構成で、対応するファイルが自動で生成されます。
- 作成画面
- 編集画面
- 一覧画面
localhost:9000/adminをブラウザで確認すると、Postsという画面ができています。
【New Post】というボタンを押すとCreate Postページに遷移しますが、ボタンとタイトル以外何も表示されていないのでここに登録内容を設定していきます。
作成画面にパーツを追加
PatientResource.phpを開き、Formメソッドの空の配列schemaに必要な項目を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public static function form(Form $form): Form { return $form ->schema([ # タイトル 通常の入力欄 Forms\Components\TextInput::make('title') ->required() ->columnSpan(2) ->maxLength(255), # 内容 リッチテキスト Forms\Components\RichEditor::make('body') ->columnSpan(2) ->maxLength(65535), # 記事タイプ select box Forms\Components\Select::make('type') ->options([ '0' => '通常', '1' => 'Tips', '2' => 'その他' ]), # 投稿日時 入力なしで下書きとすることもできるが、入力した場合には現在以降を指定しないとエラー Forms\Components\DateTimePicker::make('published') ->minDate(now()), ]); } |
追記して、ブラウザをリロードすると追加した項目が表示されます。
必要項目を入力するとレコードが無事できましたが一覧ページにタイトルなど必要な表示がまだありませんので一覧画面に表示項目を追加します。
一覧画面に表示項目を追加
PostResource.phpのtable()メソッドを以下のように変えて、表示ができるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('title'), Tables\Columns\TextColumn::make('published'), ]) ->filters([ // 下記のようにすると、投稿済みのもののみ絞り込める //Tables\Filters\Filter::make('published') // ->query(fn (Builder $query): Builder => $query->whereNotNull('published')), ]) ->actions([ // 編集画面へのリンクなど、単発レコードへのリンクが出せる Tables\Actions\EditAction::make(), ]) ->bulkActions([ // 一斉削除など、まとめてアクションを行える Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]); } |
一覧ページを更新して確認してみると登録したPostがリスト表示されることが確認できました。
Nuxt(フロント)からLaravel APIをたたいてみる
LaravelでAPIを作成する
backend側(laravel)のcorsの設定をします。以下のようにcors.phpを修正し、環境変数から接続を許可するURLを指定します。(今回であればHOST_URL=http://localhost:3000)
1 | 'allowed_origins' => [env('HOST_URL')], |
次にAPI用のルーティングを作成
1 2 3 4 5 | Route::get('/check', function (){ return response()->json([ 'message'=>'hello world.' ]); }); |
以上でLaravel側での作業は完了です。最後にlocalhost:9000/api/checkへアクセスしてjsonが取得できているかを確認します。
NuxtからAPIに接続
nuxtのapp.vueファイルを下記のように書き換えてください。
1 2 3 4 5 6 7 8 9 | <template> <div> <p>{{data}}</p> </div> </template> <script setup> const {data} = await useFetch('http://filament-post-project_nginx:9000/api/check'); </script> |
Nuxt3にはuseFetchという便利な機能があります。これは主に外部APIとの接続と非同期処理を行うためのものです。
注意点
- useFetchでURLを指定する際は、localhostではなく、コンテナの名前解決のためにcontainer_nameを指定する必要があります。
- Laravelはnginxを介してアクセスするため、APIアクセス時もLaravelコンテナではなく、nginxコンテナにアクセスする必要があります。
localhost:80にアクセスしてjsonが取得できていれば、無事完了となります
メモ
・Form BuilderのRichEditorで画像保存や、File uploadを使うときは.envのAPP_URLを正しく設定(この環境ではhttp://localhost:9000)