練習の一貫として、負債資源を一旦別Repositoryに切り出して基盤をLaravel6から12に一気にアップグレードしたら何が起こるんだろうという好奇心からやってみた。
環境差によって対処法が違うので、手順とかを詳細を書くことで逆にハマりを誘発しそうなので、(そのままコピーして環境が壊れてしまったら怖いので)、実際何を後悔して、どう悩んで、どういう選択をしたかっていうところを共有できたらな思います。
方針を示したりする補助的な意味合いでコードを明示することはありますが、取り扱いは慎重にお願いします。
- 1. レガシーシステムのLaravel基盤のBefore、After
- 1.1. 依存関係の化石度
- 1.1.1. プロジェクト構造の特徴
- 1.1.2. 技術的負債の山
- 2. Laravel6から12への移行戦略
- 3. Laravel6から12へのアップグレードで想定してたこと
- 3.1.1. 1. PHP 7.2 → 8.4
- 3.1.2. 2. Laravel 6世代からの大幅変更
- 4. PHP 8.4環境構築
- 4.1. Dockerfileの刷新
- 5. Docker構成の現代化
- 5.1. composer.json 大手術
- 5.2. 依存関係の刷新
- 5.2.1. 🔥 Before → After
- 5.2.2. 新規追加パッケージ
- 5.2.3. 削除パッケージ
- 6. Laravel6の構造を12で動くように変更する
- 6.1. Modelディレクトリの移行
- 6.2. シーダーディレクトリの移行
- 6.3. 設定ファイルの更新
- 6.3.1. 作業順序
- 7. Laravel12環境起動まで
- 7.1. 問題
- 7.2. MySQLの問題
- 7.3. PHPの問題
- 7.3.1. 依存関係制御
- 7.3.2. イメージの最適化
- 7.4. 公式Multi-archイメージの利点
- 7.5. Alpineの軽量メリット
- 8. Laravel12を入れたあとに諸々出た不具合
- 8.1. PSR-4準拠エラー
- 8.2. bootstrap/app.php 古すぎ問題
- 8.2.1. Before (Laravel 6)
- 8.2.2. After (Laravel 12) これに書き換え
- 9. Laravel8で、ルーティングの名前空間自動付与が廃止
- 9.1. Before (Laravel 6)
- 9.2. After(Laravel8+)
- 10. AuthenticatesUsersの廃止
- 10.1. 自前じゃなかったらどうするか
- 11. おわりに
レガシーシステムのLaravel基盤のBefore、After
項目 | 現状 | 目標 | 一言 |
---|---|---|---|
PHP | 7.2 | 8.4 | EOLなのに稼働中。怖。 |
Laravel | 6.2 | 12.19.3 | 6世代落ち。泣ける。 |
Dockerは Apache 単騎。DBも Redis もいない。
# 現状:最小限のApacheコンテナのみ
version: '3'
services:
apache:
build: ./docker/apache
ports:
- "8080:80"
こんな感じでEC2にStage環境を都度準備するという面倒な運用してた。
依存関係の化石度
{
"php": "^7.2",
"laravel/framework": "^6.2",
"fzaninotto/faker": "^1.4"
}
- 2022年くらいにPHPもLaravel6.2もEnd Of Life💀
- このFakerも別パッケージに移行済み
プロジェクト構造の特徴
- モデル数: 14個(見積・請求書系中心)
- コントローラー: 15個以上
- 独自実装: 謎コンポーネント群
- テストコード: 皆無 🚨
技術的負債の山
- 独自認証プロバイダー(このときはイケてると思ってた。なるべく標準使ったほうが良い!)
- 独自Facade実装が名前だけで地雷臭(CommonXX、, GetConfig, SessionUtil)
- 設定ファイルベース
Config::get('routes')
のルーティング(マルチテナントを最初考えてた残骸を処分してない)
Laravel6から12への移行戦略
段階的移行は捨てて一気に最新版まで持っていく。
大胆だとは思いますが、既存プロジェクトコンテナ化すればこの戦略でやれそうな気もする。
もちろんProdするまでに、ちゃんとリグレッションテストは必要ですが。
- Phase 1: 環境基盤の刷新
- 1. PHP 7.4 → 8.4へのアップグレード(Docker環境)
2. composer.jsonの全面書き換え
3. 新Laravel 12構造への適応
- Phase 2: 破壊的変更への対応
- 1. 名前空間の移行(Models、Seeders)
2. 廃止パッケージの置換(例:fzaninotto/faker → fakerphp/faker)
3. 独自実装部分の互換性確認
- Phase 3: 動作確認と調整
- 1. 基本動作の確認
2.外部連携の動作確認
3.独自コンポーネントの調整

既存基盤をGOALに据えてGO!何が起きるかな〜!
Laravel6から12へのアップグレードで想定してたこと
1. PHP 7.2 → 8.4
- 型システムの厳格化
- 型宣言なしのコードで型エラーが頻発(特にnullableまわり)
- 古いライブラリや独自クラスでint|nullのような明示的記述がなくてClash
- 自動キャスト(例:文字列 ’10’ を int に自動変換)に依存していた処理が死ぬ。
- 非推奨機能の削除
- each()、create_function()、assert(string)などのレガシーコード依存
- implode()の引数順(非標準のまま書いてると警告→エラーへ)。
- エラーハンドリングの変更
- ErrorとExceptionの取り扱いがより厳密に(catch(Throwable)でないと漏れる)
2. Laravel 6世代からの大幅変更
app/Models
ディレクトリへの移行- PSR-4のautoloadが想定外に狂う(App\User → App\Models\User へ)
- 既存のコードが全体的にApp\Userで参照していてリファクタ必須。
- ルーティング設定の変更
- $router-> vs $route-> の記法揺れ
- Route::resource()のカスタマイズ方法の変化
PHP 8.4環境構築
Dockerfileの刷新
まずはPHP環境から一気に最新化。
FROM php:7.4-apache-buster
COPY --from=composer:2.0.11 /usr/bin/composer /usr/bin/composer
変更後
FROM php:8.4-apache
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
主な変更点
- PHP 7.4 → 8.4
- Composer 2.0.11 → latest
- パッケージインストールの最適化(一括処理)
- PHP拡張にzip、intlを追加
Docker構成の現代化
Apacheはもう古い。今風のLaravel環境にしたかった。
新構成案
- PHP-FPM + Nginx (軽量・高速)
- MySQL 8.0
- Redis (セッション・キャッシュ)
- Node.js 20 (Vite用)
Apache単体 → マルチコンテナ構成で現代的に。
services:
apache:
build: ./docker/apache/Dockerfile
ports: ["8080:80"]
After(マルチコンテナ) – 長いので雰囲気だけ
services:
nginx: # リバースプロキシ
php: # PHP-FPM 8.4
mysql: # MySQL 8.0
redis: # キャッシュ・セッション
node: # Vite開発サーバー
composer.json 大手術
依存関係の刷新
🔥 Before → After
パッケージ | Before | After | 変更理由 |
---|---|---|---|
PHP | ^7.2 | ^8.4 | Laravel推奨似合わせる |
Laravel | ^6.2 | ^12.19.3 | 基盤を最新にしたい |
PHPUnit | ^8.0 | ^11.0 | 最新テストフレームワーク |
Faker | fzaninotto/faker | fakerphp/faker | 開発終了→移行 |
Ignition | facade/ignition | spatie/laravel-ignition | Laravel 11+対応 |
新規追加パッケージ
- Laravel Pint (コードフォーマッター)
- Laravel Breeze (認証スカフォールド)
- Laravel Sail (Docker開発環境)
- Predis (Redis接続)
削除パッケージ
fideloper/proxy
→ Laravel 11で統合済みdoctrine/dbal
→ Laravel 11で不要
Laravel6の構造を12で動くように変更する
Modelディレクトリの移行
- 現在: app/User.php (Laravel 6構造)
- 目標: app/Models/User.php (Laravel 8+標準)
名前空間変更: App\
→ App\Models\
全参照の更新: コントローラー、シーダー、ファクトリー
シーダーディレクトリの移行
- 現在: database/seeds/
- 目標: database/seeders/
名前空間追加: Database\Seeders
composer.json: classmapからPSR-4へ変更済み
設定ファイルの更新
- config/app.php: プロバイダー構成の見直し
- .env.example: Laravel 12対応項目の追加
- routes/: 名前空間設定の調整
作業順序
app/Models/
ディレクトリ作成- モデルファイルの移動と名前空間更新
- 全ファイルでの参照更新
- シーダー・ファクトリーの移行
Laravel12環境起動まで
- appleシリコンMacだったため、少しハマる
問題
- MySQL: ARM64対応でplatform指定不要
- Redis/PHP: 起動が非常に遅い(タイムアウト)
- Node: package.jsonが存在しないためエラー
MySQLの問題
一旦arm64v8/php:8.3-fpm-alpine
これで対応したけど、これは過渡期対応だったらしく、2025/7/1現在は公式使えば問題なさそう
# After: 公式multi-archイメージ
mysql:
image: mysql:8.4 # LTS版
# platform指定不要 - 自動でARM64を選択
PHPの問題
まずRedisはfileで要件足りるのでRedisは一旦使わないことにした。
依存関係制御
mysqlが健康状態になるまで起動を待つように修正
php:
depends_on:
mysql:
condition: service_healthy
イメージの最適化
こっちはx86-64
イメージを使ってたのがネックでこれを使うようにしたphp:8.4-fpm-alpine
公式Multi-archイメージの利点
- 自動アーキテクチャ判定: Intel Mac/M1 Mac両対応
- メンテナンス性: arm64v8/*は非公式、更新遅延リスク
Alpineの軽量メリット
- Debian系
php:8.4-fpm
約500MB - Alpine系
php:8.4-fpm-alpine
約80MB

Laravelはmuslで十分だけど、他にレガシーシステム積んでるならglib入ってないからAlpine使う場合は要注意だね〜。
Laravel12を入れたあとに諸々出た不具合
とりあえず、83パッケージ更新して、Laravel/framework v12.9.3
になった後に出た問題を列挙。
PSR-4準拠エラー
最後がdoes not comply with psr-4 autoloading standard
がいくつか出てた。原因は下記のような事象。
- ファイル名とクラス名の不一致
- 名前空間の大文字小文字ミス (
App\models\
→App\Models\
)
bootstrap/app.php 古すぎ問題
Fatal error: Class "Illuminate\Foundation\Application" not found
が出てる。調べてみると原因は
Laravel 6時代のbootstrap/app.phpでLaravel 12を起動しようとしていたこと
Before (Laravel 6)
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
// kernelバインディング...
After (Laravel 12) これに書き換え
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {})
->withExceptions(function (Exceptions $exceptions) {})
->create();
これでこのエラーは解消
Laravel8で、ルーティングの名前空間自動付与が廃止
Laravel8から名前空間の自動付与が終わりました。
Before (Laravel 6)
// config/routes.php
'callback' => 'LoginController@login' // 自動で App\Http\Controllers\ が付与
After(Laravel8+)
// 完全な名前空間が必要
LoginController@index -> App\Http\Controllers\LoginController@index
IDE補完効かせたいなら
Route::get('/',[App\Http\Controllers\LoginController::class, 'index'])
ルーティングについての詳しい説明は
👉️ 【Laravel入門】ルーティングの基本と実践活用まとめ:仕組み・書き方・落とし穴まで
LoginController@index
とりあえず、これを一個ずつちまちま名前変更するの大変なので、sed -i とか使って一撃で変更するのが良いかもです。
AuthenticatesUsersの廃止
ログイン機能が独自実装なので使ってなかったのですが、useしてたので、これをコメントアウトしました。
とはいえこんな感じ。
Trait が担当していた機能 | 独自実装でカバー |
---|---|
入力バリデーション | ✅ 自前 FormRequest (HogePost) |
Auth::attempt() + セッション再生成 | ✅ hoge_login() 内で実装 |
Throttle / Remember Me | ❌ 使っていない(実装も無し) |
ログアウト後トークン再生成 | ✅ hoge_logout() で実装 |
→ 偶然にも必要分だけ自前コードがあった ため、Trait を消しても動作は維持。
自前じゃなかったらどうするか
もし自分がTraitを使っていたとしたら、Auth::attempt()
でログインした箇所の実装を進めないといけなくなると思います。
Blade構成だからcsrfをView側にもたせてミドルウェアにVerifyCsrfToken
が登録されてる前提です。
// routes/web.php ← web ミドルウェアに乗る=VerifyCsrfToken が働く
Route::view('login', 'auth.login')->name('login');
Route::post('login', function (Illuminate\Http\Request $req) {
$cred = $req->validate([
'email' => ['required','email'],
'password' => ['required'],
]);
if (Auth::attempt($cred, $req->boolean('remember'))) { // login処理
$req->session()->regenerate(); // セッション固定攻撃防止
return redirect()->intended('/');
}
return back()->withErrors(['email'=>'認証失敗'])->onlyInput('email');
})->name('login.post');
Route::post('logout', function (Illuminate\Http\Request $req) {
Auth::logout();
$req->session()->invalidate();
$req->session()->regenerateToken(); // CSRF トークンも更新
return redirect('/');
})->name('logout');
Blade側はこんなような構成を想定してます、
// brade側
<!-- resources/views/auth/login.blade.php -->
<form method="POST" action="{{ route('login.post') }}">
@csrf <!-- ★ これがないと 419 (Page Expired) -->
<input type="email" name="email" placeholder="Email">
<input type="password" name="password" placeholder="Password">
<label><input type="checkbox" name="remember"> Remember me</label>
<button type="submit">Login</button>
</form>
ポイント
- @csrf を忘れると CSRF ミドルウェアが 419 を返す
- Remember Me を使うなら users.remember_token 列を追加
自分がもしレガシーシステム移行で、ログイン機能を自前実装しておらず、アップグレードの影響をモロに受けたらどうするかなーと考えてみました。
おわりに
正直この作業自体、別リポジトリでやるからどうなっても良いって前提作ってからやるってとこで、中々面白い作業だと思いました。
作業自体を記事にしようと思っていざ記事にしてみると、ハマったところを上手く言いたすぎて中々上手く仕上がらないなというのが正直なところです。
一連作業をあらかじめ体感しておくことで、実際自分が任された時に、どこでハマりそーかな~って目星つけられるのがいいとこかなと思いました。
というわけでLaravel12移行が済んだところでいくつか記事紹介します。
コメントを残す