Laravel、Next.jsでWeb回覧システム設計、Docker Sail Laravel 12 TinyMCE

ブログ・Web回覧サイト構築の要点まとめ

技術選択:

  • Laravel:管理画面重視、複数人管理向け
  • Next.js:デザイン・高速表示重視、個人向け

管理画面の差:

  • Laravel:Filament等のパッケージで簡単構築
  • Next.js:自作が必要で工数2-3倍

構成・費用:

  • Laravel:VPS + MySQL + S3 = 月1,500円程度
  • Next.js:Vercel + Supabase = 小規模なら無料
  • 最小構成:VPS + SQLite = 月500円

開発工数:

  • 管理画面含めて2-3ヶ月(最小構成なら1-2ヶ月)

結論: 管理のしやすさ重視 → Laravel コスト・モダンUI重視 → Next.js

プロジェクト流れ

  1. 開発環境
  2. 要件洗い出し
  3. DBスキーマ
  4. コーディング
  5. テスト

Laravel + Docker(Sail)構築手順 – Windows版

前提確認

環境状態確認方法
Docker Desktop起動してる?右下のクジラアイコン
WSL2(推奨)wsl -l -v で確認(Ubuntuなど)
Windows Terminal / CMDどちらでも可
Git Bash(推奨)Bashの方がLinuxコマンド使える

構築手順ステップ

composer create-project laravel/laravel web-circular
  • Laravel Sailというパッケージを開発環境用の依存関係としてインストール
composer require laravel/sail --dev

–devオプションについて

composer.json の構造 composer.json “require”: { “laravel/framework”: “^10.0”, “guzzlehttp/guzzle”: “^7.0” } “require-dev”: { “laravel/sail”: “^1.0”, “phpunit/phpunit”: “^10.0”, “laravel/pint”: “^1.0” } 本番環境でも必要 (アプリ動作に必須) 開発時のみ必要 (テスト、デバッグ用) インストール時の動作: 開発環境 composer install → 全てのパッケージインストール 本番環境(レンタルサーバー) composer install –no-dev → require のみインストール(require-dev は無視)
  • Sailを導入
php artisan sail:install

  Which services would you like to install? [mysql]
  None ............................................................................................................. 0  
  mysql ............................................................................................................ 1  
  pgsql ............................................................................................................ 2  
  mariadb .......................................................................................................... 3  
  mongodb .......................................................................................................... 4  
  redis ............................................................................................................ 5  
  valkey ........................................................................................................... 6  
  memcached ........................................................................................................ 7  
  meilisearch ...................................................................................................... 8  
  typesense ........................................................................................................ 9  
  minio ........................................................................................................... 10  
  mailpit ......................................................................................................... 11  
  rabbitmq ........................................................................................................ 12  
  selenium ........................................................................................................ 13  
  soketi .......................................................................................................... 14  
❯ 1

選択肢リスト、Laravel Sail の「どのサービス(コンテナ)を含めるか」を選べるようです。

無駄なサービスを入れすぎるとDockerコンテナが激重に

表示されるリストから Ubuntu (WSL) を選ぶ

  • SailコマンドでDockerコンテナ起動
    1. Docker Desktopを起動
    2. Ubuntu(WSL)に切り替える
      VSCodeターミナル右上の + 横の 下矢印ボタン ⏷ をクリック

sailコマンドはUbuntuでしか使えない(≒ Linux環境(bash)じゃないと動かない)

/mnt/c/web-circular# vendor/bin/sail up -d

Laravel Sail の正体は、Laravel開発用にチューニングされたDockerラッパースクリプト

処理内容自動化されてる?実際の挙動
Dockerイメージの取得laravelsail/php82-composer とか mysql:8 をPull
docker-compose.yml生成sail:install 実行時に .dockerdocker-compose.yml を作成
コンテナ起動sail up -d で Laravel(PHP)と MySQL の2コンテナが起動
ネットワーク/ボリューム作成LaravelとDBが同一ネットワークで通信できるように設定

先ほどのコンテナ起動でMySQLも一緒に起動されてます

./vendor/bin/sail up Laravel (PHP) + MySQL (DB) + その他 (Redis等) 1つのコマンドで複数のコンテナが同時起動
  • データベースが生きてるか確認
vendor/bin/sail artisan migrate

   INFO  Preparing database.  

  Creating migration table ...................................................... 308.69ms DONE

   INFO  Running migrations.  

  0001_01_01_000000_create_users_table .......................................... 586.27ms DONE
  0001_01_01_000001_create_cache_table .......................................... 171.50ms DONE
  0001_01_01_000002_create_jobs_table ........................................... 400.70ms DONE

※Laravelの初期DBスキーマ(テーブル)をMySQLコンテナ上に作成

Laravelのマイグレーションは「データベースのバージョン管理システム」Gitみたいなもの

以上でブラウザでウェルカムページが確認できます

Laravel Let’s get started Laravel has an incredibly rich ecosystem. We suggest starting with the following. Read the Documentation → Watch video tutorials at Laracasts → Deploy now http://localhost

xserverのような共用サーバーではvendor/bin/sail artisan~のようなsailコマンドが使用できない??ので下記の方法がいいかも

XserverにそのままアップできるLaravelプロジェクトをローカルで開発する

Xserver上では原則「composer は使えない」

※Xserverは共用サーバー。つまり、root権限はないし、OSレベルで自由にツールをインストールすることもできない。

→ローカルで composer install を済ませて vendor/ をUPする、npm run buildも

つまりLaravel + Vite構成で開発してる場合、開発サーバーは実質2つ

(開発中はnpm run devでTailwind CSSとか見た目の確認はできます)

項目内容
ComposerはXserverで使える?❌ ローカルで完了させて vendor/ をアップ
npm run build は必要?public/build/ に出力してアップ必須
artisanコマンドはXserverで?❌ 基本ローカルで済ませておく
public_html/ の中身は?index.php, .htaccess, build/, css/, etc
Laravel本体はどこに?public_html/ の外に laravel_project/ として設置
laravel-xserver-dev/
├── docker/
│   ├── php/
│   │   └── Dockerfile
│   └── mysql/
├── docker-compose.yml
├── .env
└── laravel/        ← Laravel本体(後で Composer で生成)

docker-compose.yml

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: laravel-app
    volumes:
      - ./laravel:/var/www
    ports:
      - "8000:8000"
    depends_on:
      - db
    working_dir: /var/www

  db:
    image: mysql:8.0
    container_name: laravel-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
    ports:
      - "3306:3306"
    volumes:
      - dbdata:/var/lib/mysql

volumes:
  dbdata:

Dockerfile

FROM php:8.2-fpm

RUN apt-get update && apt-get install -y \
    git curl zip unzip libonig-dev libxml2-dev \
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath

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

WORKDIR /var/www

Laravelをインストール(コンテナ内で)

docker compose up -d
docker compose exec app bash
# コンテナ内で以下を実行
composer create-project laravel/laravel .
exit

結構時間かかります

タイムアウトしてしまったらcomposer config –global process-timeout 900

どの.env?

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
.env のキー反映される場所
APP_ENVconfig/app.php'env' => env('APP_ENV')
APP_DEBUGconfig/app.php'debug' => env('APP_DEBUG')
DB_*config/database.php で使用される
MAIL_*config/mail.php で使用される

※Laravelは、起動時に環境変数を読み込んで設定クラスにバインドします

コンテナ内でLaravelがHTTPサーバーを立てる
→ホスト側(ブラウザ)から http://localhost:8000 でアクセス可能になる。

docker compose exec app php artisan serve --host=0.0.0.0 --port=8000

FTPでxservernのサブディレクトリにデプロイする場合、本番サーバーでもローカルと同じ構成にするのが、ベストプラクティス

vendor/ は 1万ファイル以上あるからFTPクライアント(WinSCPやFileZilla)でアップするのに時間かかる。焦るな。

Xserver/public_html/
└── laravel-xserver/
    ├── app/
    ├── public/(中身あり)
    ├── .env
    ├── index.php      ← public/index.phpを書換コピペ
    ├── .htaccess      ← public/.htaccessをコピー
    └── ... その他Laravel一式
  • public/index.php → laravel-xserver/index.php にコピーして、以下のように修正:
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
  • public/.htaccess → laravel-xserver/.htaccess にそのままコピー

環境変数を編集してデプロイ

XserverはMySQLをサービスとして提供している

┌─────────────┐           ┌────────────┐
│ ローカルPC  │           │   Xserver  │
│ Docker(MySQL)│          │  MySQL提供  │
│ Laravel dev │           │ Laravel prod │
└────┬────────┘           └────┬───────┘
     │ DB接続 → localhost       │ DB接続 → mysql1234.xserver.jp
     │                         │
     │ migrate                 │ 手動インポート(phpMyAdmin)
     ▼                         ▼
  OK                          OK

ローカルと本番のDBは「違って当然」、だからマイグレーションで定義する。

内容ローカル本番
DBサーバーDockerで構築したMySQLXserverのMySQL(管理画面で作成)
接続情報.envDB_HOST=127.0.0.1.envDB_HOST=mysql1234.xserver.jp
DB中身(データ)開発用のサンプル/テストデータ本番の実データ
DB構造(テーブル定義)Laravelのマイグレーションで定義する← 同じマイグレーションで生成される

つまり、構造(スキーマ)をマイグレーションで共通化し、
中身(データ)は環境に応じて分けるのが基本。

分類Laravelディレクトリ本番とローカルの差管理方法
コード(PHP)app/, routes/なし(完全共通)Gitで管理
DB構造(テーブル)database/migrationsなし(マイグレーションで統一)Git + artisan migrate
DBデータ(中身)database/seedersあり(本番≠開発)Seeder or 手動
設定.envあり(DB, APP_URL等).env / .env.production
静的ファイルpublic/build/あり(npm run build)buildしてUP

https://github.com/idw-coder/laravel-xserver

XserverでMySQLデータベースを作成する手順

https://www.xserver.ne.jp/login_server.php

「MySQL設定」をクリック

レンタルサーバーではDB接続はSSHでなくphpMyAdminからインポートは「あり」

むしろXserverのような共有ホスティングでは、SSHやCLIよりphpMyAdminの方が標準手段。

ローカルDBをダンプ

mysql> show tables;
Empty set

空でしたのでマイグレーション

docker-compose exec app php artisan migrate

まず、ローカルMySQLコンテナから .sql ファイルをエクスポートする:

docker exec [DBコンテナ名] mysqldump -u root -p laravel > laravel.sql

laravel Breeze

前提条件

  • Dockerでローカル開発し、xserver(レンタルサーバー)にデプロイしてます
  • composer が使える(Xserver本番環境は使えない、ローカルでやる)
  • npm が使える(ローカルでフロントビルド用)
  • .env に DB 接続ができている(マイグレーションで users テーブルが作られる)

Breeze インストール

Laravel Breezeパッケージがプロジェクトの開発依存関係(dev-dependencies)としてインストールされます

composer require laravel/breeze --dev

インストール後は以下のコマンドを実行して認証システムをセットアップします

php artisan breeze:install
root@dc586cadb708:/var/www# php artisan breeze:install Which Breeze stack would you like to install? ● Blade with Alpine ○ Livewire (Volt Class API) with Alpine ○ Livewire (Volt Functional API) with Alpine ○ React with Inertia ○ Vue with Inertia ○ API only

● Blade with Alpineを選択

選択肢内容
Blade with Alpine最もシンプルな構成。Laravel標準のBladeテンプレート + Alpine.js(軽量JS)
Livewire(Volt API)フロントのリアクティブ処理をPHPだけで書ける魔改造スタック。SPA風味
React/Vue with InertiaガチのSPA構成。React/VueとLaravelの融合
API only認証付きAPI構成(SPAやモバイル用)
Which testing framework do you prefer? ● Pest ○ PHPUnit

今度はテストフレームワークの選択

● Pestを選択

項目PestPHPUnit
記述の簡潔さ✅ 圧倒的に短くて読みやすい❌ 冗長なクラス宣言が必須
学習コスト✅ 初心者でも感覚的に書ける⛔ 「setUp()」だの「TestCase」だの面倒
Laravel公式推し✅ Breezeのデフォルト選択✅ 同じくサポートされてるが古い

npmビルドはホスト側で(Dockerコンテナ内ではなく)

Node.js入りDockerは構成が複雑になりすぎる

Breezeは「npmビルドさえ済めば」それでOK

cd \laravel-xserver-dev\laravel
npm install --save-dev vite
npm run build

xserver デプロイ用にパスを修正する

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,
        }),
    ],
    base: '/laravel-xserver/build/', // xserverデプロイ時の構成に合わせて設定
});

今度はDockerコンテナ内で

php artisan migrate

http://localhost:8000/registerにアクセスで認証画面の確認OK

Name Email Password Confirm Password Already registered? REGISTER

現状の整理

laravel/
├── app/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── Auth/         ← Breezeの認証系コントローラ
│   │   │   └── Controller.php
├── database/
│   ├── migrations/           ← users, sessionsなどが定義されてる
├── resources/
│   ├── views/
│   │   ├── auth/             ← login, registerなど
│   │   ├── layouts/          ← app.blade.php 等のレイアウト
├── routes/
│   ├── web.php               ← Breezeが登録ルート追加済
├── public/
│   ├── build/                ← npm run build で生成されたフロントJS

npm run buildでフロントのビルド後xserverのサブディレクトリにデプロイしたら表示崩れ、、

原因
「Laravelアプリを本番でサブディレクトリ配下にデプロイする」とローカルとディレクトリ構成がズレる→パスがおかしくなる

正しい設計は?

そもそもサブディレクトリでの運用がよろしくない?

下記のファイルで調節してうまいことする

vite.config.js

export default defineConfig({
    plugins: [laravel({ input: [...], refresh: true })],
    base: process.env.VITE_ASSET_BASE || '/build/',
});

.env.production

APP_URL=https://bizlabo.site/laravel-xserver
VITE_ASSET_BASE=/laravel-xserver/build/

.env(ローカル)ローカルでビルドする想定なので

APP_URL=http://localhost:8000
VITE_ASSET_BASE=/build/

結局、臨時処置ですが、public配下のbuildディレクトリをコピーし、publicディレクトリと同配下に複製しました。

表示はくずれなくなりました。

ユーザー一覧画面を作成(練習)

https://github.com/idw-coder/laravel-xserver/blob/dev_breeze/laravel/resources/views/users/index.blade.php

Undefined variable $slot

上記エラー発生

{{ $slot }} を使おうとしているにも関わらず、その変数が定義されていないことが原因

こちらおそらくlaravel Breezeをインストールした際、自動生成された下記コンポーネント

<!-- resources/views/components/app-layout.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>{{ config('app.name', 'Laravel') }}</title>
    <!-- CSS読み込み -->
</head>
<body>
    <div class="min-h-screen bg-gray-100">
        <!-- ナビゲーション -->
        @include('layouts.navigation')
        
        <!-- ページヘッダー -->
        @if (isset($header))
            <header class="bg-white shadow">
                <div class="max-w-7xl mx-auto py-6 px-4">
                    {{ $header }} <!-- 名前付きスロット -->
                </div>
            </header>
        @endif
        
        <!-- メインコンテンツ -->
        <main>
            {{ $slot }} <!-- メインのコンテンツエリア -->
        </main>
    </div>
</body>
</html>

上記に合わせて形でlaravel/resources/views/users/index.blade.phpを修正でエラー解消

そもそも$slotってなに?

$slot:コンポーネントタグの開始タグと終了タグの間に書かれた内容を受け取る変数

項目従来の方法Bladeコンポーネント
主な機能@yield / @section$slot
ファイル名layouts/app.blade.phpcomponents/app-layout.blade.php
使用方法@extends(‘layouts.app’)<x-app-layout>
内容の渡し方@section(‘content’)タグの中に直接記述
導入時期Laravel初期からLaravel 7以降

Breezeをインストールすると、新しい方式のBladeコンポーネントが使われるため、$slotを見かけるようになります。これは正常な動作で、現在のLaravelで推奨されている方法です。

posts テーブルの作成

新しいマイグレーションファイルが生成されます

php artisan make:migration create_posts_table --create=posts

Enum = Enumeration(列挙型)で管理

Enum = 選択肢を限定する仕組み

  • 従来: 文字列なので何でも入る → バグの原因
  • Enum: 決められた値だけ → 安全

つまり、「決められたルールの中でしか選べない」ようにして、プログラムをより安全にする技術です!

Laravel Enum完全ガイド

https://github.com/idw-coder/laravel-xserver/blob/add_post/laravel_enum_guide.md

マイグレーション実行

php artisan migrate:status で現在の状況を確認
php artisan migrate

Tinkerを使ってテストデータを作成

php artisan tinker
  1. 必要なクラスを読み込み
>>> use App\Models\{User, Post};
>>> use App\Enums\PostStatus;
  1. ユーザーのIDを確認
>>> User::count()
>>> $user = User::first();
>>> $user
  1. 投稿データを作成
>>> Post::create([
...   'user_id' => $user->id,
...   'title' => 'はじめての投稿',
...   'slug' => 'first-post',
...   'body' => 'これは最初のテスト投稿です。',
...   'status' => PostStatus::DRAFT
... ]);
  1. 作成されたか確認
>>> Post::count()
>>> Post::first()

本番環境にPOSTのデータ構造を反映する

xserverにssh接続してmigrateする場合

もしphp コマンドのバージョンエラーが起きたら下記記事を参考に

[username@sv12345 laravel-xserver]$ php8.3 artisan migrate

   INFO  Running migrations.  

  2025_06_14_074533_create_posts_table .............. 87.84ms DONE

php my adminでも確認できました。

表示 構造 SQL 検索 挿入 エクスポート インポート 操作 トリガ テーブルの構造 リレーションビュー # 名前 タイプ 照合順序 属性 Null デフォルト値 コメント その他 操作 1 id 🔑 bigint(20) UNSIGNED いいえ なし AUTO_INCREMENT ✏️ 変更 ❌ 削除 2 user_id 🔑 bigint(20) UNSIGNED いいえ なし ✏️ 変更 ❌ 削除 3 title varchar(255) utf8mb4_unicode_ci いいえ なし ✏️ 変更 ❌ 削除 4 slug 🔑 varchar(255) utf8mb4_unicode_ci いいえ なし ✏️ 変更 ❌ 削除 5 excerpt text utf8mb4_unicode_ci はい NULL ✏️ 変更 ❌ 削除 6 body longtext utf8mb4_unicode_ci いいえ なし ✏️ 変更 ❌ 削除 7 status 🔑 enum(‘draft’,’published’,’archived’) utf8mb4_unicode_ci いいえ draft ✏️ 変更 ❌ 削除 8 published_at 🔑 timestamp はい NULL ✏️ 変更 ❌ 削除 9 created_at timestamp はい NULL ✏️ 変更 ❌ 削除 10 updated_at timestamp はい NULL ✏️ 変更 ❌ 削除 11 deleted_at timestamp はい NULL ✏️ 変更 ❌ 削除 すべてチェックする 表示 変更 削除 ユニーク インデックス 空間 全文

User → Post (HasMany)で既存のUserテーブルと関連づけ、詳細は下記のPDFリンクから👇

https://github.com/idw-coder/laravel-xserver/blob/add_post/blog_erd.md

PostControllerを作成

php artisan make:controller PostController --resource

LaravelにWYSIWYGを実装

WYSIWYGのメリデメ

  • ユーザーにとって使いやすい(リンク、見出し、画像アップ)
  • 実装が難しい(JSライブラリ + Upload + XSS対策)
  • 要サニタイズ処理必須(XSSの温床)
  • 簡単に実装できるのはMarkdown

TinyMCE

TinyMCEは「WYSIWYGエディタの具体的な製品・ライブラリの名前」

https://www.tiny.cloud

TinyMCE 無料 (Cloud) プランの制限

無料枠では月間 1,000 エディタ読み込み(editor load)まで。それを超えるとエディタが読み込み専用モードになり、閲覧はできても編集不可になる

100ユーザーが各10ページでTinyMCEを起動すれば、100 × 10 = 1000

方法概要
Self-hostedCDN 経由ではなく、自前サーバや public/static に TinyMCE スクリプトを置けば、読み込み回数に制限なし(tiny.cloud)。
有料プランへ移行5,000 loads/月の Essential ($79/月)、20,000 loads/月の Professional などから選べる(超過分も課金)。

動作確認手順

  1. npm install fs-extra
  2. npm run dev(またはnpm run build
  3. public/js/tinymce/にコピーされているか確認