Bladeテンプレート
- イントロダクション
- データ表示
- Bladeディレクティブ
- コンポーネント
- 無名コンポーネント
- レイアウトの構築
- フォーム
- スタック
- サービス注入
- インラインBladeテンプレートのレンダー
- Bladeフラグメントのレンダー
- Bladeの拡張
イントロダクション
Bladeは、Laravelに含まれているシンプルかつ強力なテンプレートエンジンです。一部のPHPテンプレートエンジンとは異なり、Bladeはテンプレート内で素のPHPコードを使用することを制限しません。実際、すべてのBladeテンプレートは素のPHPコードにコンパイルされ、変更されるまでキャッシュされます。つまり、Bladeはアプリケーションに実質的にオーバーヘッドを加えません。Bladeテンプレートファイルは.blade.phpファイル拡張子を使用し、通常はresources/viewsディレクトリに保存します。
Bladeビューは、グローバルなviewヘルパを使用してルートやコントローラから返すことができます。もちろん、ビューのドキュメントで述べられているように、viewヘルパの第2引数を使用してデータをBladeビューに渡すことができます。
1Route::get('/', function () {2 return view('greeting', ['name' => 'Finn']);3});
LivewireによるBladeの強化
Bladeテンプレートを次のレベルに引き上げ、動的なインターフェイスを簡単に構築したいですか?Laravel Livewireをチェックしてください。Livewireを使用すると、通常はReactやVueのようなフロントエンドフレームワークを介してのみ可能だった動的な機能で拡張されたBladeコンポーネントを作成できます。これにより、多くのJavaScriptフレームワークの複雑さ、クライアントサイドのレンダー、またはビルドステップなしで、モダンでリアクティブなフロントエンドを構築するための優れたアプローチが提供されます。
データ表示
Bladeビューに渡されたデータを表示するには、変数を波括弧で囲みます。たとえば、次のルートがあるとします。
1Route::get('/', function () {2 return view('welcome', ['name' => 'Samantha']);3});
name変数の内容を次のように表示できます。
1Hello, {{ $name }}.
Bladeの{{ }} echo文は、XSS攻撃を防ぐために自動的にPHPのhtmlspecialchars関数を通ります。
ビューに渡された変数の内容を表示するだけに限定されません。PHP関数の結果をechoすることもできます。実際、Bladeのecho文の中に好きなPHPコードを何でも記述できます。
1The current UNIX timestamp is {{ time() }}.
HTMLエンティティエンコーディング
デフォルトでは、Blade(およびLaravelのe関数)はHTMLエンティティを二重にエンコードします。二重エンコーディングを無効にしたい場合は、AppServiceProviderのbootメソッドからBlade::withoutDoubleEncodingメソッドを呼び出してください。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\Facades\Blade; 6use Illuminate\Support\ServiceProvider; 7 8class AppServiceProvider extends ServiceProvider 9{10 /**11 * Bootstrap any application services.12 */13 public function boot(): void14 {15 Blade::withoutDoubleEncoding();16 }17}
エスケープしないデータの表示
デフォルトで、Bladeの{{ }}文は、XSS攻撃を防ぐためにPHPのhtmlspecialchars関数を自動的に通します。データをエスケープしたくない場合は、次の構文を使用できます。
1Hello, {!! $name !!}.
アプリケーションのユーザーから提供されたコンテンツをechoする場合は、細心の注意を払ってください。ユーザーから提供されたデータを表示する場合は、XSS攻撃を防ぐために、エスケープされた二重波括弧構文を常に使用すべきです。
BladeとJavaScriptフレームワーク
多くのJavaScriptフレームワークも「波」括弧を使用して、特定の式をブラウザに表示することを示すため、@記号を使用して、式を変更しないようにBladeレンダーエンジンに指示できます。例:
1<h1>Laravel</h1>2 3Hello, @{{ name }}.
この例では、@記号はBladeによって削除されます。ただし、{{ name }}式はBladeエンジンによって変更されないため、JavaScriptフレームワークでレンダーできます。
@記号は、Bladeディレクティブをエスケープするためにも使用できます。
1{{-- Blade template --}}2@@if()3 4<!-- HTML output -->5@if()
JSONのレンダー
JavaScript変数を初期化するために、ビューに配列を渡してJSONとしてレンダーしたい場合があります。例:
1<script>2 var app = <?php echo json_encode($array); ?>;3</script>
しかし、json_encodeを手動で呼び出す代わりに、Illuminate\Support\Js::fromメソッドのディレクティブを使用できます。fromメソッドはPHPのjson_encode関数と同じ引数を受け入れますが、結果のJSONがHTMLの引用符内に含めるために適切にエスケープされることを保証します。fromメソッドは、指定されたオブジェクトまたは配列を有効なJavaScriptオブジェクトに変換する文字列JSON.parse JavaScriptステートメントを返します。
1<script>2 var app = {{ Illuminate\Support\Js::from($array) }};3</script>
Laravelアプリケーションのスケルトンの最新バージョンには、Jsファサードが含まれており、Bladeテンプレート内でこの機能へ便利にアクセスできます。
1<script>2 var app = {{ Js::from($array) }};3</script>
Js::fromメソッドは、既存の変数をJSONとしてレンダーするためにのみ使用してください。Bladeテンプレートは正規表現に基づいており、複雑な式をディレクティブに渡そうとすると、予期しない失敗を引き起こす可能性があります。
@verbatimディレクティブ
テンプレートの大部分でJavaScript変数を表示する場合は、HTMLを@verbatimディレクティブでラップすることで、各Blade echo文の前に@記号を付ける必要がなくなります。
1@verbatim2 <div class="container">3 Hello, {{ name }}.4 </div>5@endverbatim
Bladeディレクティブ
テンプレートの継承とデータの表示に加えて、Bladeは条件文やループなどの一般的なPHP制御構造の便利なショートカットも提供します。これらのショートカットは、PHPの制御構造を非常にクリーンで簡潔に扱う方法を提供し、PHPの対応するものとおなじみのままです。
if文
@if、@elseif、@else、@endifディレクティブを使用してif文を構築できます。これらのディレクティブは、PHPの対応するものとまったく同じように機能します。
1@if (count($records) === 1)2 I have one record!3@elseif (count($records) > 1)4 I have multiple records!5@else6 I don't have any records!7@endif
Bladeは利便性のために、@unlessディレクティブも提供しています。
1@unless (Auth::check())2 You are not signed in.3@endunless
すでに説明した条件付きディレクティブに加えて、@issetおよび@emptyディレクティブは、それぞれのPHP関数の便利なショートカットとして使用できます。
1@isset($records)2 // $records is defined and is not null...3@endisset4 5@empty($records)6 // $records is "empty"...7@endempty
認証ディレクティブ
@authおよび@guestディレクティブを使用して、現在のユーザーが認証済みかゲストであるかをすばやく判断できます。
1@auth2 // The user is authenticated...3@endauth4 5@guest6 // The user is not authenticated...7@endguest
必要に応じて、@authおよび@guestディレクティブを使用するときにチェックする認証ガードを指定できます。
1@auth('admin')2 // The user is authenticated...3@endauth4 5@guest('admin')6 // The user is not authenticated...7@endguest
環境ディレクティブ
@productionディレクティブを使用して、アプリケーションが本番環境で実行されているかどうかを確認できます。
1@production2 // Production specific content...3@endproduction
または、@envディレクティブを使用して、アプリケーションが特定の環境で実行されているかどうかを判断できます。
1@env('staging')2 // The application is running in "staging"...3@endenv4 5@env(['staging', 'production'])6 // The application is running in "staging" or "production"...7@endenv
セクションディレクティブ
@hasSectionディレクティブを使用して、テンプレート継承セクションにコンテンツがあるかどうかを判断できます。
1@hasSection('navigation')2 <div class="pull-right">3 @yield('navigation')4 </div>5 6 <div class="clearfix"></div>7@endif
sectionMissingディレクティブを使用して、セクションにコンテンツがないかどうかを判断できます。
1@sectionMissing('navigation')2 <div class="pull-right">3 @include('default-navigation')4 </div>5@endif
セッションディレクティブ
セッション値が存在するかどうかを判断するために、@sessionディレクティブを使用できます。セッション値が存在する場合、@sessionと@endsessionディレクティブ内のテンプレートコンテンツが評価されます。@sessionディレクティブのコンテンツ内では、$value変数をエコーしてセッション値を表示できます。
1@session('status')2 <div class="p-4 bg-green-100">3 {{ $value }}4 </div>5@endsession
switch文
switch文は、@switch、@case、@break、@default、@endswitchディレクティブを使用して構築できます。
1@switch($i) 2 @case(1) 3 First case... 4 @break 5 6 @case(2) 7 Second case... 8 @break 9 10 @default11 Default case...12@endswitch
ループ
条件文に加えて、BladeはPHPのループ構造を扱うための簡単なディレクティブも提供します。繰り返しになりますが、これらのディレクティブはそれぞれ、PHPの対応するものとまったく同じように機能します。
1@for ($i = 0; $i < 10; $i++) 2 The current value is {{ $i }} 3@endfor 4 5@foreach ($users as $user) 6 <p>This is user {{ $user->id }}</p> 7@endforeach 8 9@forelse ($users as $user)10 <li>{{ $user->name }}</li>11@empty12 <p>No users</p>13@endforelse14 15@while (true)16 <p>I'm looping forever.</p>17@endwhile
foreachループを反復処理するときに、ループ変数を使用して、ループの最初または最後の反復処理であるかなど、ループに関する貴重な情報を取得できます。
ループを使用するときは、@continueおよび@breakディレクティブを使用して、現在の反復処理をスキップしたり、ループを終了したりすることもできます。
1@foreach ($users as $user) 2 @if ($user->type == 1) 3 @continue 4 @endif 5 6 <li>{{ $user->name }}</li> 7 8 @if ($user->number == 5) 9 @break10 @endif11@endforeach
ディレクティブ宣言内に継続または中断の条件を含めることもできます。
1@foreach ($users as $user)2 @continue($user->type == 1)3 4 <li>{{ $user->name }}</li>5 6 @break($user->number == 5)7@endforeach
ループ変数
foreachループを反復処理する間、ループ内で$loop変数が利用可能になります。この変数は、現在のループインデックスや、これがループの最初または最後の反復処理であるかなど、いくつかの有用な情報へのアクセスを提供します。
1@foreach ($users as $user) 2 @if ($loop->first) 3 This is the first iteration. 4 @endif 5 6 @if ($loop->last) 7 This is the last iteration. 8 @endif 9 10 <p>This is user {{ $user->id }}</p>11@endforeach
ネストされたループ内にいる場合は、parentプロパティを介して親ループの$loop変数にアクセスできます。
1@foreach ($users as $user)2 @foreach ($user->posts as $post)3 @if ($loop->parent->first)4 This is the first iteration of the parent loop.5 @endif6 @endforeach7@endforeach
$loop変数には、他にもさまざまな便利なプロパティが含まれています。
| プロパティ | 説明 |
|---|---|
$loop->index |
現在のループ反復のインデックス(0から始まります)。 |
$loop->iteration |
現在のループ反復(1から始まります)。 |
$loop->remaining |
ループの残りの反復回数。 |
$loop->count |
反復処理される配列内のアイテムの総数。 |
$loop->first |
これがループの最初の反復処理であるかどうか。 |
$loop->last |
これがループの最後の反復処理であるかどうか。 |
$loop->even |
これがループの偶数回目の反復処理であるかどうか。 |
$loop->odd |
これがループの奇数回目の反復処理であるかどうか。 |
$loop->depth |
現在のループのネストレベル。 |
$loop->parent |
ネストされたループ内にある場合、親のループ変数。 |
条件付きクラスとスタイル
@classディレクティブは、条件付きでCSSクラス文字列をコンパイルします。このディレクティブは、配列キーに追加したいクラスを含み、値がブール式であるクラスの配列を受け入れます。配列要素が数値キーを持つ場合、それは常にレンダーされるクラスリストに含まれます。
1@php 2 $isActive = false; 3 $hasError = true; 4@endphp 5 6<span @class([ 7 'p-4', 8 'font-bold' => $isActive, 9 'text-gray-500' => ! $isActive,10 'bg-red' => $hasError,11])></span>12 13<span class="p-4 text-gray-500 bg-red"></span>
同様に、@styleディレクティブを使用して、HTML要素にインラインCSSスタイルを条件付きで追加できます。
1@php 2 $isActive = true; 3@endphp 4 5<span @style([ 6 'background-color: red', 7 'font-weight: bold' => $isActive, 8])></span> 9 10<span style="background-color: red; font-weight: bold;"></span>
追加の属性
利便性のために、@checkedディレクティブを使用して、特定のHTMLチェックボックス入力が「チェックされている」かどうかを簡単に示すことができます。このディレクティブは、指定された条件がtrueに評価された場合にcheckedをエコーします。
1<input2 type="checkbox"3 name="active"4 value="active"5 @checked(old('active', $user->active))6/>
同様に、@selectedディレクティブを使用して、特定のselectオプションが「選択」されるべきかを示すことができます。
1<select name="version">2 @foreach ($product->versions as $version)3 <option value="{{ $version }}" @selected(old('version') == $version)>4 {{ $version }}5 </option>6 @endforeach7</select>
さらに、@disabledディレクティブを使用して、特定の要素を「無効」にするかどうかを示すことができます。
1<button type="submit" @disabled($errors->isNotEmpty())>Submit</button>
さらに、@readonlyディレクティブを使用して、特定の要素が「読み取り専用」であるべきかを示すことができます。
1<input2 type="email"3 name="email"5 @readonly($user->isNotAdmin())6/>
さらに、@requiredディレクティブを使用して、特定の要素が「必須」であるべきかを示すことができます。
1<input2 type="text"3 name="title"4 value="title"5 @required($user->isAdmin())6/>
サブビューのインクルード
@includeディレクティブを自由に使用できますが、Bladeのコンポーネントは同様の機能を提供し、データや属性のバインディングなど、@includeディレクティブに比べていくつかの利点があります。
Bladeの@includeディレクティブを使用すると、別のビュー内からBladeビューをインクルードできます。親ビューで利用可能なすべての変数は、インクルードされたビューでも利用可能になります。
1<div>2 @include('shared.errors')3 4 <form>5 <!-- Form Contents -->6 </form>7</div>
インクルードされたビューは、親ビューで利用可能なすべてのデータを継承しますが、インクルードされたビューで利用可能にすべき追加データの配列を渡すこともできます。
1@include('view.name', ['status' => 'complete'])
存在しないビューを@includeしようとすると、Laravelはエラーをスローします。存在するかもしれないし、しないかもしれないビューをインクルードしたい場合は、@includeIfディレクティブを使用する必要があります。
1@includeIf('view.name', ['status' => 'complete'])
特定のブール式がtrueまたはfalseに評価された場合にビューを@includeしたい場合は、@includeWhenおよび@includeUnlessディレクティブを使用できます。
1@includeWhen($boolean, 'view.name', ['status' => 'complete'])2 3@includeUnless($boolean, 'view.name', ['status' => 'complete'])
指定したビューの配列から存在する最初のビューをインクルードするには、includeFirstディレクティブを使用します。
1@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
Bladeビューで__DIR__および__FILE__定数を使用するのは避けるべきです。これらはキャッシュされたコンパイル済みビューの場所を参照するためです。
コレクションのビューをレンダー
Bladeの@eachディレクティブを使用すると、ループとインクルードを一行にまとめることができます。
1@each('view.name', $jobs, 'job')
@eachディレクティブの最初の引数は、配列またはコレクションの各要素に対してレンダーするビューです。2番目の引数は、反復処理する配列またはコレクションです。3番目の引数は、ビュー内で現在の反復処理に割り当てられる変数名です。たとえば、jobsの配列を反復処理する場合、通常はビュー内で各ジョブにjob変数としてアクセスします。現在の反復処理の配列キーは、ビュー内でkey変数として利用できます。
@eachディレクティブに4番目の引数を渡すこともできます。この引数は、指定した配列が空の場合にレンダーされるビューを決定します。
1@each('view.name', $jobs, 'job', 'view.empty')
@eachを介してレンダーされるビューは、親ビューから変数を継承しません。子ビューがこれらの変数を必要とする場合は、代わりに@foreachと@includeディレクティブを使用する必要があります。
@onceディレクティブ
@onceディレクティブを使用すると、レンダーサイクルごとに一度だけ評価されるテンプレートの一部を定義できます。これは、スタックを使用して、特定のJavaScriptをページのヘッダーにプッシュする場合に役立ちます。たとえば、ループ内で特定のコンポーネントをレンダーする場合、コンポーネントが最初にレンダーされるときにのみJavaScriptをヘッダーにプッシュしたい場合があります。
1@once2 @push('scripts')3 <script>4 // Your custom JavaScript...5 </script>6 @endpush7@endonce
@onceディレクティブは@pushまたは@prependディレクティブと組み合わせて使用されることが多いため、利便性のために@pushOnceおよび@prependOnceディレクティブが利用できます。
1@pushOnce('scripts')2 <script>3 // Your custom JavaScript...4 </script>5@endPushOnce
PHPの直接使用
状況によっては、ビューにPHPコードを埋め込むと便利です。Bladeの@phpディレクティブを使用して、テンプレート内で素のPHPのブロックを実行できます。
1@php2 $counter = 1;3@endphp
または、クラスをインポートするためにPHPを使用するだけの場合は、@useディレクティブを使用できます。
1@use('App\Models\Flight')
@useディレクティブには、インポートしたクラスに別名を付けるための第2引数を指定できます。
1@use('App\Models\Flight', 'FlightModel')
コメント
Bladeでは、ビューにコメントを定義することもできます。ただし、HTMLコメントとは異なり、Bladeコメントはアプリケーションから返されるHTMLに含まれません。
1{{-- This comment will not be present in the rendered HTML --}}
コンポーネント
コンポーネントとスロットは、セクション、レイアウト、インクルードと同様の利点を提供します。しかし、コンポーネントとスロットのメンタルモデルの方が理解しやすいと感じる人もいるでしょう。コンポーネントを作成するには、クラスベースのコンポーネントと無名コンポーネントの2つのアプローチがあります。
クラスベースのコンポーネントを作成するには、make:component Artisanコマンドを使用できます。コンポーネントの使用方法を説明するために、簡単なAlertコンポーネントを作成します。make:componentコマンドは、コンポーネントをapp/View/Componentsディレクトリに配置します。
1php artisan make:component Alert
make:componentコマンドは、コンポーネントのビューテンプレートも作成します。ビューはresources/views/componentsディレクトリに配置されます。独自のアプリケーション用にコンポーネントを作成する場合、コンポーネENTはapp/View/Componentsディレクトリとresources/views/componentsディレクトリ内で自動的に検出されるため、通常はこれ以上のコンポーネント登録は必要ありません。
サブディレクトリ内にコンポーネントを作成することもできます。
1php artisan make:component Forms/Input
上記のコマンドは、app/View/Components/FormsディレクトリにInputコンポーネントを作成し、ビューはresources/views/components/formsディレクトリに配置されます。
無名コンポーネント(Bladeテンプレートのみでクラスがないコンポーネント)を作成したい場合は、make:componentコマンドを呼び出す際に--viewフラグを使用できます。
1php artisan make:component forms.input --view
上記のコマンドはresources/views/components/forms/input.blade.phpにBladeファイルを作成し、これは<x-forms.input />を介してコンポーネントとしてレンダーできます。
パッケージコンポーネントの手動登録
独自のアプリケーションのコンポーネントを作成する場合、コンポーネントはapp/View/Componentsディレクトリとresources/views/componentsディレクトリ内で自動的に検出されます。
ただし、Bladeコンポーネントを利用するパッケージを構築している場合は、コンポーネントクラスとそのHTMLタグエイリアスを手動で登録する必要があります。通常、コンポーネントはパッケージのサービスプロバイダのbootメソッドで登録します。
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::component('package-alert', Alert::class);9}
コンポーネントが登録されると、そのタグエイリアスを使用してレンダーできます。
1<x-package-alert/>
あるいは、componentNamespaceメソッドを使用して、規約に基づいてコンポーネントクラスをオートロードすることもできます。たとえば、Nightshadeパッケージには、Package\Views\Components名前空間内に存在するCalendarおよびColorPickerコンポーネントがあるかもしれません。
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
これにより、package-name::構文を使用して、ベンダー名前空間によるパッケージコンポーネントの使用が可能になります。
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Bladeは、コンポーネント名をパスカルケースにすることで、このコンポーネントにリンクされているクラスを自動的に検出します。サブディレクトリも「ドット」記法を使用してサポートされています。
コンポーネントのレンダー
コンポーネントを表示するには、Bladeテンプレート内でBladeコンポーネントタグを使用します。Bladeコンポーネントタグは、文字列x-で始まり、その後にコンポーネントクラスのケバブケース名が続きます。
1<x-alert/>2 3<x-user-profile/>
コンポーネントクラスがapp/View/Componentsディレクトリのより深い階層にある場合は、.文字を使用してディレクトリのネストを示すことができます。たとえば、コンポーネントがapp/View/Components/Inputs/Button.phpにあると仮定すると、次のようにレンダーできます。
1<x-inputs.button/>
コンポーネントを条件付きでレンダーしたい場合は、コンポーネントクラスにshouldRenderメソッドを定義できます。shouldRenderメソッドがfalseを返した場合、コンポーネントはレンダーされません。
1use Illuminate\Support\Str;2 3/**4 * Whether the component should be rendered5 */6public function shouldRender(): bool7{8 return Str::length($this->message) > 0;9}
インデックスコンポーネント
コンポーネントはコンポーネントグループの一部である場合があり、関連するコンポーネントを単一のディレクトリにグループ化したい場合があります。たとえば、次のようなクラス構造を持つ「カード」コンポーネントを想像してください。
1App\Views\Components\Card\Card2App\Views\Components\Card\Header3App\Views\Components\Card\Body
ルートのCardコンポーネントはCardディレクトリ内にネストされているため、<x-card.card>を介してコンポーネントをレンダーする必要があると予想されるかもしれません。しかし、コンポーネントのファイル名がコンポーネントのディレクトリ名と一致する場合、Laravelは自動的にそのコンポーネントが「ルート」コンポーネントであるとみなし、ディレクトリ名を繰り返すことなくコンポーネントをレンダーできます。
1<x-card>2 <x-card.header>...</x-card.header>3 <x-card.body>...</x-card.body>4</x-card>
コンポーネントへのデータ受け渡し
HTML属性を使用してBladeコンポーネントにデータを渡すことができます。ハードコードされたプリミティブな値は、単純なHTML属性文字列を使用してコンポーネントに渡すことができます。PHP式や変数は、:文字をプレフィックスとして使用する属性を介してコンポーネントに渡す必要があります。
1<x-alert type="error" :message="$message"/>
コンポーネントのすべてのデータ属性は、そのクラスのコンストラクタで定義する必要があります。コンポーネントのすべてのパブリックプロパティは、自動的にコンポーネントのビューで利用可能になります。コンポーネントのrenderメソッドからビューにデータを渡す必要はありません。
1<?php 2 3namespace App\View\Components; 4 5use Illuminate\View\Component; 6use Illuminate\View\View; 7 8class Alert extends Component 9{10 /**11 * Create the component instance.12 */13 public function __construct(14 public string $type,15 public string $message,16 ) {}17 18 /**19 * Get the view / contents that represent the component.20 */21 public function render(): View22 {23 return view('components.alert');24 }25}
コンポーネントがレンダーされるときに、コンポーネントのパブリック変数の内容を、変数を名前でエコーすることで表示できます。
1<div class="alert alert-{{ $type }}">2 {{ $message }}3</div>
大文字小文字の区別
コンポーネントのコンストラクタ引数はcamelCaseで指定し、HTML属性で引数名を参照する場合はkebab-caseを使用する必要があります。たとえば、次のコンポーネントコンストラクタがあるとします。
1/**2 * Create the component instance.3 */4public function __construct(5 public string $alertType,6) {}
$alertType引数は、次のようにコンポーネントに提供できます。
1<x-alert alert-type="danger" />
短縮属性構文
コンポーネントに属性を渡すとき、「短縮属性」構文を使用することもできます。属性名は対応する変数名と一致することが多いため、これはしばしば便利です。
1{{-- Short attribute syntax... --}}2<x-profile :$userId :$name />3 4{{-- Is equivalent to... --}}5<x-profile :user-id="$userId" :name="$name" />
属性レンダーのエスケープ
Alpine.jsなどの一部のJavaScriptフレームワークもコロンで始まる属性を使用するため、二重コロン(::)プレフィックスを使用して、属性がPHP式ではないことをBladeに知らせることができます。たとえば、次のコンポーネントが与えられたとします。
1<x-button ::class="{ danger: isDeleting }">2 Submit3</x-button>
次のHTMLがBladeによってレンダーされます。
1<button :class="{ danger: isDeleting }">2 Submit3</button>
コンポーネントのメソッド
パブリック変数がコンポーネントテンプレートで利用できることに加えて、コンポーネント上の任意のパブリックメソッドを呼び出すことができます。たとえば、isSelectedメソッドを持つコンポーネントを想像してください。
1/**2 * Determine if the given option is the currently selected option.3 */4public function isSelected(string $option): bool5{6 return $option === $this->selected;7}
このメソッドをコンポーネントテンプレートから実行するには、メソッドの名前と一致する変数を呼び出します。
1<option {{ $isSelected($value) ? 'selected' : '' }} value="{{ $value }}">2 {{ $label }}3</option>
コンポーネントクラス内での属性とスロットへのアクセス
Bladeコンポーネントでは、クラスのrenderメソッド内でコンポーネント名、属性、スロットにアクセスすることもできます。ただし、このデータにアクセスするには、コンポーネントのrenderメソッドからクロージャを返す必要があります。
1use Closure; 2 3/** 4 * Get the view / contents that represent the component. 5 */ 6public function render(): Closure 7{ 8 return function () { 9 return '<div {{ $attributes }}>Components content</div>';10 };11}
コンポーネントのrenderメソッドによって返されるクロージャは、唯一の引数として$data配列を受け取ることもできます。この配列には、コンポーネントに関する情報を提供するいくつかの要素が含まれます。
1return function (array $data) {2 // $data['componentName'];3 // $data['attributes'];4 // $data['slot'];5 6 return '<div {{ $attributes }}>Components content</div>';7}
$data配列の要素は、renderメソッドによって返されるBlade文字列に直接埋め込むべきではありません。そうすると、悪意のある属性コンテンツを介してリモートでコードが実行される可能性があるためです。
componentNameは、x-プレフィックスの後のHTMLタグで使用される名前と同じです。したがって、<x-alert />のcomponentNameはalertになります。attributes要素には、HTMLタグに存在したすべての属性が含まれます。slot要素は、コンポーネントのスロットの内容を持つIlluminate\Support\HtmlStringインスタンスです。
クロージャは文字列を返す必要があります。返された文字列が既存のビューに対応する場合、そのビューがレンダーされます。それ以外の場合、返された文字列はインラインBladeビューとして評価されます。
追加の依存関係
コンポーネントがLaravelのサービスコンテナからの依存関係を必要とする場合は、コンポーネントのデータ属性の前にそれらをリストすることができ、コンテナによって自動的に注入されます。
1use App\Services\AlertCreator; 2 3/** 4 * Create the component instance. 5 */ 6public function __construct( 7 public AlertCreator $creator, 8 public string $type, 9 public string $message,10) {}
属性/メソッドの非表示
一部のパブリックメソッドやプロパティがコンポーネントテンプレートに変数として公開されるのを防ぎたい場合は、コンポーネントの$except配列プロパティにそれらを追加できます。
1<?php 2 3namespace App\View\Components; 4 5use Illuminate\View\Component; 6 7class Alert extends Component 8{ 9 /**10 * The properties / methods that should not be exposed to the component template.11 *12 * @var array13 */14 protected $except = ['type'];15 16 /**17 * Create the component instance.18 */19 public function __construct(20 public string $type,21 ) {}22}
コンポーネントの属性
コンポーネントにデータ属性を渡す方法についてはすでに説明しましたが、コンポーネントが機能するために必要なデータの一部ではない、classなどの追加のHTML属性を指定する必要がある場合があります。通常、これらの追加の属性をコンポーネントテンプレートのルート要素に渡したいと考えます。たとえば、alertコンポーネントを次のようにレンダーしたいとします。
1<x-alert type="error" :message="$message" class="mt-4"/>
コンポーネントのコンストラクタの一部ではないすべての属性は、自動的にコンポーネントの「属性バッグ」に追加されます。この属性バッグは、$attributes変数を介してコンポーネントで自動的に利用可能になります。この変数をエコーすることで、すべての属性をコンポーネント内でレンダーできます。
1<div {{ $attributes }}>2 <!-- Component content -->3</div>
現時点では、コンポーネントタグ内で@envなどのディレクティブを使用することはサポートされていません。たとえば、<x-alert :live="@env('production')"/>はコンパイルされません。
デフォルト/マージされた属性
属性のデフォルト値を指定したり、コンポーネントの属性に値を追加してマージしたりする必要がある場合があります。これを実現するには、属性バッグのmergeメソッドを使用できます。このメソッドは、コンポーネントに常に適用されるべきデフォルトのCSSクラスのセットを定義するのに特に役立ちます。
1<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>2 {{ $message }}3</div>
このコンポーネントが次のように利用されると仮定します。
1<x-alert type="error" :message="$message" class="mb-4"/>
コンポーネントの最終的にレンダーされたHTMLは、次のようになります。
1<div class="alert alert-error mb-4">2 <!-- Contents of the $message variable -->3</div>
クラスの条件付きマージ
特定の条件がtrueの場合にクラスをマージしたい場合があります。これはclassメソッドを介して実現できます。このメソッドは、配列キーに追加したいクラスを含み、値がブール式であるクラスの配列を受け入れます。配列要素に数値キーがある場合、それは常にレンダーされるクラスリストに含まれます。
1<div {{ $attributes->class(['p-4', 'bg-red' => $hasError]) }}>2 {{ $message }}3</div>
コンポーネントに他の属性をマージする必要がある場合は、mergeメソッドをclassメソッドにチェーンできます。
1<button {{ $attributes->class(['p-4'])->merge(['type' => 'button']) }}>2 {{ $slot }}3</button>
マージされた属性を受け取るべきではない他のHTML要素でクラスを条件付きでコンパイルする必要がある場合は、@classディレクティブを使用できます。
クラス以外の属性のマージ
class属性以外の属性をマージする場合、mergeメソッドに指定された値は属性の「デフォルト」値と見なされます。ただし、class属性とは異なり、これらの属性は注入された属性値とマージされません。代わりに、上書きされます。たとえば、buttonコンポーネントの実装は次のようになります。
1<button {{ $attributes->merge(['type' => 'button']) }}>2 {{ $slot }}3</button>
カスタムのtypeでボタンコンポーネントをレンダーするには、コンポーネントを使用するときに指定できます。typeが指定されていない場合は、buttonタイプが使用されます。
1<x-button type="submit">2 Submit3</x-button>
この例のbuttonコンポーネントのレンダーされたHTMLは次のようになります。
1<button type="submit">2 Submit3</button>
class以外の属性のデフォルト値と注入された値を結合したい場合は、prependsメソッドを使用できます。この例では、data-controller属性は常にprofile-controllerで始まり、追加で注入されたdata-controller値はこのデフォルト値の後に配置されます。
1<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>2 {{ $slot }}3</div>
属性の取得とフィルタリング
filterメソッドを使用して属性をフィルタリングできます。このメソッドは、属性バッグに属性を保持したい場合にtrueを返すクロージャを受け入れます。
1{{ $attributes->filter(fn (string $value, string $key) => $key == 'foo') }}
利便性のために、whereStartsWithメソッドを使用して、キーが特定の文字列で始まるすべての属性を取得できます。
1{{ $attributes->whereStartsWith('wire:model') }}
逆に、whereDoesntStartWithメソッドを使用して、キーが特定の文字列で始まるすべての属性を除外できます。
1{{ $attributes->whereDoesntStartWith('wire:model') }}
firstメソッドを使用して、特定の属性バッグの最初の属性をレンダーできます。
1{{ $attributes->whereStartsWith('wire:model')->first() }}
コンポーネントに属性が存在するかどうかを確認したい場合は、hasメソッドを使用できます。このメソッドは、属性名を唯一の引数として受け取り、属性が存在するかどうかを示すブール値を返します。
1@if ($attributes->has('class'))2 <div>Class attribute is present</div>3@endif
hasメソッドに配列が渡された場合、メソッドは指定されたすべての属性がコンポーネントに存在するかどうかを判断します。
1@if ($attributes->has(['name', 'class']))2 <div>All of the attributes are present</div>3@endif
hasAnyメソッドを使用して、指定された属性のいずれかがコンポーネントに存在するかどうかを判断できます。
1@if ($attributes->hasAny(['href', ':href', 'v-bind:href']))2 <div>One of the attributes is present</div>3@endif
getメソッドを使用して、特定の属性の値を取得できます。
1{{ $attributes->get('class') }}
予約済みキーワード
デフォルトでは、一部のキーワードはコンポーネントをレンダーするためにBladeの内部使用のために予約されています。次のキーワードは、コンポーネント内でパブリックプロパティまたはメソッド名として定義できません。
datarenderresolveViewshouldRenderviewwithAttributeswithName
スロット
コンポーネントに「スロット」を介して追加のコンテンツを渡す必要があることがよくあります。コンポーネントのスロットは、$slot変数をエコーすることでレンダーされます。この概念を探るために、alertコンポーネントが次のマークアップを持っていると想像してみましょう。
1<!-- /resources/views/components/alert.blade.php -->2 3<div class="alert alert-danger">4 {{ $slot }}5</div>
コンポーネントにコンテンツを注入することで、slotにコンテンツを渡すことができます。
1<x-alert>2 <strong>Whoops!</strong> Something went wrong!3</x-alert>
コンポーネントは、コンポーネント内のさまざまな場所に複数の異なるスロットをレンダーする必要がある場合があります。アラートコンポーネントを変更して、「タイトル」スロットの注入を許可するようにしましょう。
1<!-- /resources/views/components/alert.blade.php -->2 3<span class="alert-title">{{ $title }}</span>4 5<div class="alert alert-danger">6 {{ $slot }}7</div>
名前付きスロットのコンテンツは、x-slotタグを使用して定義できます。明示的なx-slotタグ内にないコンテンツは、$slot変数でコンポーネントに渡されます。
1<x-alert>2 <x-slot:title>3 Server Error4 </x-slot>5 6 <strong>Whoops!</strong> Something went wrong!7</x-alert>
スロットのisEmptyメソッドを呼び出して、スロットにコンテンツが含まれているかどうかを判断できます。
1<span class="alert-title">{{ $title }}</span>2 3<div class="alert alert-danger">4 @if ($slot->isEmpty())5 This is default content if the slot is empty.6 @else7 {{ $slot }}8 @endif9</div>
さらに、hasActualContentメソッドを使用して、スロットにHTMLコメントではない「実際の」コンテンツが含まれているかどうかを判断できます。
1@if ($slot->hasActualContent())2 The scope has non-comment content.3@endif
スコープ付きスロット
VueなどのJavaScriptフレームワークを使用したことがある場合は、「スコープ付きスロット」に精通しているかもしれません。これにより、スロット内でコンポーネントのデータやメソッドにアクセスできます。Laravelでも同様の動作を実現するには、コンポーネントでパブリックなメソッドやプロパティを定義し、スロット内で$component変数を介してコンポーネントにアクセスします。この例では、x-alertコンポーネントがコンポーネントクラスにパブリックなformatAlertメソッドが定義されていると仮定します。
1<x-alert>2 <x-slot:title>3 {{ $component->formatAlert('Server Error') }}4 </x-slot>5 6 <strong>Whoops!</strong> Something went wrong!7</x-alert>
スロットの属性
Bladeコンポーネントと同様に、CSSクラス名など、スロットに追加の属性を割り当てることができます。
1<x-card class="shadow-sm"> 2 <x-slot:heading class="font-bold"> 3 Heading 4 </x-slot> 5 6 Content 7 8 <x-slot:footer class="text-sm"> 9 Footer10 </x-slot>11</x-card>
スロット属性を操作するには、スロット変数のattributesプロパティにアクセスします。属性の操作方法の詳細については、コンポーネント属性に関するドキュメントを参照してください。
1@props([ 2 'heading', 3 'footer', 4]) 5 6<div {{ $attributes->class(['border']) }}> 7 <h1 {{ $heading->attributes->class(['text-lg']) }}> 8 {{ $heading }} 9 </h1>10 11 {{ $slot }}12 13 <footer {{ $footer->attributes->class(['text-gray-700']) }}>14 {{ $footer }}15 </footer>16</div>
インラインコンポーネントビュー
非常に小さなコンポーネントの場合、コンポーネントクラスとコンポーネントのビューテンプレートの両方を管理するのは面倒に感じることがあります。このため、renderメソッドからコンポーネントのマークアップを直接返すことができます。
1/** 2 * Get the view / contents that represent the component. 3 */ 4public function render(): string 5{ 6 return <<<'blade' 7 <div class="alert alert-danger"> 8 {{ $slot }} 9 </div>10 blade;11}
インラインビューコンポーネントの生成
インラインビューをレンダーするコンポーネントを作成するには、make:componentコマンドを実行するときにinlineオプションを使用できます。
1php artisan make:component Alert --inline
動的コンポーネント
コンポーネントをレンダーする必要があるが、実行時までどのコンポーネントをレンダーすべきかわからない場合があります。このような状況では、Laravelの組み込みdynamic-componentコンポーネントを使用して、実行時の値や変数に基づいてコンポーネントをレンダーできます。
1// $componentName = "secondary-button";2 3<x-dynamic-component :component="$componentName" class="mt-4" />
コンポーネントの手動登録
コンポーネントの手動登録に関する以下のドキュメントは、主にビューコンポーネントを含むLaravelパッケージを作成している方を対象としています。パッケージを作成していない場合、コンポーネントのドキュメントのこの部分は関係ない可能性があります。
独自のアプリケーションのコンポーネントを作成する場合、コンポーネントはapp/View/Componentsディレクトリとresources/views/componentsディレクトリ内で自動的に検出されます。
しかし、Bladeコンポーネントを利用するパッケージを構築している場合、またはコンポーネントを非標準のディレクトリに配置している場合は、コンポーネントクラスとそのHTMLタグエイリアスを手動で登録して、Laravelがコンポーネントを見つけられるようにする必要があります。通常、コンポーネントはパッケージのサービスプロバイダのbootメソッドで登録します。
1use Illuminate\Support\Facades\Blade; 2use VendorPackage\View\Components\AlertComponent; 3 4/** 5 * Bootstrap your package's services. 6 */ 7public function boot(): void 8{ 9 Blade::component('package-alert', AlertComponent::class);10}
コンポーネントが登録されると、そのタグエイリアスを使用してレンダーできます。
1<x-package-alert/>
パッケージコンポーネントのオートロード
あるいは、componentNamespaceメソッドを使用して、規約に基づいてコンポーネントクラスをオートロードすることもできます。たとえば、Nightshadeパッケージには、Package\Views\Components名前空間内に存在するCalendarおよびColorPickerコンポーネントがあるかもしれません。
1use Illuminate\Support\Facades\Blade;2 3/**4 * Bootstrap your package's services.5 */6public function boot(): void7{8 Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');9}
これにより、package-name::構文を使用して、ベンダー名前空間によるパッケージコンポーネントの使用が可能になります。
1<x-nightshade::calendar />2<x-nightshade::color-picker />
Bladeは、コンポーネント名をパスカルケースにすることで、このコンポーネントにリンクされているクラスを自動的に検出します。サブディレクトリも「ドット」記法を使用してサポートされています。
無名コンポーネント
インラインコンポーネントと同様に、無名コンポーネントは単一のファイルを介してコンポーネントを管理するメカニズムを提供します。ただし、無名コンポーネントは単一のビューファイルを利用し、関連するクラスはありません。無名コンポーネントを定義するには、resources/views/componentsディレクトリ内にBladeテンプレートを配置するだけです。たとえば、resources/views/components/alert.blade.phpでコンポーネントを定義したと仮定すると、次のように簡単にレンダーできます。
1<x-alert/>
コンポーネントがcomponentsディレクトリのより深い階層にネストされている場合は、.文字を使用して示すことができます。たとえば、コンポーネントがresources/views/components/inputs/button.blade.phpで定義されていると仮定すると、次のようにレンダーできます。
1<x-inputs.button/>
無名インデックスコンポーネント
コンポーネントが多くのBladeテンプレートで構成されている場合、特定のコンポーネントのテンプレートを単一のディレクトリにグループ化したいことがあります。たとえば、次のようなディレクトリ構造を持つ「アコーディオン」コンポーネントを想像してください。
1/resources/views/components/accordion.blade.php2/resources/views/components/accordion/item.blade.php
このディレクトリ構造により、アコーディオンコンポーネントとそのアイテムを次のようにレンダーできます。
1<x-accordion>2 <x-accordion.item>3 ...4 </x-accordion.item>5</x-accordion>
しかし、x-accordionを介してアコーディオンコンポーネントをレンダーするためには、「インデックス」アコーディオンコンポーネントテンプレートを、他のアコーディオン関連テンプレートと一緒にaccordionディレクトリにネストするのではなく、resources/views/componentsディレクトリに配置する必要がありました。
幸いなことに、Bladeではコンポーネントのディレクトリ名と一致するファイルをコンポーネントのディレクトリ自体に配置することができます。このテンプレートが存在する場合、ディレクトリ内にネストされていてもコンポーネントの「ルート」要素としてレンダーできます。したがって、上記の例で指定したのと同じBlade構文を引き続き使用できますが、ディレクトリ構造を次のように調整します。
1/resources/views/components/accordion/accordion.blade.php2/resources/views/components/accordion/item.blade.php
データプロパティ/属性
無名コンポーネントには関連するクラスがないため、どのデータを変数としてコンポーネントに渡すべきか、どの属性をコンポーネントの属性バッグに配置すべきかをどのように区別するのか疑問に思うかもしれません。
コンポーネントのBladeテンプレートの先頭で@propsディレクティブを使用して、どの属性をデータ変数と見なすべきかを指定できます。コンポーネントの他のすべての属性は、コンポーネントの属性バッグを介して利用可能になります。データ変数にデフォルト値を与えたい場合は、変数名を配列キーとして、デフォルト値を配列値として指定できます。
1<!-- /resources/views/components/alert.blade.php -->2 3@props(['type' => 'info', 'message'])4 5<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>6 {{ $message }}7</div>
上記のコンポーネント定義を考えると、次のようにコンポーネントをレンダーできます。
1<x-alert type="error" :message="$message" class="mb-4"/>
親データへのアクセス
親コンポーネントのデータに子コンポーネント内でアクセスしたい場合があります。このような場合、@awareディレクティブを使用できます。たとえば、親の<x-menu>と子の<x-menu.item>からなる複雑なメニューコンポーネントを構築しているとします。
1<x-menu color="purple">2 <x-menu.item>...</x-menu.item>3 <x-menu.item>...</x-menu.item>4</x-menu>
<x-menu>コンポーネントは、次のような実装を持つかもしれません。
1<!-- /resources/views/components/menu/index.blade.php -->2 3@props(['color' => 'gray'])4 5<ul {{ $attributes->merge(['class' => 'bg-'.$color.'-200']) }}>6 {{ $slot }}7</ul>
colorプロパティは親(<x-menu>)にのみ渡されたため、<x-menu.item>内では利用できません。しかし、@awareディレクティブを使用すると、<x-menu.item>内でも利用できるようになります。
1<!-- /resources/views/components/menu/item.blade.php -->2 3@aware(['color' => 'gray'])4 5<li {{ $attributes->merge(['class' => 'text-'.$color.'-800']) }}>6 {{ $slot }}7</li>
@awareディレクティブは、HTML属性を介して親コンポーネントに明示的に渡されていない親データにはアクセスできません。親コンポーネントに明示的に渡されていないデフォルトの@props値は、@awareディレクティブではアクセスできません。
無名コンポーネントのパス
前述のように、無名コンポーネントは通常、resources/views/componentsディレクトリにBladeテンプレートを配置することで定義されます。ただし、デフォルトのパスに加えて、他の無名コンポーネントパスをLaravelに登録したい場合があります。
anonymousComponentPathメソッドは、最初の引数として無名コンポーネントの場所への「パス」を、2番目の引数としてコンポーネントを配置すべきオプションの「名前空間」を受け入れます。通常、このメソッドはアプリケーションのサービスプロバイダのいずれかのbootメソッドから呼び出す必要があります。
1/**2 * Bootstrap any application services.3 */4public function boot(): void5{6 Blade::anonymousComponentPath(__DIR__.'/../components');7}
上記の例のようにプレフィックスを指定せずにコンポーネントパスを登録した場合、Bladeコンポーネント内で対応するプレフィックスなしでレンダーできます。たとえば、上記で登録されたパスにpanel.blade.phpコンポーネントが存在する場合、次のようにレンダーできます。
1<x-panel />
プレフィックス「名前空間」は、anonymousComponentPathメソッドの第2引数として提供できます。
1Blade::anonymousComponentPath(__DIR__.'/../components', 'dashboard');
プレフィックスが指定されている場合、その「名前空間」内のコンポーネントは、コンポーネントをレンダーする際にコンポーネント名にコンポーネントの名前空間をプレフィックスとして付けてレンダーできます。
1<x-dashboard::panel />
レイアウトの構築
コンポーネントを使用したレイアウト
ほとんどのWebアプリケーションは、さまざまなページで同じ一般的なレイアウトを維持します。作成するすべてのビューでレイアウト全体のHTMLを繰り返さなければならないとしたら、アプリケーションの維持は非常に面倒で困難になります。幸い、このレイアウトを単一のBladeコンポーネントとして定義し、アプリケーション全体で使用するのは便利です。
レイアウトコンポーネントの定義
たとえば、「ToDo」リストアプリケーションを構築しているとします。次のようなlayoutコンポーネントを定義するかもしれません。
1<!-- resources/views/components/layout.blade.php --> 2 3<html> 4 <head> 5 <title>{{ $title ?? 'Todo Manager' }}</title> 6 </head> 7 <body> 8 <h1>Todos</h1> 9 <hr/>10 {{ $slot }}11 </body>12</html>
レイアウトコンポーネントの適用
layoutコンポーネントが定義されたら、そのコンポーネントを利用するBladeビューを作成できます。この例では、タスクリストを表示する単純なビューを定義します。
1<!-- resources/views/tasks.blade.php -->2 3<x-layout>4 @foreach ($tasks as $task)5 <div>{{ $task }}</div>6 @endforeach7</x-layout>
コンポーネントに注入されたコンテンツは、layoutコンポーネント内のデフォルトの$slot変数に提供されることを覚えておいてください。お気づきかもしれませんが、私たちのlayoutは、提供されていれば$titleスロットも尊重します。そうでなければ、デフォルトのタイトルが表示されます。コンポーネントのドキュメントで説明されている標準のスロット構文を使用して、タスクリストビューからカスタムタイトルを注入できます。
1<!-- resources/views/tasks.blade.php --> 2 3<x-layout> 4 <x-slot:title> 5 Custom Title 6 </x-slot> 7 8 @foreach ($tasks as $task) 9 <div>{{ $task }}</div>10 @endforeach11</x-layout>
レイアウトとタスクリストのビューを定義したので、あとはルートからtaskビューを返すだけです。
1use App\Models\Task;2 3Route::get('/tasks', function () {4 return view('tasks', ['tasks' => Task::all()]);5});
テンプレート継承を使用したレイアウト
レイアウトの定義
レイアウトは「テンプレート継承」によって作成することもできます。これは、コンポーネントが導入される前のアプリケーション構築の主要な方法でした。
まず、簡単な例を見てみましょう。最初に、ページレイアウトを調べます。ほとんどのWebアプリケーションは、さまざまなページで同じ一般的なレイアウトを維持するため、このレイアウトを単一のBladeビューとして定義すると便利です。
1<!-- resources/views/layouts/app.blade.php --> 2 3<html> 4 <head> 5 <title>App Name - @yield('title')</title> 6 </head> 7 <body> 8 @section('sidebar') 9 This is the master sidebar.10 @show11 12 <div class="container">13 @yield('content')14 </div>15 </body>16</html>
ご覧のとおり、このファイルには一般的なHTMLマークアップが含まれています。ただし、@sectionおよび@yieldディレクティブに注意してください。@sectionディレクティブは、その名の通りコンテンツのセクションを定義し、@yieldディレクティブは特定のセクションのコンテンツを表示するために使用されます。
アプリケーションのレイアウトを定義したので、レイアウトを継承する子ページを定義しましょう。
レイアウトの拡張
子ビューを定義するときは、@extends Bladeディレクティブを使用して、子ビューが「継承」すべきレイアウトを指定します。Bladeレイアウトを拡張するビューは、@sectionディレクティブを使用してレイアウトのセクションにコンテンツを注入できます。上記の例で見たように、これらのセクションの内容は@yieldを使用してレイアウトに表示されることを覚えておいてください。
1<!-- resources/views/child.blade.php --> 2 3@extends('layouts.app') 4 5@section('title', 'Page Title') 6 7@section('sidebar') 8 @@parent 9 10 <p>This is appended to the master sidebar.</p>11@endsection12 13@section('content')14 <p>This is my body content.</p>15@endsection
この例では、sidebarセクションは@@parentディレクティブを利用して、レイアウトのサイドバーにコンテンツを(上書きするのではなく)追加しています。@@parentディレクティブは、ビューがレンダーされるときにレイアウトのコンテンツに置き換えられます。
前の例とは対照的に、このsidebarセクションは@showではなく@endsectionで終わります。@endsectionディレクティブはセクションを定義するだけであり、@showはセクションを定義して即座にyieldします。
@yieldディレクティブは、第2パラメータとしてデフォルト値も受け入れます。この値は、yieldされるセクションが未定義の場合にレンダーされます。
1@yield('content', 'Default content')
フォーム
CSRFフィールド
アプリケーションでHTMLフォームを定義するときはいつでも、フォームに非表示のCSRFトークンフィールドを含めて、CSRF保護ミドルウェアがリクエストを検証できるようにする必要があります。@csrf Bladeディレクティブを使用してトークンフィールドを生成できます。
1<form method="POST" action="/profile">2 @csrf3 4 ...5</form>
メソッドフィールド
HTMLフォームはPUT、PATCH、またはDELETEリクエストを作成できないため、これらのHTTP動詞を偽装するために非表示の_methodフィールドを追加する必要があります。@method Bladeディレクティブは、このフィールドをあなたのために作成できます。
1<form action="/foo/bar" method="POST">2 @method('PUT')3 4 ...5</form>
バリデーションエラー
@errorディレクティブは、特定の属性に対するバリデーションエラーメッセージが存在するかどうかをすばやく確認するために使用できます。@errorディレクティブ内で、$message変数をエコーしてエラーメッセージを表示できます。
1<!-- /resources/views/post/create.blade.php --> 2 3<label for="title">Post Title</label> 4 5<input 6 id="title" 7 type="text" 8 class="@error('title') is-invalid @enderror" 9/>10 11@error('title')12 <div class="alert alert-danger">{{ $message }}</div>13@enderror
@errorディレクティブは「if」文にコンパイルされるため、属性にエラーがない場合にコンテンツをレンダーするために@elseディレクティブを使用できます。
1<!-- /resources/views/auth.blade.php -->2 3<label for="email">Email address</label>4 5<input6 id="email"7 type="email"8 class="@error('email') is-invalid @else is-valid @enderror"9/>
複数のフォームを含むページでバリデーションエラーメッセージを取得するには、@errorディレクティブの2番目のパラメータとして特定のエラーバッグの名前を渡すことができます。
1<!-- /resources/views/auth.blade.php --> 2 3<label for="email">Email address</label> 4 5<input 6 id="email" 7 type="email" 8 class="@error('email', 'login') is-invalid @enderror" 9/>10 11@error('email', 'login')12 <div class="alert alert-danger">{{ $message }}</div>13@enderror
スタック
Bladeでは、名前付きスタックにプッシュでき、それを別のビューやレイアウトのどこかでレンダーできます。これは、子ビューで必要なJavaScriptライブラリを指定するのに特に役立ちます。
1@push('scripts')2 <script src="/example.js"></script>3@endpush
特定のブール式がtrueに評価された場合にコンテンツを@pushしたい場合は、@pushIfディレクティブを使用できます。
1@pushIf($shouldPush, 'scripts')2 <script src="/example.js"></script>3@endPushIf
必要なだけ何度でもスタックにプッシュできます。スタックの完全な内容をレンダーするには、スタックの名前を@stackディレクティブに渡します。
1<head>2 <!-- Head Contents -->3 4 @stack('scripts')5</head>
スタックの先頭にコンテンツを追加したい場合は、@prependディレクティブを使用する必要があります。
1@push('scripts')2 This will be second...3@endpush4 5// Later...6 7@prepend('scripts')8 This will be first...9@endprepend
サービス注入
@injectディレクティブは、Laravel サービスコンテナからサービスを取得するために使用できます。@injectに渡される最初の引数は、サービスが配置される変数の名前で、2番目の引数は解決したいサービスのクラスまたはインターフェイス名です。
1@inject('metrics', 'App\Services\MetricsService')2 3<div>4 Monthly Revenue: {{ $metrics->monthlyRevenue() }}.5</div>
インラインBladeテンプレートのレンダー
生のBladeテンプレート文字列を有効なHTMLに変換する必要がある場合があります。これは、Bladeファサードが提供するrenderメソッドを使用して実現できます。renderメソッドは、Bladeテンプレート文字列と、テンプレートに提供するオプションのデータ配列を受け入れます。
1use Illuminate\Support\Facades\Blade;2 3return Blade::render('Hello, {{ $name }}', ['name' => 'Julian Bashir']);
Laravelは、インラインBladeテンプレートをstorage/framework/viewsディレクトリに書き込むことでレンダーします。Bladeテンプレートをレンダーした後にLaravelにこれらの一時ファイルを削除させたい場合は、メソッドにdeleteCachedView引数を指定できます。
1return Blade::render(2 'Hello, {{ $name }}',3 ['name' => 'Julian Bashir'],4 deleteCachedView: true5);
Bladeフラグメントのレンダー
Turboやhtmxのようなフロントエンドフレームワークを使用する場合、HTTPレスポンス内でBladeテンプレートの一部のみを返す必要がある場合があります。Bladeの「フラグメント」は、まさにそれを可能にします。まず、Bladeテンプレートの一部を@fragmentと@endfragmentディレクティブ内に配置します。
1@fragment('user-list')2 <ul>3 @foreach ($users as $user)4 <li>{{ $user->name }}</li>5 @endforeach6 </ul>7@endfragment
次に、このテンプレートを利用するビューをレンダーするときに、fragmentメソッドを呼び出して、指定されたフラグメントのみが送信HTTPレスポンスに含まれるように指定できます。
1return view('dashboard', ['users' => $users])->fragment('user-list');
fragmentIfメソッドを使用すると、特定の条件に基づいてビューのフラグメントを条件付きで返すことができます。それ以外の場合は、ビュー全体が返されます。
1return view('dashboard', ['users' => $users])2 ->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
fragmentsおよびfragmentsIfメソッドを使用すると、レスポンスで複数のビューフラグメントを返すことができます。フラグメントは連結されます。
1view('dashboard', ['users' => $users])2 ->fragments(['user-list', 'comment-list']);3 4view('dashboard', ['users' => $users])5 ->fragmentsIf(6 $request->hasHeader('HX-Request'),7 ['user-list', 'comment-list']8 );
Bladeの拡張
Bladeでは、directiveメソッドを使用して独自のカスタムディレクティブを定義できます。Bladeコンパイラがカスタムディレクティブを検出すると、ディレクティブに含まれる式とともに指定されたコールバックを呼び出します。
次の例では、指定された$var(DateTimeのインスタンスであるべき)をフォーマットする@datetime($var)ディレクティブを作成します。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\Facades\Blade; 6use Illuminate\Support\ServiceProvider; 7 8class AppServiceProvider extends ServiceProvider 9{10 /**11 * Register any application services.12 */13 public function register(): void14 {15 // ...16 }17 18 /**19 * Bootstrap any application services.20 */21 public function boot(): void22 {23 Blade::directive('datetime', function (string $expression) {24 return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";25 });26 }27}
ご覧のとおり、ディレクティブに渡された式にformatメソッドをチェーンします。したがって、この例では、このディレクティブによって生成される最終的なPHPは次のようになります。
1<?php echo ($var)->format('m/d/Y H:i'); ?>
Bladeディレクティブのロジックを更新した後は、キャッシュされたすべてのBladeビューを削除する必要があります。キャッシュされたBladeビューは、view:clear Artisanコマンドを使用して削除できます。
カスタムechoハンドラ
Bladeを使用してオブジェクトを「echo」しようとすると、オブジェクトの__toStringメソッドが呼び出されます。__toStringメソッドは、PHPの組み込み「マジックメソッド」の1つです。しかし、やり取りしているクラスがサードパーティのライブラリに属している場合など、特定のクラスの__toStringメソッドを制御できない場合があります。
このような場合、Bladeではその特定のオブジェクトタイプに対するカスタムechoハンドラを登録できます。これを実現するには、Bladeのstringableメソッドを呼び出す必要があります。stringableメソッドはクロージャを受け入れます。このクロージャは、レンダーを担当するオブジェクトのタイプを型ヒントで指定する必要があります。通常、stringableメソッドはアプリケーションのAppServiceProviderクラスのbootメソッド内で呼び出されるべきです。
1use Illuminate\Support\Facades\Blade; 2use Money\Money; 3 4/** 5 * Bootstrap any application services. 6 */ 7public function boot(): void 8{ 9 Blade::stringable(function (Money $money) {10 return $money->formatTo('en_GB');11 });12}
カスタムechoハンドラが定義されたら、Bladeテンプレートでオブジェクトを単にechoすることができます。
1Cost: {{ $money }}
カスタムif文
単純なカスタム条件文を定義する場合、カスタムディレクティブのプログラミングは必要以上に複雑になることがあります。そのため、BladeはBlade::ifメソッドを提供しており、クロージャを使用してカスタム条件ディレクティブを迅速に定義できます。たとえば、アプリケーションで設定されたデフォルトの「ディスク」をチェックするカスタム条件を定義しましょう。これは、AppServiceProviderのbootメソッドで行うことができます。
1use Illuminate\Support\Facades\Blade; 2 3/** 4 * Bootstrap any application services. 5 */ 6public function boot(): void 7{ 8 Blade::if('disk', function (string $value) { 9 return config('filesystems.default') === $value;10 });11}
カスタム条件が定義されたら、テンプレート内で使用できます。
1@disk('local') 2 <!-- The application is using the local disk... --> 3@elsedisk('s3') 4 <!-- The application is using the s3 disk... --> 5@else 6 <!-- The application is using some other disk... --> 7@enddisk 8 9@unlessdisk('local')10 <!-- The application is not using the local disk... -->11@enddisk