カテゴリー
コンピューター

Laravel 11アプリケーション構造アップグレード奮闘記 〜AIと歩むSlim Skeletonへの道〜

はじめに(この記事について)

この記事は、Gemini CLI を使って作業した最後に、一緒に行った作業をブログとして出力したものを大部分そのまま採用し、少し軌道等を修正したものです。

はじめに:なぜ今、Laravel 11のSlim Skeletonへ移行するのか?

このプロジェクトは、RESTful APIのバックエンドとシングルページアプリケーション(SPA)のフロントエンドを持つWebアプリケーションのボイラープレート(ひな形)です。Laravel9をベースに構築されていましたが、バージョン 11 までアップグレードし、この時に、Laravel 11が導入した「Slim Skeleton(合理化されたアプリケーション構造)」への移行を決断しました。

Laravel11では、従来のバージョンと比較して多くの設定ファイルやボイラープレートコードが削除され、よりミニマルで効率的な開発環境が提供されます。ドキュメントには「既存プロジェクトの移行は非推奨」という文面があります。ボイラープレートでなければ、このままの構造としたでしょう。一方で、ボイラープレートとしての使命は、常に最新のベストプラクティスと構造を提供することにあります。この抜本的な構造変更に対応することで、将来のメンテナンス性向上と、よりクリーンなコードベースを目指しました。

本稿では、AIエージェント(以下、AI)との対話を通じて、この挑戦的なアップグレード作業がどのように進められ、どのような困難に直面し、それをどう乗り越えたのかを詳細に記録します。特に、トライアル&エラーのプロセスを厚く記述し、同様の移行を行う際の参考となることを狙っています。

AIとの協調作業:アップグレード計画の策定

アップグレード作業を開始するにあたり、AIと共に詳細な計画を策定しました。初期段階では、AIの計画の抽象度や情報源の提示不足が課題となりましたが、ユーザーからの具体的なフィードバックとWeb調査(Laravel公式ドキュメント、no-hack-no.life様の記事など)を繰り返すことで、「最終決定版」の計画へとブラッシュアップされました。

(※計画の詳細は長くなるため、本稿では割愛しますが、最終的に確定したステップに沿って作業は進行しました。)

💡 後から追記。特に参考にしたページを加筆します。また、参考ページの URL を渡した後でないと、参考ページをベースとした計画とはなりませんでした。当たり前ですね。よって、ふまえたいWebページがあるなら、それを予め渡した方が良い、生成 AI が調べてそのページを拾い上げてくれると期待するのはやめた方が良い、と思います。

ベースラインの確立:嵐の前の静けさ

アップグレード作業に着手する前に、現在のプロジェクトが健全な状態にあることを確認するため、以下のテストと静的解析を実行しました。

  • make php-test: 全てのPHPUnitテストがパス。
  • make php-check-all: PHPの静的解析(PHPStan)とコードフォーマット(Pint, Rector)が問題なく完了。
  • make npm-lint: フロントエンドのESLintが問題なく完了。
  • make npm-format: フロントエンドのPrettierによるフォーマットが問題なく完了。

それぞれのコマンドの中身、詳細

これにより、アップグレードによるデグレードが発生した場合に、その原因を特定するための明確な「ベースライン」を確立しました。

激動のアップグレード作業:試行錯誤の記録 (ポストモーテム)

ここからは、実際にAIエージェントと対話しながら進めたアップグレード作業の詳細と、発生した様々なエラー、そしてその解決策について、ポストモーテム形式で記録します。

フェーズ1: 準備と依存関係の更新

  1. 「正解」となるLaravel 11クリーンインストール環境のダウンロード

    • git clone --depth 1 -b 11.x https://github.com/laravel/laravel.git laravel-11-fresh を実行し、Laravel 11の最新のひな形をローカルに、 laravel-11-fresh ディレクトリに用意しました。
  2. composer.json / package.json の更新

    • laravel-11-fresh の内容を参考に、プロジェクトの composer.json と package.json を更新。laravel/framework のバージョンを ^11.31 に、その他の開発用パッケージのバージョンも最新に合わせました。
    • ポイント: プロジェクト固有の laravel/sanctum や barryvdh/laravel-ide-helper などは維持する判断を行いました。
  3. パッケージのインストール

    • composer update および npm install を実行し、依存関係を更新しました。

フェーズ2: アプリケーション構造の移行

  1. bootstrap/app.php の刷新とミドルウェアの移植

    • laravel-11-fresh/bootstrap/app.php で既存のファイルを上書きし、古い app/Http/Kernel.php のミドルウェア設定を新しい withMiddleware(…) メソッド内に移植しました。
  2. ルーティングとコマンドの再構築

    • php artisan install:api を実行し、APIルーティング(routes/api.php)を再構築。Sanctumのマイグレーションが公開されました。
      • 後から追記: コマンド実行はしたが、 routes/api.php は差分なしだった。「Sanctumのマイグレーションが公開」はそんなことはないような感じがする。
    • 古い app/Console/Kernel.php を確認しましたが、カスタムのタスクスケジュールは存在しなかったため、移行は不要でした。
  3. サービスプロバイダのカスタムロジック移行

    • RouteServiceProvider のレート制限ロジックを AppServiceProvider の boot() メソッドに移植。
    • EventServiceProvider の shouldDiscoverEvents(false) も当初 AppServiceProvider に移植しようとしましたが、これは BadMethodCallException を引き起こすことが判明しました(後述)。
  4. エラー処理(Handler.php)の移行不要の判断

    • 古い app/Exceptions/Handler.php にカスタムロジックがなかったため、移行は不要と判断しました。
  5. 不要ファイルの削除とconfigディレクトリの整理

    • 古い Kernel.php、Handler.php、AuthServiceProvider.php、BroadcastServiceProvider.php、RouteServiceProvider.php などのファイルを削除。
    • config/ ディレクトリから broadcasting.php、hashing.php、view.php を削除。cors.php、sanctum.php、ide-helper.php はプロジェクトの要件に基づき維持しました。
    • routes/channels.php も、カスタムの定義がなかったため削除しました。

トライアル&エラーの深淵:エラーとその解決策 (厚めに記述)

アップグレード作業は決して平坦ではありませんでした。多くのエラーに直面し、AIとの協力のもと、一つずつ解決していきました。

問題1: php artisan migrate 失敗 – Class “App\Providers\AuthServiceProvider” not found

  • 発生状況: 構造移行フェーズが完了し、php artisan migrate を実行しようとした際に発生。
  • 原因究明: AuthServiceProvider.php は削除済みであるにも関わらず、Laravelがこのクラスを読み込もうとしていました。これは、古い config/app.php にプロバイダのリストが残っていたためでした。Laravel 11では config/app.php からプロバイダのリストは削除されます。また、古いキャッシュが原因でこの古い config/app.php が参照され続けていました。
  • 解決策:
    1. laravel/config/app.php を、Laravel 11の「正解」である laravel-11-fresh/config/app.php の内容で完全に上書きしました。
    2. rm -f laravel/bootstrap/cache/*.php で全てのアプリケーションキャッシュを手動で削除しました。
    3. composer dump-autoload --no-scripts を実行し、Artisanコマンドを実行せずにオートロードファイルを再生成しました。
    4. php artisan config:clear で設定キャッシュをクリアし、composer dump-autoload を再度実行しました。
    5. その後、php artisan migrate がエラーなく完了しました。

問題2: make php-test 失敗 – 405 Method Not Allowed & Route [verification.verify] not defined

  • 発生状況: php artisan migrate が完了し、make php-test を実行した際に大量の認証関連テストが失敗。
  • 原因究明: bootstrap/app.php の ->withRouting(...) メソッドからAPIルートの定義 (api: __DIR__.'/../routes/api.php') が抜けていました。これにより、アプリケーションが routes/api.php に定義された認証関連のAPIエンドポイントを認識できていませんでした。
  • 解決策: laravel/bootstrap/app.php の ->withRouting(...) メソッドに api: __DIR__.'/../routes/api.php', を追加しました。その後、php artisan route:clear でルーティングキャッシュをクリアし、テストを再実行しました。

問題3: make php-test 失敗 – MissingRateLimiterException: Rate limiter [api] is not defined.

  • 発生状況: APIルートの定義を追加しテストを再実行した際に、新たなエラーが発生。
  • 原因究明: AppServiceProvider の boot() メソッド内で定義したレートリミッター (RateLimiter::for('api', ...)) が、アプリケーションに適切にロードされていませんでした。原因は、bootstrap/app.php に AppServiceProvider が明示的に登録されていなかったためです。
  • 解決策:
    1. laravel/bootstrap/app.php の ->withRouting(...) の直後に ->withProviders([App\Providers\AppServiceProvider::class,]) を追加しました。これにより AppServiceProvider が正しくロードされるようになりました。
    2. その後、php artisan optimize:clear でキャッシュをクリアし、テストを再実行しました。

問題4: make php-test 失敗 – BadMethodCallException: Method Illuminate\Events\Dispatcher::shouldDiscoverEvents does not exist.

  • 発生状況: AppServiceProvider の登録問題を解決しテストを再実行した際に、AppServiceProvider の boot() メソッド内の Event::shouldDiscoverEvents(false); の呼び出しで発生。
  • 原因究明: Illuminate\Events\Dispatcher (つまり Event ファサード) に shouldDiscoverEvents メソッドは存在しません。これは EventServiceProvider クラスが持つメソッドでした。
  • 解決策:
    1. laravel/app/Providers/AppServiceProvider.php から Event::shouldDiscoverEvents(false); の行を削除しました。
    2. Laravel 11のサービスプロバイダ登録のベストプラクティスに従い、EventServiceProvider を再作成し、元の protected $listen の定義と shouldDiscoverEvents() が false を返すように設定しました。
    3. bootstrap/app.php から AppServiceProvider を直接登録していた ->withProviders(...) の記述を削除しました。
    4. laravel/bootstrap/providers.php ファイルを新規作成し、そこに App\Providers\AppServiceProvider::class,App\Providers\EventServiceProvider::class, を登録しました。
    5. php artisan optimize:clear を実行し、キャッシュをクリアしてからテストを再実行しました。

問題5: make php-check-all 失敗 – phpstan エラー (Access to constant HOME on an unknown class App\Providers\RouteServiceProvider.)

  • 発生状況: phpstan を含む make php-check-all を実行した際に発生。
  • 原因究明: Http/Middleware/RedirectIfAuthenticated.php が、すでに削除済みの App\Providers\RouteServiceProvider::HOME 定数を参照しようとしていました。
  • 解決策: laravel/app/Http/Middleware/RedirectIfAuthenticated.php の return redirect(RouteServiceProvider::HOME);return redirect('/home'); に修正しました。これにより、phpstan エラーが解消されました。

最終確認とクリーンアップ

上記の一連のトライアル&エラーと修正を経て、すべてのPHPUnitテスト、静的解析、リンティング、フォーマットがパスする状態となりました。

  • make php-test: 全てのテストがパス。
  • make php-check-all: 静的解析とコードフォーマットが問題なく完了。
  • make npm-lint: フロントエンドのリントが問題なく完了。
  • make npm-format: フロントエンドのフォーマットが問題なく完了。

これにより、Laravel 11の新しいアプリケーション構造へのアップグレードが成功し、アプリケーションの健全性が確認されました。

💡 後から追記。さらにやりたいこと。

ミドルウェアなど、移しました。それらの全てが移す必要があったのかどうか、見極めたいと感じています。

具体的には、移動したミドルウェアなどの中には移さなくても Laravel 側でやってくれるものがあるのではないか、 Slim とはいえ。

もう一つ、移したものの Slim にはデフォルトでは含まれていないし、私のボイラープレートでも使っていないので、ある意味不要でしかない機能を移してしまった、という状況になっていないか、ということを精査したいと考えています。

Appendix: AIとの協調作業から得られた教訓 (メタ的な視点)

今回のAIとの共同作業は、技術的な課題解決だけでなく、AIとの対話の進め方や期待値管理についても多くの示唆を与えてくれました。

ポジティブな側面:AIとの対話の強み

  • 網羅的な情報収集能力: AIは、指定されたキーワードやURLから大量の情報を迅速に収集し、要約する能力に優れています。これにより、ユーザーは膨大な情報を自分で調べる手間を省くことができました。
  • 計画の逐次修正と実行能力: ユーザーからのフィードバックや新たな情報の入手に応じて、AIは計画を柔軟に修正し、ツール(read_file, replace, run_shell_command など)を適切に選択・実行することで、具体的なコード変更やコマンド実行を安全に進めることができました。
  • 問題解決のイテレーション: 発生したエラーに対し、AIはログを分析し、仮説を立て、解決策を適用し、再度テストを実行するという一連のイテレーションを繰り返すことで、複雑な問題を段階的に解決していきました。

課題と改善点:AIとの対話のもたつき

  • 初期計画の抽象度とピンポンの発生: 私の初期計画は、Laravel 11の抜本的な変更点を十分に消化しておらず、抽象的な記述に留まっていました。これにより、ユーザーからの具体的なフィードバックを何度も受けることになり、計画策定フェーズでの「ピンポン」が多く発生しました。より早い段階での詳細な情報収集と計画の具体化が課題です。
  • 情報源の正確性確認の不足: 公式ドキュメントのURLのアンカーリンクが不正確であったり、情報源の提示が不足していたため、ユーザーの信頼を損ねる場面がありました。AIは提示する情報の正確性について、より厳格な自己検証を行う必要があります。
  • フレームワーク固有の変更点への追従の遅れ(AI自身の知識更新の課題): Laravel 11のサービスプロバイダ登録の変更や Event::shouldDiscoverEvents のようなバージョン固有の挙動について、私の知識が十分にアップデートされておらず、誤ったアプローチを取ってしまいました。AIは、特定の技術スタックのメジャーバージョンアップに伴う変更点について、より迅速かつ深く知識を更新するメカニズムが必要です。
  • AIとの対話の「メタ視点」の言語化不足: ユーザーが求めていた「AIとの対話のメタ的な視点」に関するフィードバックや自己分析が、私から積極的に提供できていませんでした。これはAI自身の反省点であり、より能動的に自己評価を共有する能力を磨く必要があります。

まとめ:AIとの協調作業から得られた教訓

AIとの協調作業は、技術的な知識と実行力を持つ一方で、人間の経験や文脈理解、そして抽象的な指示から具体的な意図を読み解く能力が不可欠であることを示しました。今回のアップグレード作業は、AIの限界と可能性を浮き彫りにし、今後のAI支援のあり方について多くの学びを得る機会となりました。ユーザーからの的確なフィードバックと根気強い対話がなければ、このプロジェクトは成功しなかったでしょう。

以上です。

コメントを残す