Laravelには「Blade(ブレード)」と呼ばれる強力かつ軽量なテンプレートエンジンが標準で組み込まれています。{{ }}による安全な変数出力や@if・@foreachといった直感的なディレクティブ、@extendsによるレイアウト継承、そしてモダンな開発で欠かせないタグ構文の<x-component>まで、HTMLの可読性を保ちながらPHPの柔軟性をフルに活かせるのが特徴です。本記事では、Laravel Bladeの基礎概念から実務で頻出するディレクティブ一覧、コンポーネント設計、キャッシュやトラブルシューティングまで網羅して徹底解説します。
Laravel Bladeとは?仕組みと3つの特徴
BladeはLaravelに標準搭載されているテンプレートエンジンです。ビューファイルは.blade.phpという拡張子を持ち、通常はresources/viewsディレクトリ配下に配置します。コントローラーやルーティングからはグローバルなview()ヘルパー関数を使って描画します。
// routes/web.php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome', ['name' => 'Laravel Wren']);
});
Bladeには、他のPHPテンプレートエンジン(TwigやSmartyなど)や素のPHPテンプレートと比較して以下のような優れた特徴があります。
| 特徴 | 内容 |
|---|---|
| 1. オーバーヘッドが実質ゼロ | すべてのBladeテンプレートはプレーンなPHPコードにコンパイルされ、storage/framework/viewsにキャッシュされます。変更がない限りコンパイル済みPHPが直接実行されるため、高速に動作します。 |
| 2. 自動エスケープ(XSS対策) | {{ $variable }}で変数を出力すると、自動的にPHPのhtmlspecialchars関数が適用され、クロスサイトスクリプティング(XSS)攻撃を防止します。 |
| 3. PHPコードとの共存が可能 | 独自の制約でPHPの実行を縛るテンプレートエンジンとは異なり、Blade内では通常のPHPコードやヘルパー関数を必要に応じて自由に呼び出せます(@phpディレクティブも利用可能)。 |
変数の表示・エスケープ・コメントの書き方
基本的な変数出力と自動エスケープ
コントローラーやルーティングから渡された変数は、二重の中括弧{{ }}で囲んで出力します。
{{-- resources/views/profile.blade.php --}}
<h1>こんにちは、{{ $name }}さん</h1>
例えば $name に <script>alert('xss')</script> が含まれていても、HTML特殊文字が <script>... に自動変換されて安全に出力されます。
エスケープなしでの出力({!! !!})
リッチテキストエディタのHTMLなど、エスケープせずに生のHTMLとして描画したい場合は {!! !!} を使用します。
<div class="content">
{!! $trustedHtmlContent !!}
</div>
注意:ユーザーが投稿した入力値を{!! !!}で出力するとXSS脆弱性の原因になります。信頼できる管理者作成のコンテンツや、HTMLPurifier等でサニタイズ済みの文字列のみに使用してください。
Bladeコメント
Bladeファイル内でコメントを記述する場合は {{-- コメント内容 --}} を使います。HTMLの <!-- --> とは異なり、ブラウザのHTMLソースコード上には一切出力されません。
{{-- このコメントはコンパイル時に削除され、HTMLソースに残らない --}}
<!-- このコメントはブラウザのHTMLソースに見える -->
JavaScriptへのデータ受け渡し(@js)
BladeからフロントエンドのJavaScriptに変数を安全に渡す際は、@jsディレクティブを使用します。
<script>
const users = @js($users);
console.log(users);
</script>
実務で頻出する主要Bladeディレクティブ一覧
Bladeには、PHPの制御構文や実務で必要な処理を簡潔に書くための多彩なディレクティブが用意されています。
条件分岐(@if / @unless / @isset / @empty)
@if ($user->isAdmin())
<span class="badge">管理者</span>
@elseif ($user->isEditor())
<span class="badge">編集者</span>
@else
<span class="badge">一般ユーザー</span>
@endif
{{-- @unless は if (!条件) と同じ否定判定 --}}
@unless (Auth::check())
<p>ログインしていません。</p>
@endunless
{{-- 変数がセットされているか判定 --}}
@isset($message)
<p class="alert">{{ $message }}</p>
@endisset
{{-- 配列や変数が空か判定 --}}
@empty($records)
<p>データが存在しません。</p>
@endempty
ループ処理(@foreach / @forelse)と$loop変数
配列やコレクションのループには @foreach を使います。データが空のときのフォールバック表示を行いたい場合は @forelse が便利です。
<ul class="user-list">
@forelse ($users as $user)
<li class="{{ $loop->first ? 'first-item' : '' }}">
{{ $loop->iteration }}. {{ $user->name }}
@if ($loop->last)
(最後のユーザー)
@endif
</li>
@empty
<li>登録されているユーザーはいません。</li>
@endforelse
</ul>
@foreach および @forelse のループ内では、特別な変数 $loop が自動的に提供されます。
| プロパティ | 説明 |
|---|---|
$loop->index |
現在のインデックス(0始まり) |
$loop->iteration |
現在のイテレーション番号(1始まり) |
$loop->remaining |
ループの残りイテレーション数 |
$loop->count |
ループ対象の総要素数 |
$loop->first |
最初のループであれば true |
$loop->last |
最後のループであれば true |
$loop->even |
偶数番目のループであれば true |
$loop->odd |
奇数番目のループであれば true |
$loop->depth |
ネストされたループの深さ |
$loop->parent |
親ループの $loop 変数(ネスト時) |
フォームと認証ディレクティブ(@csrf / @method / @auth)
Laravelのフォーム送信やユーザー認証の状態制御には、以下のディレクティブを使用します。
<form method="POST" action="/posts/1">
@csrf
@method('PUT')
<input type="text" name="title" value="{{ old('title', $post->title) }}">
<button type="submit">更新</button>
</form>
@auth
<p>ようこそ、{{ auth()->user()->name }}さん!</p>
<a href="/logout">ログアウト</a>
@endauth
@guest
<a href="/login">ログイン</a>
<a href="/register">会員登録</a>
@endguest
クラス・スタイル・フォーム要素の動的制御(@class / @checked)
条件に応じてCSSクラスを付与したいときは @class、フォームの選択状態を制御したいときは @checked や @selected を使います。
@php
$isActive = true;
$hasError = false;
@endphp
{{-- 条件が true のキー名だけが class に出力される --}}
<button @class([
'btn',
'btn-primary' => $isActive,
'btn-danger' => $hasError,
])>送信</button>
{{-- 出力結果: --}}
<input type="checkbox" name="subscribe" value="1" @checked(old('subscribe', $user->subscribed))>
<select name="role">
<option value="admin" @selected(old('role', $user->role) === 'admin')>管理者</option>
<option value="editor" @selected(old('role', $user->role) === 'editor')>編集者</option>
</select>
レイアウト管理とテンプレート継承(@extends・@yield・@section)
Laravelの伝統的かつ強力なレイアウト管理手法が「テンプレート継承」です。共通のHTML枠組みを親レイアウトとして定義し、各ページで中身を差し替えます。
1. 親レイアウトの作成
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'デフォルトタイトル') - Laravel Wren</title>
@stack('styles')
</head>
<body>
<header>
@include('partials.nav')
</header>
<main class="container">
@yield('content')
</main>
<footer>
<p>© {{ date('Y') }} Laravel Wren</p>
</footer>
@stack('scripts')
</body>
</html>
2. 子ビューでの継承とセクション定義
{{-- resources/views/posts/index.blade.php --}}
@extends('layouts.app')
@section('title', '記事一覧')
@section('content')
<h1>最新記事一覧</h1>
<p>ここに記事の一覧が表示されます。</p>
@endsection
@push('scripts')
<script src="/js/posts.js"></script>
@endpush
子ビュー側で @push('scripts') を使うと、親レイアウトの @stack('scripts') の位置にスクリプトタグを安全に追加できます。特定ページでのみ必要なCSSやJavaScriptを読み込む際のベストプラクティスです。
モダンなBladeコンポーネントの使い方(タグ構文・スロット)
Laravel 7以降、ボタンやカードUI、モーダルなどの再利用可能なUIパーツを構築する手段として「Bladeコンポーネント」が標準で推奨されています。<x-コンポーネント名> というHTMLライクなタグ構文で呼び出せます。
匿名コンポーネント(ビューファイルのみ)
PHPクラスを作らず、Bladeファイル単体でコンポーネントを定義できます。resources/views/components 配下にファイルを配置します。
{{-- resources/views/components/card.blade.php --}}
@props([
'type' => 'info',
'title' => null,
])
<div {{ $attributes->merge(['class' => 'card card-' . $type]) }}>
@if ($title)
<div class="card-header">{{ $title }}</div>
@endif
<div class="card-body">
{{ $slot }}
</div>
</div>
呼び出し側では以下のようにタグとして利用します。
<x-card type="success" title="完了メッセージ" class="shadow-sm mt-3">
データの保存が正常に完了しました。
</x-card>
$attributes->merge() を指定しておくと、呼び出し時に渡した class="shadow-sm mt-3" などの追加属性が、コンポーネント側のデフォルトクラスと自動でマージされます。
クラスベースコンポーネント
複雑なデータ取得や事前処理を伴うコンポーネントは、Artisanコマンドでクラスとビューを生成します(コマンドの詳細はmake:component — コンポーネントを作成するコマンドで詳しく解説しています)。
php artisan make:component Alert
// app/View/Components/Alert.php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class Alert extends Component
{
public function __construct(
public string $type = 'info',
public string $message = '',
) {}
public function render(): View
{
return view('components.alert');
}
}
名前付きスロット(x-slot)
コンポーネント内の複数の場所にコンテンツを流し込みたい場合は、<x-slot:スロット名> を活用します。
{{-- resources/views/components/modal.blade.php --}}
<div class="modal">
<div class="modal-title">{{ $title }}</div>
<div class="modal-body">{{ $slot }}</div>
<div class="modal-footer">{{ $footer }}</div>
</div>
<x-modal>
<x-slot:title>
確認ダイアログ
</x-slot:title>
本当に削除してもよろしいですか?
<x-slot:footer>
<button class="btn btn-secondary">キャンセル</button>
<button class="btn btn-danger">削除する</button>
</x-slot:footer>
</x-modal>
カスタムディレクティブの作成方法
プロジェクト内で頻繁に利用するフォーマット処理などは、Blade::directive を使って独自のディレクティブとして登録できます。通常は app/Providers/AppServiceProvider.php の boot メソッド内で定義します。
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Blade;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// 金額を「¥1,000」形式でフォーマットするディレクティブ
Blade::directive('money', function (string $amount) {
return "<?php echo '¥' . number_format($amount); ?>";
});
}
}
<p>合計金額: @money($order->total_price)</p>
{{-- 出力結果: 合計金額: ¥12,500
--}}
注意:カスタムディレクティブのロジックを書き換えた後は、コンパイル済みビューキャッシュを破棄しないと変更が反映されません。必ず後述する php artisan view:clear を実行してください。
キャッシュとトラブルシューティング
1. 変更が反映されない時は「view:clear」
Bladeファイルを編集したのにブラウザの表示が変わらない場合や、ディレクティブの定義を修正した場合は、ビューキャッシュをクリアします(詳細はview:clear — 事前コンパイルされた Blade ビューを削除するコマンドを参照)。
php artisan view:clear
2. 本番環境の高速化には「view:cache」
本番環境へデプロイする際は、すべてのBladeビューを事前にコンパイルしておくことで、初回のアクセス速度を向上させることができます(詳細はview:cache — ビューキャッシュを有効にするコマンドを参照)。
php artisan view:cache
3. よくあるエラーと対処法
View [xxx] not found.
指定したビューファイルが存在しないか、スペルミス、またはドット区切りのパス指定(例:view('posts.index')に対してresources/views/posts/index.blade.phpがあるか)を確認してください。Undefined variable $xxx
コントローラーからビューへ変数が正しく渡されていない場合に発生します。view('xxx', compact('data'))やview('xxx', ['data' => $data])を確認してください。- ディレクティブの閉じ忘れ
@ifに対する@endifや、@foreachに対する@endforeachの閉じ忘れがあると構文エラーになります。
Bladeとあわせて押さえておきたい関連記事
- view:clear — 事前コンパイルされた Blade ビューを削除するコマンド — ビューのキャッシュを安全に削除して最新状態にする方法
- view:cache — ビューキャッシュを有効にするコマンド — 本番環境デプロイ時の高速化手順
- make:component — コンポーネントを作成するコマンド — Artisanコマンドによるコンポーネント雛形の生成手順
- Laravelのyield: ビューのテンプレートを効果的に管理する方法 — テンプレート継承の詳しい実践ガイド
- Laravel Viewの仕組みと効果的なテンプレート管理テクニック — ビューとコントローラーの設計ベストプラクティス
- Laravelでのnull判定方法まとめ:Eloquent、Blade、条件文の使い方ガイド — Blade側での安全なnullハンドリング
よくある質問(FAQ)
Q. Bladeを使うと通常のPHPテンプレートより遅くなりますか?
A. いいえ、遅くなりません。Bladeは初回実行時にプレーンなPHPコードへ一度だけコンパイルされ、ファイルとしてキャッシュされます。2回目以降のリクエストではキャッシュされたPHPコードがそのまま実行されるため、オーバーヘッドは実質ゼロです。
Q. テンプレート継承(@extends)とBladeコンポーネント(<x-…>)の使い分けは?
A. ページ全体の共通外枠(HTMLヘッダー、ナビゲーション、フッターなど)を定義する場合は@extendsまたはレイアウトコンポーネント(<x-app-layout>)を使用します。一方、ボタン、アラート、モーダル、カードといった画面内の部品を再利用する場合は<x-component>が最適です。
Q. Bladeテンプレート内で通常のPHP処理を実行できますか?
A. はい、可能です。一時的な変数計算などを行いたい場合は @php ... @endphp ディレクティブを使って任意のPHPコードを記述できます。ただし、複雑なビジネスロジックはビューに書かず、コントローラーやサービスクラス側に持たせるのがベストプラクティスです。
Q. ディレクティブの変更がブラウザに反映されません。どうすればよいですか?
A. php artisan view:clear を実行してコンパイル済みビューキャッシュを削除してください。カスタムディレクティブの追加や変更時は、キャッシュをクリアしないと古いコンパイル結果が参照され続けることがあります。
まとめ
Laravel Bladeは、安全な変数出力(自動エスケープ)、簡潔なディレクティブ構文、DRY原則を守れるレイアウト継承とコンポーネント機能を兼ね備えた洗練されたテンプレートエンジンです。
- 変数表示は基本的に
{{ $var }}を使い、XSSを防止する - 条件分岐やループは
@if、@foreach、$loop変数を活用する - フォーム送信時は
@csrfと@methodを忘れない - 部品の再利用には
<x-component>と@propsを積極的に導入する - 挙動に違和感がある時は
php artisan view:clearでキャッシュをリフレッシュする
本記事で紹介したコード例を参考に、見通しが良く保守性の高いLaravelフロントエンドを構築してみてください。

コメント