laravel-debugbarとIgnitionで詰まりを素早く解消する
概要
Laravel開発では「画面が真っ白」「レスポンスは返るが中身がおかしい」「SQLが想定より何倍も飛んでいる」といった詰まりが日常的に起きます。これらを勘とログの往復で潰すのは非効率です。
本記事では、Laravel標準のエラー画面を担う Ignition(spatie/laravel-ignition) と、リクエストの内部挙動を可視化する laravel-debugbar(barryvdh/laravel-debugbar) を組み合わせ、「どこで詰まっているか」を素早く切り分ける手順を整理します。公開情報とパッケージの一般的な挙動に基づいています。
この2つは守備範囲が異なります。例外が発生して処理が止まったときはIgnition、例外は出ないが挙動が怪しいときはDebugbar、という役割分担を押さえると切り分けが速くなります。
環境
- PHP 8.2 以上(Laravel 13 は 8.3 以上)
- Laravel 11 / 12 / 13 系
barryvdh/laravel-debugbar(開発依存)spatie/laravel-ignition(Laravel 9 以降は標準同梱、Laravel 8.x 以前はfacade/ignition)
なお、Laravel 11 以降は例外ハンドラの登録先が bootstrap/app.php の withExceptions() に移っています(従来の App\Exceptions\Handler 中心の構成から変更)。Ignition が標準同梱である点は変わりません。
Debugbar は明示的に追加します。
composer require barryvdh/laravel-debugbar --dev
--dev を付けて開発依存にするのが重要です。本番混入を防ぐ第一の防波堤になります。
発生した問題
典型的な「詰まり」を3パターンに分けます。
- 例外で処理が止まる: フォーム送信後に500、または画面が真っ白
- 処理は通るが結果が変: 一覧に出るはずのデータが欠ける、更新が反映されない
- 動くが遅い: 一覧ページの読み込みが遅い(N+1クエリの疑い)
ログを tail して該当行を探すだけでは、どの層(ルーティング/コントローラ/クエリ/ビュー)で起きているかが見えにくく、調査が長引きます。
原因
詰まりの正体は多くの場合、次のいずれかに収束します。
- 例外の発生箇所と、実際に修正すべき箇所がずれている(トレースを追えていない)
- Eloquentのリレーションをループ内で都度ロードしている(N+1)
- ビューに渡したつもりの変数が渡っていない/型が想定と違う
- セッションや認証状態が想定と異なる
「どの層で何が起きたか」を可視化できれば、原因特定までの時間が大きく短縮されます。
対応方法
Ignitionで「止まった場所」を特定する
.env で APP_DEBUG=true のとき、例外発生時にIgnitionの詳細エラー画面が表示されます。見るべき点は3つです。
- スタックトレースの「アプリ側のフレーム」まで降りて確認する(ベンダー内部で止まらない)
- 例外の型で原因の層を先に絞る:
QueryException→SQL、ViewException→Blade、TypeError→型不一致 - Request / Context タブで、送られたパラメータが想定どおりか確認する
Debugbarで「通っているが怪しい処理」を覗く
APP_DEBUG=true でDebugbarが有効なとき、画面下部にバーが表示されます。よく使うタブは以下です。
| タブ | 見るもの | 切り分け |
|---|---|---|
| Queries | 実行SQL・件数・実行時間 | N+1、想定外のWHERE、重い結合 |
| Views | レンダリングされたBladeと渡された変数 | 変数未定義・型違い |
| Route | ルート・コントローラ・ミドルウェア | ルーティングの取り違え |
| Request | 入力・セッション・認証ユーザ | 認証状態・入力値 |
| Timeline | 処理区間の時間 | どの処理が遅いか |
特に Queriesタブのクエリ件数 は強力です。一覧ページを開いた瞬間に数十〜数百のクエリが並んでいれば、N+1がほぼ確定します。
コード側からDebugbarに情報を送る
use Barryvdh\Debugbar\Facades\Debugbar;
public function index()
{
$orders = Order::with('customer')->get();
Debugbar::info($orders->count());
Debugbar::startMeasure('aggregate', '集計処理');
$summary = $this->buildSummary($orders);
Debugbar::stopMeasure('aggregate');
return view('orders.index', compact('orders', 'summary'));
}
dd() のように処理を止めずに値や処理時間を確認できるため、流れを追う調査と相性が良いです。
実装例
N+1をDebugbarで見つけて潰す
まず問題のあるコードです。
// 遅い: ループ内で customer を都度ロード(N+1)
$orders = Order::all();
foreach ($orders as $order) {
echo $order->customer->name; // 毎回クエリが飛ぶ
}
Debugbarの Queries タブを開くと、select * from customers where id = ? が注文件数ぶん並びます。「感覚的に遅い」が「クエリが多い」という具体に変わります。
対策は eager loading です。
// 速い: 関連を一括ロード
$orders = Order::with('customer')->get();
foreach ($orders as $order) {
echo $order->customer->name; // 追加クエリは飛ばない
}
修正後にもう一度 Queries タブを確認し、件数が2クエリ程度に減っていれば解消です。同じ画面で件数の前後比較をするのがポイントです。
「変数が渡っていない」をViewsタブで確認する
compact() の綴りミスなど「渡しているつもりで渡せていない」ケースは、Views タブで実際に渡っている変数一覧を見るのが確実です。
確認方法
- Ignition:
APP_DEBUG=trueでわざと例外を起こし、詳細画面が出るか - Debugbar: 任意ページでバーが表示されるか
- N+1: 修正前後でクエリ件数が明確に減ったか
- 本番で両方とも無効化されているか
注意点
最重要は本番での無効化です。 DebugbarもIgnitionの詳細表示も、内部構造・SQL・環境情報を画面に露出します。本番で有効なままだと情報漏えいに直結します。
- 本番は
APP_DEBUG=falseを徹底する。Ignitionの詳細画面は標準エラーページに切り替わる - Debugbar は
DEBUGBAR_ENABLEDまたはconfig/debugbar.phpのenabledで制御する - Debugbar は必ず
--devで導入し、本番のcomposer install --no-devで混入させない - Debugbar自体が負荷をかけるため、計測の絶対値ではなく傾向・件数比較に使う
- API / JSONレスポンスにはバーが出ないため、クエリ確認は別手段と併用する
まとめ
- 止まる系はIgnition、通るけど怪しい系はDebugbar、という役割分担で切り分けを速くする
- N+1はDebugbarのQueriesタブの件数で可視化し、
with()修正後に件数比較まで行う Debugbar::info()/startMeasure()で、処理を止めずに流れと時間を追える- 本番では
APP_DEBUG=falseとDebugbar無効化・--dev導入を徹底し、情報露出を防ぐ
