◀ 3.

繰り返し(@for, @foreach, @forelse, @while)

▶
この記事の要点
  • Blade の繰り返しは @for / @foreach / @forelse / @while の 4 種類。中身は PHP の for / foreach / while と同じ
  • コレクションや配列を回すなら @foreach が基本
  • @forelse ... @empty ... @endforelse なら「0 件のときの表示」を 1 つの構文で書ける
  • @foreach / @forelse の中では $loop 変数で「最初か」「最後か」「何回目か」が分かる
  • 途中で飛ばす・抜けるは @continue / @break。条件を引数に書ける
  • ループ内でリレーションを参照すると N+1 クエリになりやすい。コントローラ側で with() を使う

Laravel の Blade テンプレートで使用する繰り返し(for 文)に関する記事です。各ディレクティブの書き方、$loop 変数、よくあるミスをまとめます。

構文の一覧

ディレクティブ用途$loop 変数
@for ... @endfor回数が決まっている繰り返し(1〜10 の数字を出すなど)使えない
@foreach ... @endforeach配列・コレクションの全要素を順に処理使える
@forelse ... @empty ... @endforelseforeach + 「0 件だった場合」の表示使える
@while ... @endwhile条件が真の間繰り返す使えない

基本の書き方

@for ($i = 0; $i < 10; $i++)
    現在の値は: {{ $i }}
@endfor

@foreach ($users as $user)
    <p>これは {{ $user->id }} ユーザーです。</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>ユーザーなし</p>
@endforelse

@while (true)
    <p>無限ループ中</p>
@endwhile

最後の @while (true) は構文の例で、実際に書くと終わらずにタイムアウトします。@while を使うときは、ループ内で必ず条件が偽になるように変数を変化させます。

キーと値の両方を使う

@foreach ($prices as $name => $price)
    <tr>
        <td>{{ $name }}</td>
        <td>{{ number_format($price) }} 円</td>
    </tr>
@endforeach

$loop 変数で「何回目か」「最後か」を判定する

@foreach と @forelse の中では、Blade が自動で $loop という変数を用意します。カンマ区切りの最後だけカンマを付けない、最初の行だけ強調する、といった処理を自前のカウンタなしで書けます。

プロパティ意味
$loop->index0 から始まる番号
$loop->iteration1 から始まる番号
$loop->remaining残りの回数
$loop->count全体の要素数
$loop->first / $loop->last最初 / 最後の要素なら true
$loop->even / $loop->odd偶数回目 / 奇数回目なら true(iteration 基準)
$loop->depthネストの深さ(一番外側が 1)
$loop->parentネストしたループで、外側のループの $loop
@foreach ($tags as $tag)
    <span @class(['tag', 'tag-first' => $loop->first])>{{ $tag->name }}</span>@if (! $loop->last), @endif
@endforeach

@foreach ($categories as $category)
    @foreach ($category->items as $item)
        {{ $loop->parent->iteration }}-{{ $loop->iteration }}: {{ $item->name }}
    @endforeach
@endforeach

途中で飛ばす・抜ける(@continue / @break)

@foreach ($users as $user)
    @continue($user->is_banned)   {{-- 条件が真ならこの回を飛ばす --}}

    <li>{{ $user->name }}</li>

    @break($loop->iteration >= 5) {{-- 5 件表示したら抜ける --}}
@endforeach

@if ... @continue ... @endif と書くこともできますが、引数に条件を書く形の方が短く読みやすくなります。ただし「最初の 5 件だけ表示」が目的なら、ビューではなくコントローラ側で take(5) や limit(5) で絞る方が無駄なデータを取得しません。

部分テンプレートを繰り返す(@each)

{{-- 各要素を views/users/item.blade.php に $user として渡す。0 件なら users/empty を表示 --}}
@each('users.item', $users, 'user', 'users.empty')

@each で読み込んだ部分テンプレートは親ビューの変数を引き継がないため、親の変数も使いたい場合は @foreach の中で @include を使います。

よくある落とし穴

  • N+1 クエリ: @foreach ($posts as $post) {{ $post->user->name }} のようにループ内でリレーションを参照すると、件数分の SQL が発行される。コントローラで Post::with('user')->get() として事前に読み込む
  • $loop は @for / @while では使えない: 使うと「Undefined variable $loop」になる
  • 閉じ忘れ: @endforeach などを忘れると、エラーの行番号がずれた位置で「syntax error, unexpected end of file」になる
  • エスケープ: {{ }} は自動で HTML エスケープされる。{!! !!} はエスケープしないので、利用者が入力した値には使わない
  • null を渡す: $users が null だと foreach でエラーや警告になる。コントローラで必ず配列かコレクションを渡すか、@forelse と組み合わせて $users ?? [] とする

確認方法

  • 想定どおりのデータが渡っているかは、ループの前に @dump($users) を置いて確認できる(確認後は必ず削除)
  • Blade がどんな PHP に変換されたかは storage/framework/views のコンパイル済みファイルで見られる。表示が古いままなら php artisan view:clear

関連

Post Share
子ページ

子ページはありません

同階層のページ
  1. 親ビューと子ビュー(@section, @yield, @extends, @parent, @include)
  2. 条件分岐(@if)
  3. 繰り返し(@for, @foreach, @forelse, @while)
  4. 個別のページにcssやjsを反映させる方法