Contents
XserverにそのままアップできるLaravelプロジェクトをローカルで開発する
Xserver上では原則「composer は使えない」
※Xserverは共用サーバー。つまり、root権限はないし、OSレベルで自由にツールをインストールすることもできない。
→ローカルで composer install を済ませて vendor/ をUPする、npm run buildも同様
そもそも開発環境について整理すると
composer(PHPのパッケージマネージャー)、npm(Node.jsのpm)を2つ使用します!
Laravel + Vite構成で開発してる場合、開発サーバーは実質2つになりますね
項目 | 内容 |
---|---|
ComposerはXserverで使える? | ❌ ローカルで完了させて vendor/ をアップ |
npm run build は必要? | ✅ public/build/ に出力してアップ必須 |
artisanコマンドはXserverで? | ❌ 基本ローカルで済ませておく |
public_html/ の中身は? | index.php , .htaccess , build/ , css/ , etc |
Laravel本体はどこに? | public_html/ の外に laravel_project/ として設置 |
Dockerコンテナ作成手順(Sailでない)
Laravel Sailは、LaravelをDockerで簡単に構築できるツールで便利ですが、今回はローカルでのみcomposerを使用するので、本番でcomposerコマンドを使用しない想定なので使用しません、、
docker-compose.yml
laravel-xserver-dev/
├── docker/
│ ├── php/
│ │ └── Dockerfile
│ └── mysql/
├── docker-compose.yml
├── .env
└── laravel/ ← Laravel本体(後で Composer で生成)
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
コンテナ内でLaravelがHTTPサーバーを立てる
→ホスト側(ブラウザ)から http://localhost:8000
でアクセス可能になる。
docker compose exec app php artisan serve --host=0.0.0.0 --port=8000
Laravelの開発サーバはデフォルトで 127.0.0.1:8000(localhost)(ループバック)で起動ます
そしてコンテナ内部のネットワークのため
php artisan serveのみだとホストからアクセスできず、「–host=0.0.0.0 –port=8000」が必要です
(全てのインターフェースでListen→ポートをマッピング)
※Laravelは、起動時に環境変数を読み込んで設定クラスにバインドします
どの.env?
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret
.env のキー | 反映される場所 |
---|---|
APP_ENV | config/app.php の 'env' => env('APP_ENV') |
APP_DEBUG | config/app.php の 'debug' => env('APP_DEBUG') |
DB_* | config/database.php で使用される |
MAIL_* | config/mail.php で使用される |
マウントの設定を修正し、スピードパフォーマンスを改善
原因はvendorとstorageがDocker環境でファイルI/O(Input/Output)が極端に遅くなるから
Laravelは、リクエストごとに以下を行う:
- vendor/autoload.php からオートロード
- .env を読んで構成
- storage/logs/ にログ書き込み
- cache/, sessions/, views/ を操作
つまり、毎回100回以上ディスクアクセスしてる。
そのたびに「ホストマシンとコンテナ間のファイルシステム同期」が発生してたら、そりゃ爆遅に
DockerでLaravelが重すぎたのでvendorとstorageをマウントしないことで解決した気がする
https://qiita.com/ryo_one/items/cced7c7b6e21527ad81e
名前付きボリューム初期化手順
毎回同期をしない名前付きボリュームを導入すればOK
「バインドマウント」と「ボリュームマウント」については下記の記事参考に
Dockerのマウントについて
https://zenn.dev/randd_inc/articles/84ac7de7f22800
docker-compose.ymlに名前付きボリュームの設定を追記
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: laravel-app-fast
volumes:
- ./laravel:/var/www
- storage-volume:/var/www/html/storage/framework
- vendor-volume:/var/www/html/vendor
ports:
- "8000:8000"
...
volumes:
dbdata:
storage-volume:
vendor-volume:
コンテナとVolumeを含めて完全再起動(vendor-volがマウントされる)
docker-compose down -v
docker-compose up -d
vendorが空になるので、Volume上に再構築
docker-compose exec app composer install
INFO Discovering packages.
laravel/pail ................................................................................................................... DONE
laravel/sail ................................................................................................................... DONE
laravel/tinker ................................................................................................................. DONE
nesbot/carbon .................................................................................................................. DONE
nunomaduro/collision ........................................................................................................... DONE
nunomaduro/termwind ............................................................................................................ DONE
79 packages you are using are looking for funding.
Use the composer fund command to find out more!
# Laravelのキャッシュ・権限系を整える
docker-compose exec app php artisan config:clear
docker-compose exec app chmod -R 777 storage bootstrap/cache
# アクセス確認(localhost:8000)
docker-compose exec app php artisan serve --host=0.0.0.0 --port=8000
名前付きボリュームが正しく設定されているか確認
docker-compose exec app ls /var/www/html/
storage vendor
docker-compose exec app mount | grep /var/www/html/vendor
/dev/sde on /var/www/html/vendor type ext4 (rw,relatime)
/dev/sde はDockerの内部(ext4)のボリューム領域です、つまりホストにマウント(同期)していないことが分かります
Named Volumeは、Dockerが管理する永続的なデータ保存領域
項目 | Bind Mount | Named Volume |
---|---|---|
書き方 | ./laravel:/var/www | vendor-volume:/var/www/vendor |
保存場所 | ホストのフォルダ | Docker内部領域 |
速度 | 遅い(特にWindows/Mac) | 高速 |
ホストからアクセス | 可能 | 不可 |
永続性 | ホストファイルに依存 | コンテナ削除後も残る |
リアルタイム同期 | あり | なし |
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 にそのままコピー
環境変数を編集してデプロイ
ローカルと本番のDBは「違って当然」、だからマイグレーションで定義する。
XserverはMySQLをサービスとして提供している
┌─────────────┐ ┌────────────┐
│ ローカルPC │ │ Xserver │
│ Docker(MySQL)│ │ MySQL提供 │
│ Laravel dev │ │ Laravel prod │
└────┬────────┘ └────┬───────┘
│ DB接続 → localhost │ DB接続 → mysql1234.xserver.jp
│ │
│ migrate │ 手動インポート(phpMyAdmin)
▼ ▼
OK OK
内容 | ローカル | 本番 |
---|---|---|
DBサーバー | Dockerで構築したMySQL | XserverのMySQL(管理画面で作成) |
接続情報 | .env に DB_HOST=127.0.0.1 | .env に DB_HOST=mysql1234.xserver.jp |
DB中身(データ) | 開発用のサンプル/テストデータ | 本番の実データ |
DB構造(テーブル定義) | Laravelのマイグレーションで定義する | ← 同じマイグレーションで生成される |
構造(スキーマ)をマイグレーションで共通化し、
中身(データ)は環境に応じて分ける
https://github.com/idw-coder/laravel-xserver
XserverでMySQLデータベースのデータ構造を反映する手段
- SSH接続でphp artisan migrate
- phpMyAdminでインポート
phpMyAdminでインポート方法
レンタルサーバーではDB接続はSSHでなくphpMyAdminからインポートはあり
むしろXserverのような共有ホスティングでは、SSHやCLIよりphpMyAdminの方が標準手段。
ローカルMySQLコンテナから .sql ファイルをエクスポートする
ローカルDBをダンプ
docker exec [DBコンテナ名] mysqldump -u root -p laravel > /tmp/laravel.sql
コンテナからホストにコピー
docker cp laravel-db:/tmp/backup.sql ./backup.sql
xserverレンタルサーバーコントロールパネル画面から「phpMyAdmin」を開き上記で生成したファイルをインポート
レンタルサーバーにssh接続してphp artisan migrate
client_loop: send disconnect: Connection reset
PS C:\Users\user\.ssh> ssh server
Last login: Wed Jun 25 06:38:32 2025 from 192.168.1.100
[user@server ~]$ cd mysite.com/public_html/laravel-app/
[user@server laravel-app]$ php8.3 artisan migrate
APPLICATION IN PRODUCTION.
Are you sure you want to run this command?
Yes
INFO
Running migrations.
2025_06_24_093122_add_qualification_and_role_to_users_table
.....................................................................
100.27ms
DONE
laravel Breeze
前提条件
- Dockerでローカル開発し、xserver(レンタルサーバー)にデプロイしてます
- composer が使える(Xserver本番環境は使えない、ローカルでやる)
- npm が使える(ローカルでフロントビルド用)
- .env に DB 接続ができている(マイグレーションで users テーブルが作られる)
Breeze インストール
Laravel Breezeパッケージがプロジェクトの開発依存関係(dev-dependencies)としてインストールされます
composer require laravel/breeze --dev
インストール後は以下のコマンドを実行して認証システムをセットアップします
php artisan breeze:install
● 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やモバイル用) |
今度はテストフレームワークの選択
● Pestを選択
項目 | Pest | PHPUnit |
---|---|---|
記述の簡潔さ | ✅ 圧倒的に短くて読みやすい | ❌ 冗長なクラス宣言が必須 |
学習コスト | ✅ 初心者でも感覚的に書ける | ⛔ 「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
現状の整理
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
laravel\app\Http\Requests\Auth\LoginRequest.php
メールアドレスではなくIDで認証できるように変更(Breeze)
ログイン機能をadmin_idを使用するように変更し、関連するバリデーションとエラーメッセージを更新。ユーザーモデルにadmin_idでユーザーを検索するメソッドを追加。ユーザー一覧ビューを改善し、テーブルにadmin_idを表示。ログインフォームのラベルと入力フィールドを更新。
https://github.com/idw-coder/laravel-xserver/commit/e00b58546117998e2cf6096fc89fb55817d54866
ユーザー新規作成をログインしている特定の権限のユーザーのみに公開
npm run buildでフロントのビルド後xserverのサブディレクトリにデプロイしたら表示崩れ、、
原因①「Laravelアプリを本番でサブディレクトリ配下にデプロイする」とローカルとディレクトリ構成がズレる→パスがおかしくなる
結局、臨時処置ですが、public配下のbuildディレクトリをコピーし、publicディレクトリと同配下に複製しました。
具体的には私はxserverのサブディレクトリにデプロイしたのですが、以下の通りです
local
/project-dir/
┣ docker
┣ laravel
┃ ┣ public
┃ ┣ build
┃ ┃ ┣ assets
┃ ┃ ┃ ┣ app-xx.js ※デプロイしたら移動
┃ ┃ ┃ ┣ app-xx.css ※デプロイしたら移動
┃ ┃ ┗ manifest.json
┃ ┃
┃ ┣ js
xserver
/your-domain.com/public_html/laravel-dir/
┣ public
┃ ┣ js
┃ ┣ build
┃ ┃ ┗ manifest.json
┃
┣ build ※public配下とは別で作成
┃ ┗ assets ※public配下から移動
┃ ┣ app-xx.js
┃ ┗ app-xx.css
┃
表示はくずれなくなりました。
原因②Laravel はまだ「開発モード」と判断した状態でviteでbuildしてしまった場合
<script type="module" src="http://[::1]:5173/@vite/client"></script>
<link rel="stylesheet" href="http://[::1]:5173/resources/css/app.css">
<script type="module" src="http://[::1]:5173/resources/js/app.js">
上記のように検証画面でソースコードみると、Vite Dev Server(http://[::1]:5173)を使おうとしているパスのままであればVite の開発モードの挙動です
.env(ローカルでビルドしてるからローカルの.env)を修正したらどうだろう、、
# APP_DEBUG=true
APP_DEBUG=false
APP_URL=http://localhost:8000 # @vite() とは無関係
# APP_ENV=local
APP_ENV=production
でもかわらず、、
npm run build で埋め込まれる URL(例:http://[::1]:5173)は、Laravel の .env じゃなくて Vite 側の環境変数定義から来てる。
つまり
✅ APP_ENV=production
とか Laravel 側にいくら書いても、
❌ Vite 側には 伝わらない。
そもそも@vite()の仕様を確認すると(下記resources\views\welcome.blade.php)
<!-- Styles / Scripts -->
@if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot')))
@vite(['resources/css/app.css', 'resources/js/app.js'])
@else
アセットバンドル(Vite)
https://laravel.com/docs/12.x/vite
Laravel Viteでフロントと管理画面のビルドを分ける方法
https://www.webopixel.net/php/1796.html
そもそもローカルでnpm run build → FTPでデプロイがまずいのか、、本番でbuildしないといけないのか、、
あきらめかけていたとこに原因判明「hot ファイル」
🔥 hot ファイルとは?
npm run dev(開発モード)を実行したときに public/hot が作成されます。
Laravel は public/hot が存在すると manifest.json を無視して localhost:5173 を参照します。
$ ls -l public
合計 12
drwx---r-x 3 jingtian members 53 6月 13 06:27 build
-rw----r-- 1 jingtian members 0 5月 27 02:17 favicon.ico
-rw----r-- 1 jingtian members 17 6月 21 08:42 hot
-rw----r-- 1 jingtian members 543 5月 27 02:17 index.php
drwx---r-x 3 jingtian members 29 6月 18 21:21 js
-rw----r-- 1 jingtian members 24 5月 27 02:17 robots.txt
drwx---r-x 2 jingtian members 290 6月 25 06:27 uploads
rm public/hot
ユーザー一覧画面を作成(練習)
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.php | components/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
- 必要なクラスを読み込み
>>> use App\Models\{User, Post};
>>> use App\Enums\PostStatus;
- ユーザーのIDを確認
>>> User::count()
>>> $user = User::first();
>>> $user
- 投稿データを作成
>>> Post::create([
... 'user_id' => $user->id,
... 'title' => 'はじめての投稿',
... 'slug' => 'first-post',
... 'body' => 'これは最初のテスト投稿です。',
... 'status' => PostStatus::DRAFT
... ]);
- 作成されたか確認
>>> 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でも確認できました。
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エディタの具体的な製品・ライブラリの名前」
TinyMCE 無料 (Cloud) プランの制限
無料枠では月間 1,000 エディタ読み込み(editor load)まで。それを超えるとエディタが読み込み専用モードになり、閲覧はできても編集不可になる
100ユーザーが各10ページでTinyMCEを起動すれば、100 × 10 = 1000
方法 | 概要 |
---|---|
Self-hosted | CDN 経由ではなく、自前サーバや public/static に TinyMCE スクリプトを置けば、読み込み回数に制限なし(tiny.cloud)。 |
有料プランへ移行 | 5,000 loads/月の Essential ($79/月)、20,000 loads/月の Professional などから選べる(超過分も課金)。 |
動作確認手順
npm install fs-extra
npm run dev
(またはnpm run build
)public/js/tinymce/
にコピーされているか確認