イントロダクション
Bladeは、Laravelに含まれているシンプルでありながら強力なテンプレートエンジンです。一部のPHPテンプレートエンジンとは異なり、BladeはテンプレートでプレーンなPHPコードの使用を制限しません。実際、すべてのBladeテンプレートはプレーンなPHPコードにコンパイルされ、変更されるまでキャッシュされます。つまり、Bladeはアプリケーションに実質的にオーバーヘッドをかけません。Bladeテンプレートファイルは.blade.php
ファイル拡張子を使用し、通常はresources/views
ディレクトリに保存します。
Bladeビューは、グローバルなview
ヘルパーを使用してルートまたはコントローラから返します。もちろん、viewsのドキュメントに記載されているように、データをview
ヘルパーの2番目の引数を使用してBladeビューに渡せます。
Route::get('/', function () {
return view('greeting', ['name' => 'Finn']);
});
Tip!! Bladeを深く掘り下げる前に、Laravelビュードキュメントを必ずお読みください。
データの表示
変数を中括弧で囲むことにより、Bladeビューに渡すデータを表示できます。たとえば、以下のルートがあるとします。
Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
次のように name
変数の内容を表示できます。
Hello, {{ $name }}.
Tip!! Bladeの
{{ }}
エコー文は、XSS攻撃を防ぐために、PHPのhtmlspecialchars
関数を通して自動的に送信します。
ビューに渡した変数の内容を表示するに留まりません。PHP関数の結果をエコーすることもできます。実際、Bladeエコーステートメント内に任意のPHPコードを入れることができます。
The current UNIX timestamp is {{ time() }}.
JSONのレンダー
JavaScript変数を初期化するために、配列をJSONとしてレンダリングする目的でビューに配列を渡す場合があります。一例をご確認ください。
<script>
var app = <?php echo json_encode($array); ?>;
</script>
ただし、手動でjson_encode
を呼び出す代わりに、@json
Bladeディレクティブが使用できます。@json
ディレクティブは、PHPのjson_encode
関数と同じ引数を取ります。デフォルトの@json
ディレクティブはJSON_HEX_TAG
、JSON_HEX_APOS
、JSON_HEX_AMP
、JSON_HEX_QUOT
フラグを使用してjson_encode
関数を呼び出します。
<script>
var app = @json($array);
var app = @json($array, JSON_PRETTY_PRINT);
</script>
Note: @jsonディレクティブは既存の変数をJSONとしてレンダするためだけに使用してください。Bladeテンプレートは正規表現ベースのため、複雑な式をディレクティブに渡すと予期しない不良動作の原因になります。
HTMLエンティティエンコーディング
デフォルトのBlade(およびLaravele
ヘルパー)はHTMLエンティティをダブルエンコードします。ダブルエンコーディングを無効にする場合は、AppServiceProvider
のboot
メソッドからBlade::withoutDoubleEncoding
メソッドを呼び出します。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 全アプリケーションサービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::withoutDoubleEncoding();
}
}
エスケープしないデータの表示
デフォルトのBlade {{ }}
文は、XSS攻撃を防ぐために、PHPのhtmlspecialchars
関数を通して自動的に送信します。データをエスケープしたくない場合は、次の構文を使用します。
Hello, {!! $name !!}.
Note: アプリケーションのユーザーによって提供されるコンテンツをエコーするときは十分に注意してください。ユーザーが入力したデータを表示するときはXSS攻撃を防ぐために、通常はエスケープする二重中括弧構文を使用してください。
BladeとJavaScriptフレームワーク
多くのJavaScriptフレームワークでも「中括弧」を使用して、特定の式をブラウザに表示することを示しているため、@
記号を使用して式をそのままにしておくように、Bladeレンダリングエンジンへ通知できます。例を確認してください。
<h1>Laravel</h1>
Hello, @{{ name }}.
この例の@
記号はBladeが削除します。そして、{{ name }}
式はBladeエンジンによって変更されないので、JavaScriptフレームワークでレンダリングできます。
@
記号は、Bladeディレクティブをエスケープするためにも使用できます。
{{-- Bladeテンプレート --}}
@@json()
<!-- HTML出力 -->
@json()
@verbatim
ディレクティブ
テンプレートの大部分でJavaScript変数を表示している場合は、HTMLを@verbatim
ディレクティブでラップして、各Bladeエコーステートメントの前に@
記号を付ける必要がないようにできます。
@verbatim
<div class="container">
Hello, {{ name }}.
</div>
@endverbatim
Bladeディレクティブ
テンプレートの継承とデータの表示に加えて、Bladeは条件ステートメントやループなど一般的なPHP制御構造への便利なショートカットも提供しています。こうしたショートカットは、PHP制御構造を操作するための非常にクリーンで簡潔な方法を提供する一方で、慣れ親しんだ同等のPHP構文も生かしています。
If文
@if
、@elseif
、@else
、@endif
ディレクティブを使用してif
ステートメントを作成できます。これらのディレクティブは、対応するPHPの構文と同じように機能します。
@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
使いやすいように、Bladeは@unless
ディレクティブも提供しています。
@unless (Auth::check())
You are not signed in.
@endunless
すでに説明した条件付きディレクティブに加えて、@isset
および@empty
ディレクティブをそれぞれのPHP関数の便利なショートカットとして使用できます。
@isset($records)
// $recordsが定義されており、nullでもない
@endisset
@empty($records)
// $recordsは空
@endempty
認証ディレクティブ
@auth
および@guest
ディレクティブを使用すると、現在のユーザーが認証済みであるか、ゲストであるかを簡潔に判断できます。
@auth
// ユーザーは認証済み
@endauth
@guest
// ユーザーは認証されていない
@endguest
必要に応じて、@auth
および@guest
ディレクティブを使用するときにチェックする必要がある認証ガードを指定できます。
@auth('admin')
// ユーザーは認証済み
@endauth
@guest('admin')
// ユーザーは認証されていない
@endguest
環境ディレクティブ
@production
ディレクティブを使用して、アプリケーションが本番環境で実行されているかを確認できます。
@production
// 本番環境だけのコンテンツ
@endproduction
または、@env
ディレクティブを使用して、アプリケーションが特定の環境で実行されているかどうかを判断できます。
@env('staging')
// アプリケーションは"staging"環境で動いている
@endenv
@env(['staging', 'production'])
// アプリケーションは"staging"か"production"環境で動いている
@endenv
セクションディレクティブ
@hasSection
ディレクティブを使用して、テンプレート継承セクションにコンテンツがあるかどうかを判断できます。
@hasSection('navigation')
<div class="pull-right">
@yield('navigation')
</div>
<div class="clearfix"></div>
@endif
sectionMissing
ディレクティブを使用して、セクションにコンテンツがないかどうかを判断できます。
@sectionMissing('navigation')
<div class="pull-right">
@include('default-navigation')
</div>
@endif
Switch文
Switchステートメントは、@switch
、@case
、@break
、@default
、@endswitch
ディレクティブを使用して作成できます。
@switch($i)
@case(1)
最初のケース
@break
@case(2)
2番目のケース
@break
@default
デフォルトケース
@endswitch
繰り返し
条件文に加えて、BladeはPHPのループ構造を操作するための簡単なディレクティブを提供します。繰り返しますが、これらの各ディレクティブは、対応するPHPと同じように機能します。
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach
@forelse ($users as $user)
<li>{{ $user->name }}</li>
@empty
<p>ユーザーが存在しない</p>
@endforelse
@while (true)
<p>無限にループ中。</p>
@endwhile
Tip!! ループするときは、ループ変数を使用して、ループの最初の反復か最後の反復かなど、ループに関する重要な情報を取得できます。
ループを使用する場合は、@continue
および@break
ディレクティブを使用して、ループを終了するか、現在の反復をスキップすることもできます。
@foreach ($users as $user)
@if ($user->type == 1)
@continue
@endif
<li>{{ $user->name }}</li>
@if ($user->number == 5)
@break
@endif
@endforeach
ディレクティブ宣言に継続条件または中断条件を含めることもできます。
@foreach ($users as $user)
@continue($user->type == 1)
<li>{{ $user->name }}</li>
@break($user->number == 5)
@endforeach
ループ変数
ループするとき、$loop
変数がループ内で利用可能になります。この変数は、現在のループインデックスや、これがループの最初または最後の反復であるかどうかなど、いくつかの有用な情報へのアクセスを提供します。
@foreach ($users as $user)
@if ($loop->first)
ここは最初の繰り返し
@endif
@if ($loop->last)
ここは最後の繰り返し
@endif
<p>This is user {{ $user->id }}</p>
@endforeach
ネストしたループ内にいる場合は、parent
プロパティを介して親ループの$loop
変数にアクセスできます。
@foreach ($users as $user)
@foreach ($user->posts as $post)
@if ($loop->parent->first)
ここは親ループの最初の繰り返し処理
@endif
@endforeach
@endforeach
$loop
変数は、他にもいろいろと便利なプロパティを持っています。
プロパティ | 説明 |
---|---|
$loop->index |
現在の反復のインデックス(初期値0) |
$loop->iteration |
現在の反復数(初期値1)。 |
$loop->remaining |
反復の残数。 |
$loop->count |
反復している配列の総アイテム数 |
$loop->first |
ループの最初の繰り返しか判定 |
$loop->last |
ループの最後の繰り返しか判定 |
$loop->even |
今回が偶数回目の繰り返しか判定 |
$loop->odd |
今回が奇数回目の繰り返しか判定 |
$loop->depth |
現在のループのネストレベル |
$loop->parent |
ループがネストしている場合、親のループ変数 |
コメント
Bladeでは、ビューにコメントを定義することもできます。ただし、HTMLコメントとは異なり、Bladeコメントはアプリケーションから返されるHTMLに含まれていません。
{{-- このコメントは、レンダーするHTMLで表示されません --}}
サブビューの読み込み
Tip!!
@include
ディレクティブは自由に使用できますが、Bladeコンポーネントは同様の機能を提供しつつ、データや属性のバインドなど@include
ディレクティブに比べていくつかの利点があります。
Bladeの@include
ディレクティブを使用すると、別のビュー内からBladeビューを読み込めます。親ビューで使用できるすべての変数は、読み込むビューで使用できます。
<div>
@include('shared.errors')
<form>
<!-- フォームのコンテンツ -->
</form>
</div>
読み込むビューは親ビューで使用可能なすべてのデータを継承しますが、読み込むビューで使用可能にする必要がある追加データの配列を渡すこともできます。
@include('view.name', ['status' => 'complete'])
存在しないビューを@include
しようとすると、Laravelはエラーを投げます。存在する場合と存在しない場合があるビューを読み込む場合は、@includeIf
ディレクティブを使用する必要があります。
@includeIf('view.name', ['status' => 'complete'])
指定する論理式がtrue
かfalse
と評価された場合に、ビューを@include
したい場合は、@includeWhen
および@includeUnless
ディレクティブを使用できます。
@includeWhen($boolean, 'view.name', ['status' => 'complete'])
@includeUnless($boolean, 'view.name', ['status' => 'complete'])
To include the first view that exists from a given array of views, you may use the includeFirst
directive: 指定するビュー配列中、存在する最初のビューを含めるには、includeFirst
ディレクティブを使用します。
@includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
Note: Bladeビューで
__DIR__
と__FILE__
定数は使用しないでください。これらは、キャッシュされコンパイルされたビューの場所を参照するためです。
コレクションのビューのレンダー
ループと読み込みをBladeの@each
ディレクティブで1行に組み合わせられます。
@each('view.name', $jobs, 'job')
@each
ディレクティブの最初の引数は、配列またはコレクション内の各要素に対してレンダーするビューです。2番目の引数は、反復する配列またはコレクションであり、3番目の引数は、ビュー内で現在の反復要素に割り当てる変数名です。したがって、たとえばjobs
配列を反復処理する場合、通常はビュー内でjob
変数として各ジョブにアクセスしたいと思います。現在の反復の配列キーは、ビュー内でkey
変数として使用できます。
@each
ディレクティブに4番目の引数を渡すこともできます。この引数は、指定された配列が空の場合にレンダーするビューを指定します。
@each('view.name', $jobs, 'job', 'view.empty')
Note:
@each
を使いレンダーするビューは、親ビューから変数を継承しません。子ビューでそうした変数が必要な場合は、代わりに@foreach
と@include
ディレクティブを使用する必要があります。
@once
ディレクティブ
@once
ディレクティブを使用すると、レンダリングサイクルごとに1回だけ評価されるテンプレートの部分を定義できます。これは、stacksを使用してJavaScriptの特定の部分をページのヘッダーへ入れ込むのに役立つでしょう。たとえば、ループ内で特定のコンポーネントをレンダーする場合に、コンポーネントが最初にレンダーされるときにのみ、あるJavaScriptをヘッダーに入れ込みたい場合があるとしましょう。
@once
@push('scripts')
<script>
// カスタムJavaScript…
</script>
@endpush
@endonce
レイアウト構築
コンポーネントを使用したレイアウト
ほとんどのWebアプリケーションは、さまざまなページで同じ共通のレイアウトを使用します。作成するすべてのビューでレイアウトHTML全体を繰り返し定義する必要があると、アプリケーションを維持するのは非常に面倒で困難です。こうしたレイアウトを単一のBladeコンポーネントとして定義し、アプリケーション全体で使用すると便利です。
レイアウトコンポーネントの定義
たとえば、「todo」リストアプリケーションを構築していると想像してください。次のようなlayout
コンポーネントを定義する場合があります。
<!-- resources/views/components/layout.blade.php -->
<html>
<head>
<title>{{ $title ?? 'Todo Manager' }}
</head>
<body>
<h1>Todos</h1>
<hr/>
{{ $slot }}
</body>
</html>
レイアウトコンポーネントの適用
layout
コンポーネントを定義したら、そのコンポーネントを利用するBladeビューを作成できます。この例では、タスクリストを表示する単純なビューを定義してみます。
<!-- resources/views/tasks.blade.php -->
<x-layout>
@foreach ($tasks as $task)
{{ $task }}
@endforeach
</x-layout>
コンポーネントに挿入するコンテンツは、layout
コンポーネント内のデフォルトの$slot
変数へ提供されることを覚えておいてください。お気づきかもしれませんが、layout
は$title
スロットが提供されている場合はそれも尊重し利用できます。それ以外の場合は、デフォルトのタイトルが表示されます。コンポーネントドキュメントで説明している標準のスロット構文を使用して、タスクリストビューからカスタムタイトルを挿入できます。
<!-- resources/views/tasks.blade.php -->
<x-layout>
<x-slot name="title">
Custom Title
</x-slot>
@foreach ($tasks as $task)
{{ $task }}
@endforeach
</x-layout>
これで、レイアウトビューとタスクリストビューを定義できたので、あとはルートからtask
ビューを返すだけです。
use App\Models\Task;
Route::get('/tasks', function () {
return view('tasks', ['tasks' => Task::all()]);
});
テンプレートの継承を使用したレイアウト
レイアウトの定義
レイアウトは、「テンプレートの継承」を介して作成することもできます。これは、componentsが導入される前のアプリケーションを構築する主要な方法でした。
はじめに、簡単な例を見てみましょう。まず、ページレイアウトを検討します。ほとんどのWebアプリケーションは、さまざまなページで同じ一般的なレイアウトを維持しているため、このレイアウトを単一のBladeビューとして定義すると便利です。
<!-- resources/views/layouts/app.blade.php -->
<html>
<head>
<title>App Name - @yield('title')</title>
</head>
<body>
@section('sidebar')
This is the master sidebar.
@show
<div class="container">
@yield('content')
</div>
</body>
</html>
ご覧のとおり、このファイルには一般的なHTMLマークアップが含まれています。ただし、@section
および@yield
ディレクティブに注意してください。@section
ディレクティブは、その名前が示すように、コンテンツのセクションを定義し、@yield
ディレクティブは特定のセクションのコンテンツを表示するために使用します。
アプリケーションのレイアウトを定義したので、レイアウトを継承する子ページを定義しましょう。
レイアウトの拡張
子ビューを定義するときは、@extends
Bladeディレクティブを使用して、子ビューが「継承」するレイアウトを指定します。Bladeレイアウトを拡張するビューは、@section
ディレクティブを使用してレイアウトのセクションへコンテンツを挿入できます。上記の例に見られるように、これらのセクションの内容は@yield
を使用してレイアウトへ表示させることに注意してください。
<!-- resources/views/child.blade.php -->
@extends('layouts.app')
@section('title', 'Page Title')
@section('sidebar')
@@parent
<p>ここはマスターサイドバーに追加されます。</p>
@endsection
@section('content')
<p>これが本文の内容です。</p>
@endsection
この例では、sidebar
セクションは@parent
ディレクティブを利用して、コンテンツを(上書きするのではなく)レイアウトのサイドバーに追加しています。@parent
ディレクティブは、ビューがレンダリングされるときにレイアウトのコンテンツに置き換えられます。
Tip!! 前例とは異なり、この
sidebar
セクションは@show
ではなく@endsection
で終わります。@endsection
ディレクティブはセクションのみを定義し、@show
はセクションを定義してすぐに生成します。
@yield
ディレクティブは、2番目のパラメーターとしてデフォルト値も取ります。この値は、生成するセクションが未定義の場合にレンダーされます。
@yield('content', 'Default content')
フォーム
CSRFフィールド
アプリケーションでHTMLフォームを定義するときはいつでも、CSRF保護ミドルウェアがリクエストを検証できるように、フォームへ非表示のCSRFトークンフィールドを含める必要があります。@csrf
Bladeディレクティブを使用して、トークンフィールドを生成できます。
<form method="POST" action="/profile">
@csrf
...
</form>
メソッドフィールド
HTMLフォームはPUT
、PATCH
、DELETE
リクエストを作成できないため、これらのHTTP動詞へ見せかけるために、非表示の_method
フィールドを追加する必要があります。@method
Bladeディレクティブは、次のフィールドを作成します。
<form action="/foo/bar" method="POST">
@method('PUT')
...
</form>
バリデーションエラー
@error
ディレクティブを使用すると、特定の属性にバリデーションエラーメッセージが存在するかどうかを簡単に確認できます。@error
ディレクティブ内で、$message
変数をエコーしてエラーメッセージを表示できます。
<!-- /resources/views/post/create.blade.php -->
<label for="title">Post Title</label>
<input id="title" type="text" class="@error('title') is-invalid @enderror">
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
特定のエラーバッグの名前を2番目のパラメータとして@error
ディレクティブに渡し、複数のフォームを含むページのバリデーションエラーメッセージが取得できます。
<!-- /resources/views/auth.blade.php -->
<label for="email">Email address</label>
<input id="email" type="email" class="@error('email', 'login') is-invalid @enderror">
@error('email', 'login')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
生PHP
状況によっては、PHPコードをビューに埋め込めると便利です。Blade@php
ディレクティブを使用して、テンプレート内でプレーンPHPのブロックを定義し、実行できます。
@php
$counter = 1;
@endphp
コンポーネント
コンポーネントとスロットは、セクション、レイアウト、インクルードと同様の利便性を提供します。ただし、コンポーネントとスロットのメンタルモデルが理解しやすいと感じる人もいるでしょう。コンポーネントを作成するには、クラスベースのコンポーネントと匿名コンポーネントの2つのアプローチがあります。
クラスベースのコンポーネントを作成するには、make:component
Artisanコマンドを使用できます。コンポーネントの使用方法を説明するために、単純な「アラート(Alert
)」コンポーネントを作成します。make:component
コマンドは、コンポーネントをApp\View\Components
ディレクトリに配置します。
php artisan make:component Alert
make:component
コマンドは、コンポーネントのビューテンプレートも作成します。ビューはresources/views/components
ディレクトリに配置されます。独自のアプリケーション用のコンポーネントを作成する場合、コンポーネントはapp/View/Components
ディレクトリとresources/views/components
ディレクトリ内で自動的に検出するため、通常それ以上のコンポーネント登録は必要ありません。
サブディレクトリ内にコンポーネントを作成することもできます。
php artisan make:component Forms/Input
上記のコマンドは、App\View\Components\Forms
ディレクトリにInput
コンポーネントを作成し、ビューはresources/views/components/forms
ディレクトリに配置します。
パッケージコンポーネントの手動登録
独自のアプリケーション用コンポーネントを作成する場合、コンポーネントをapp/View/Components
ディレクトリとresources/views/components
ディレクトリ内で自動的に検出します。
ただし、Bladeコンポーネントを利用するパッケージを構築する場合は、コンポーネントクラスとそのHTMLタグエイリアスを手動で登録する必要があります。通常、コンポーネントはパッケージのサービスプロバイダーのboot
メソッドに登録する必要があります。
use Illuminate\Support\Facades\Blade;
/**
* パッケージの全サービスの初期起動処理
*/
public function boot()
{
Blade::component('package-alert', AlertComponent::class);
}
コンポーネントを登録すると、タグエイリアスを使用してレンダリングできます。
<x-package-alert/>
または、規約により、componentNamespace
メソッドを使用してコンポーネントクラスを自動ロードすることもできます。たとえば、Nightshade
パッケージには、Package\Views\Components
名前空間内にあるCalendar
とColorPicker
コンポーネントが含まれているとしましょう。
use Illuminate\Support\Facades\Blade;
/**
* パッケージの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
これにより、package-name::
構文を使用して、ベンダーの名前空間でパッケージコンポーネントが使用できるようになります。
<x-nightshade::calendar />
<x-nightshade::color-picker />
Bladeは、コンポーネント名のパスカルケースを使い、コンポーネントにリンクしているクラスを自動的に検出します。サブディレクトリもサポートしており、「ドット」表記を使用します。
コンポーネントのレンダー
コンポーネントを表示するために、Bladeテンプレート1つの中でBladeコンポーネントタグを使用できます。Bladeコンポーネントタグは、文字列x-
で始まり、その後にコンポーネントクラスのケバブケース名を続けます。
<x-alert/>
<x-user-profile/>
コンポーネントクラスがApp\View\Components
ディレクトリ内のより深い場所にネストしている場合は、.
文字を使用してディレクトリのネストを表せます。たとえば、コンポーネントがApp\View\Components\Inputs\Button.php
にあるとしたら、以下のようにレンダリングできます。
<x-inputs.button/>
コンポーネントへのデータ渡し
HTML属性を使用してBladeコンポーネントへデータを渡せます。ハードコードするプリミティブ値は、単純なHTML属性文字列を使用してコンポーネントに渡すことができます。PHPの式と変数は、接頭辞として:
文字を使用する属性を介してコンポーネントに渡す必要があります。
<x-alert type="error" :message="$message"/>
コンポーネントの必要なデータは、そのクラスコンストラクターで定義する必要があります。コンポーネントのすべてのパブリックプロパティは、コンポーネントのビューで自動的に使用可能になります。コンポーネントのrender
メソッドからビューにデータを渡す必要はありません。
<?php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
/**
* 警告タイプ
*
* @var string
*/
public $type;
/**
* 警告メッセージ
*
* @var string
*/
public $message;
/**
* コンポーネントインスタンスを作成
*
* @param string $type
* @param string $message
* @return void
*/
public function __construct($type, $message)
{
$this->type = $type;
$this->message = $message;
}
/**
* コンポーネントを表すビュー/コンテンツを取得
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return view('components.alert');
}
}
コンポーネントをレンダーするときに、変数を名前でエコーすることにより、コンポーネントのパブリック変数の内容を表示できます。
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
ケース形式
コンポーネントコンストラクタの引数はキャメルケース(camelCase
)を使用して指定する必要がありますが、HTML属性で引数名を参照する場合はケバブケース(kebab-case
)を使用する必要があります。たとえば、次のようにコンポーネントコンストラクタに渡します。
/**
* コンポーネントインスタンスの作成
*
* @param string $alertType
* @return void
*/
public function __construct($alertType)
{
$this->alertType = $alertType;
}
$alertType
引数は、次のようにコンポーネントに提供できます。
<x-alert alert-type="danger" />
コンポーネントメソッド
コンポーネントテンプレートで使用できるパブリック変数に加えて、コンポーネントの任意のパブリックメソッドを呼び出すこともできます。たとえば、isSelected
メソッドを持つコンポーネントを想像してみてください。
/**
* 指定したオプションが現在選択されているオプションであるか判別
*
* @param string $option
* @return bool
*/
public function isSelected($option)
{
return $option === $this->selected;
}
メソッドの名前に一致する変数を呼び出すことにより、コンポーネントテンプレートでこのメソッドを実行できます。
<option {{ $isSelected($value) ? 'selected="selected"' : '' }} value="{{ $value }}">
{{ $label }}
</option>
コンポーネントクラス内の属性とスロットへのアクセス
Bladeコンポーネントを使用すると、クラスのrenderメソッド内のコンポーネント名、属性、およびスロットにアクセスすることもできます。ただし、このデータにアクセスするには、コンポーネントのrender
メソッドからクロージャーを返す必要があります。クロージャーは、唯一の引数として$data
配列を取ります。この配列はコンポーネントに関する情報を提供するいくつかの要素が含んでいます。
/**
* コンポーネントを表すビュー/コンテンツを取得
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return function (array $data) {
// $data['componentName'];
// $data['attributes'];
// $data['slot'];
return '<div>Components content</div>';
};
}
componentName
は、HTMLタグでx-
プレフィックスの後に使用されている名前と同じです。したがって、<x-alert/>
のcomponentName
はalert
になります。attributes
要素には、HTMLタグに存在していたすべての属性が含まれます。slot
要素は、コンポーネントのスロットの内容を含むIlluminate\Support\HtmlString
インスタンスです。
クロージャは文字列を返す必要があります。返された文字列が既存のビューに対応している場合、そのビューがレンダリングされます。それ以外の場合、返される文字列はインラインBladeビューとして評価されます。
追加の依存関係
コンポーネントがLaravelのサービスコンテナからの依存を必要とする場合、コンポーネントのデータ属性の前にそれらをリストすると、コンテナによって自動的に注入されます。
use App\Services\AlertCreator
/**
* コンポーネントインスタンスを作成
*
* @param \App\Services\AlertCreator $creator
* @param string $type
* @param string $message
* @return void
*/
public function __construct(AlertCreator $creator, $type, $message)
{
$this->creator = $creator;
$this->type = $type;
$this->message = $message;
}
コンポーネント属性
データ属性をコンポーネントへ渡す方法は、すでに説明しました。ただ、コンポーネントが機能するためには、不必要なclass
などの追加のHTML属性データを指定する必要が起きることもあります。通常これらの追加の属性は、コンポーネントテンプレートのルート要素に渡します。たとえば、次のようにalert
コンポーネントをレンダリングしたいとします。
<x-alert type="error" :message="$message" class="mt-4"/>
コンポーネントのコンストラクタの一部ではないすべての属性は、コンポーネントの「属性バッグ」に自動的に追加されます。この属性バッグは、$attributes
変数を通じてコンポーネントで自動的に使用可能になります。この変数をエコーすることにより、すべての属性をコンポーネント内でレンダリングできます。
<div {{ $attributes }}>
<!-- コンポーネントの内容 -->
</div>
Note: コンポーネントタグ内での
@env
などのディレクティブの使用は、現時点でサポートしていません。たとえば、<x-alert :live="@env('production')"/>
はコンパイルしません。
デフォルト/マージした属性
属性のデフォルト値を指定したり、コンポーネントの属性の一部へ追加の値をマージしたりする必要のある場合があります。これを実現するには、属性バッグのmerge
メソッドを使用できます。このメソッドは、コンポーネントに常に適用する必要のあるデフォルトのCSSクラスのセットを定義する場合でとくに便利です。
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
このコンポーネントが以下のように使用されると仮定しましょう。
<x-alert type="error" :message="$message" class="mb-4"/>
コンポーネントが最終的にレンダーするHTMLは、以下のようになります。
<div class="alert alert-error mb-4">
<!-- $message変数の中身 -->
</div>
非クラス属性のマージ
class
属性ではない属性をマージする場合、merge
メソッドへ指定する値は属性の「デフォルト」値と見なします。ただし、class
属性とは異なり、こうした属性は挿入した属性値とマージしません。代わりに上書きします。たとえば、button
コンポーネントの実装は次のようになるでしょう。
<button {{ $attributes->merge(['type' => 'button']) }}>
{{ $slot }}
</button>
カスタムtype
を指定してボタンコンポーネントをレンダーすると、このコンポーネントを使用するときにそれが指定されます。タイプが指定されない場合、button
タイプを使用します。
<x-button type="submit">
Submit
</x-button>
この例でレンダリングされる、button
コンポーネントのHTMLは以下のようになります。
<button type="submit">
Submit
</button>
class
以外の属性にデフォルト値と挿入する値を結合させたい場合は、prepends
メソッドを使用します。この例では、data-controller
属性は常にprofile-controller
で始まり、追加で挿入するdata-controller
値はデフォルト値の後に配置されます。
<div {{ $attributes->merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
{{ $slot }}
</div>
属性のフィルタリング
filter
メソッドを使用して属性をフィルタリングできます。このメソッドは、属性を属性バッグへ保持する場合にtrue
を返す必要があるクロージャを引数に取ります。
{{ $attributes->filter(fn ($value, $key) => $key == 'foo') }}
キーが特定の文字列で始まるすべての属性を取得するために、whereStartsWith
メソッドを使用できます。
{{ $attributes->whereStartsWith('wire:model') }}
first
メソッドを使用して、特定の属性バッグの最初の属性をレンダーできます。
{{ $attributes->whereStartsWith('wire:model')->first() }}
スロット
多くの場合、「スロット」を利用して追加のコンテンツをコンポーネントに渡す必要があることでしょう。コンポーネントスロットは、$slot
変数をエコーしレンダーします。この概念を学習するために、alert
コンポーネントに次のマークアップがあると過程してみましょう。
<!-- /resources/views/components/alert.blade.php -->
<div class="alert alert-danger">
{{ $slot }}
</div>
コンポーネントにコンテンツを挿入することにより、そのコンテンツをslot
に渡せます。
<x-alert>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
コンポーネント内の異なる場所へ、複数の異なるスロットをレンダーする必要のある場合があります。「タイトル("title")」スロットへ挿入できるようにするため、アラートコンポーネントを変更してみましょう。
<!-- /resources/views/components/alert.blade.php -->
<span class="alert-title">{{ $title }}</span>
<div class="alert alert-danger">
{{ $slot }}
</div>
x-slot
タグを使用して、名前付きスロットのコンテンツを定義できます。明示的にx-slot
タグ内にないコンテンツは、$slot
変数のコンポーネントに渡されます。
<x-alert>
<x-slot name="title">
Server Error
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
スコープ付きスロット
VueのようなJavaScriptフレームワークを使用している方は「スコープ付きスロット」に慣れていると思います。これは、スロットの中でコンポーネントのデータやメソッドへアクセスできる機構です。コンポーネントにパブリックメソッドまたはプロパティを定義し、$component
変数を使いスロット内のコンポーネントにアクセスすることで、Laravelと同様の動作を実現できます。この例では、x-alert
コンポーネントのコンポーネントクラスにパブリックformatAlert
メソッドが定義されていると想定しています。
<x-alert>
<x-slot name="title">
{{ $component->formatAlert('Server Error') }}
</x-slot>
<strong>Whoops!</strong> Something went wrong!
</x-alert>
インラインコンポーネントビュー
非常に小さいコンポーネントの場合、コンポーネントクラスとビューテンプレートの両方を管理するのは面倒だと感じるかもしれません。そのため、コンポーネントのマークアップをrender
メソッドから直接返すことができます。
/**
* コンポーネントを表すビュー/コンテンツの取得
*
* @return \Illuminate\View\View|\Closure|string
*/
public function render()
{
return <<<'blade'
<div class="alert alert-danger">
{{ $slot }}
</div>
blade;
}
インラインビューコンポーネントの生成
インラインビューをレンダーするコンポーネントを作成するには、make:component
コマンドを実行するときにinline
オプションを使用します。
php artisan make:component Alert --inline
匿名コンポーネント
インラインコンポーネントと同様に、匿名コンポーネントは、単一のファイルを介してコンポーネントを管理するためのメカニズムを提供します。ただし、匿名コンポーネントは単一のビューファイルを利用し、関連するクラスはありません。匿名コンポーネントを定義するには、resources/views/components
ディレクトリ内にBladeテンプレートを配置するだけで済みます。たとえば、resources/views/components/alert.blade.php
でコンポーネントを定義すると、以下のようにレンダーできます。
<x-alert/>
.
文字を使用して、コンポーネントがcomponents
ディレクトリ内のより深い場所にネストしていることを示せます。たとえば、コンポーネントがresources/views/components/input/button.blade.php
で定義されていると仮定すると、次のようにレンダリングできます。
<x-inputs.button/>
データのプロパティ/属性
匿名コンポーネントには関連付けたクラスがないため、どのデータを変数としてコンポーネントに渡す必要があり、どの属性をコンポーネントの属性バッグに配置する必要があるか、どう区別するか疑問に思われるかもしれません。
コンポーネントのBladeテンプレートの上部にある@props
ディレクティブを使用して、データ変数と見なすべき属性を指定できます。コンポーネント上の他のすべての属性は、コンポーネントの属性バッグを介して利用します。データ変数にデフォルト値を指定する場合は、変数の名前を配列キーに指定し、デフォルト値を配列値として指定します。
<!-- /resources/views/components/alert.blade.php -->
@props(['type' => 'info', 'message'])
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
</div>
上記のコンポーネント定義をすると、そのコンポーネントは次のようにレンダーできます。
<x-alert type="error" :message="$message" class="mb-4"/>
動的コンポーネント
コンポーネントをレンダーする必要があるが、実行時までどのコンポーネントをレンダーする必要のわからない場合があります。その状況では、Laravel組み込みのdynamic-component
コンポーネントを使用して、ランタイム値または変数に基づいてコンポーネントをレンダーできます。
<x-dynamic-component :component="$componentName" class="mt-4" />
コンポーネントの手動登録
Note: コンポーネントの手動登録に関する以下のドキュメントは、主にビューコンポーネントを含むLaravelパッケージを作成している人に当てはまります。パッケージを作成していない場合は、コンポーネントドキュメントのこの箇所は関係ないでしょう。
独自のアプリケーション用のコンポーネントを作成する場合、コンポーネントはapp/View/Components
ディレクトリとresources/views/components
ディレクトリ内で自動的に検出されます。
ただし、Bladeコンポーネントを利用するパッケージを構築する場合、またはコンポーネントを従来とは異なるディレクトリに配置する場合は、Laravelがコンポーネントの場所を認識できるように、コンポーネントクラスとそのHTMLタグエイリアスを手動で登録する必要があります。通常、コンポーネントはパッケージのサービスプロバイダーのboot
メソッドで、登録する必要があります。
use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;
/**
* パッケージの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::component('package-alert', AlertComponent::class);
}
コンポーネントを登録すると、タグエイリアスを使用してレンダーできます。
<x-package-alert/>
パッケージコンポーネントの自動ロード
または、規約により、componentNamespace
メソッドを使用してコンポーネントクラスを自動ロードすることもできます。たとえば、Nightshade
パッケージには、Package\Views\Components
名前空間内にあるCalendar
とColorPicker
コンポーネントが含まれているとしましょう。
use Illuminate\Support\Facades\Blade;
/**
* パッケージの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}
これにより、package-name::
構文を使用して、ベンダーの名前空間でパッケージコンポーネントを使用できるようになります。
<x-nightshade::calendar />
<x-nightshade::color-picker />
Bladeは、コンポーネント名のパスカルケースを使い、コンポーネントにリンクしているクラスを自動的に検出します。サブディレクトリもサポートしており、「ドット」表記を使用します
スタック
Bladeを使用すると、別のビューまたはレイアウトのどこか別の場所でレンダーできる名前付きスタックに入れ込めます。これは、子ビューに必要なJavaScriptライブラリを指定する場合、とくに便利です。
@push('scripts')
<script src="/example.js"></script>
@endpush
必要な回数だけスタックに入れ込めます。スタックしたコンテンツを完全にレンダーするには、スタックの名前を@stack
ディレクティブに渡します。
<head>
<!-- HEADのコンテンツ -->
@stack('scripts')
</head>
スタックの先頭にコンテンツを追加する場合は、@prepend
ディレクティブを使用します。
@push('scripts')
これは2番めになる
@endpush
// Later...
@prepend('scripts')
これが最初になる
@endprepend
サービス注入
@inject
ディレクティブは、Laravelサービスコンテナからサービスを取得するために使用します。@inject
に渡す最初の引数は、サービスが配置される変数の名前であり、2番目の引数は解決したいサービスのクラスかインターフェイス名です。
@inject('metrics', 'App\Services\MetricsService')
<div>
Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
Bladeの拡張
Bladeでは、directive
メソッドを使用して独自のカスタムディレクティブを定義できます。Bladeコンパイラは、カスタムディレクティブを検出すると、ディレクティブに含まれる式を使用して、提供したコールバックを呼び出します。
次の例は、指定した$var
をフォーマットする@datetime($var)
ディレクティブを作成します。これはDateTime
インスタンスである必要があります。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* アプリケーションの全サービスの登録
*
* @return void
*/
public function register()
{
//
}
/**
* アプリケーションの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
}
}
ご覧のとおり、ディレクティブに渡される式にformat
メソッドをチェーンします。したがって、この例でディレクティブが生成する最終的なPHPは、以下のようになります。
<?php echo ($var)->format('m/d/Y H:i'); ?>
Note: Bladeディレクティブのロジックを更新した後は、キャッシュ済みのBladeビューをすべて削除する必要があります。キャッシュ済みBladeビューは、
view:clear
Artisanコマンドを使用して削除できます。
カスタムIf文
カスタムディレクティブのプログラミングは、単純なカスタム条件ステートメントを定義するとき、必要以上の複雑さになる場合があります。そのため、BladeにはBlade::if
メソッドが用意されており、クロージャを使用してカスタム条件付きディレクティブをすばやく定義できます。たとえば、アプリケーションのデフォルト「ディスク」の設定をチェックするカスタム条件を定義しましょう。これは、AppServiceProvider
のboot
メソッドで行います。
use Illuminate\Support\Facades\Blade;
/**
* アプリケーションの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Blade::if('disk', function ($value) {
return config('filesystems.default') === $value;
});
}
カスタム条件を定義すると、テンプレート内で使用できます。
@disk('local')
<!-- アプリケーションはローカルディスクを使用している -->
@elsedisk('s3')
<!-- アプリケーションはs3ディスクを使用している -->
@else
<!-- アプリケーションは他のディスクを使用している -->
@enddisk
@unlessdisk('local')
<!-- アプリケーションはローカルディスクを使用していない -->
@enddisk