Laravel Blade テンプレートの書き方チートシート

シェアしてね〜🤞

記事の概要

Laravelに含まれているテンプレートエンジンBladeは大変便利ですが、多機能ゆえに意外と知らない書き方があったり、何度も同じ事を調べてしまいがちです。

こちらに網羅的な公式ドキュメントがありますが、タスキに長しという感があります。

この記事では筆者が業務でよく使用するものに絞り込んで、Bladeテンプレートの書き方をご紹介します。

PHP変数の展開

{{ $v }}

BladeテンプレートにPHP変数の中身を表示するには{{ $v }}を使います。

この方法はただ変数の中身をHTMLに展開するだけでなく、セキュリティ対策として、ブラウザがHTMLタグとして解釈できないよう特定の文字をエスケープしてくれます。

例えば、$vの中に<script>が含まれていてもブラウザはJavascriptを実行しません。

{!! $v !!}

BladeテンプレートにPHP変数の中身をエスケープせずにそのまま表示するには{!! $v !!}を使います。

しかし、びっくりマークが使われていることからもわかるように、この書き方にはリスクが伴います。

悪意のあるコードはどこにでも紛れ込みうるものと考えて、常に{{ $v }}を使うべきです。

{!! nl2br(e($v)) !!}

{!! $v !!}の使用を回避すべきことの例外として、PHP変数の改行文字"¥n"<br>に変換して出力したいケースがあります。

{{ nl2br($v) }}と書くと「<br>」が文字としてそのまま表示されてしまいます。

そこで、e()で特定の文字をエスケープしたのちに、nl2br()を適用しそのまま出力することで、<br>がエスケープされるのを回避します。

エスケープとは

ここでいうエスケープとは、上記例の$vの中身がブラウザによってHTMLとして認識されないようにすることを言います。

例えば、$vがユーザーが投稿したメッセージである場合、悪意あるコードを実行する<script>タグが記述されている可能性があります。

これをブラウザが読み込んで実行する恐れがあるため、Bladeで変数の中身をHTMLに展開するときは、常にエスケープすることが推奨されます。

条件分岐

@if

PHPのifと同じように表示内容を制御できます。条件分岐には様々なディレクティブが存在しますが、@ifで再現できるものが多いので、差し当たり@ifを知っていれば大丈夫でしょう。

@if (count($records) === 1)
    一件のレコードが存在します!
@elseif (count($records) > 1)
    複数件のレコードが存在します!
@else
    レコードが存在しません!
@endif

@switch

PHPのswitchと同じように表示内容を制御できます。

@breakがないと次の@case以下のコードが実行されてしまうため、それを意図しない場合は@breakを忘れないようにしましょう。

自信がなければ@ifでも代替できるので、@switchを無理に使う必要はありません。

@switch($i)
    @case(1)
        ケース1...
        @break
    @case(2)
        ケース2...
        @break
    @default
        その他のケース...
@endswitch

ループ

@for

PHPのforと同じように表示内容を繰り返すことができます。

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

@foreach

PHPのforeachと同じように、配列・コレクションに含まれている要素の数だけ、表示内容を繰り返すことができます。ループ内では、取り出した値(以下の例では$user)を使うことができます。

@foreach ($users as $index => $user)
    <p>{{ $index }}番目のユーザーのIDは {{ $user->id }}</p>
@endforeach

@forelse

配列・コレクションが空のとき、その旨を表示したいことが多々あります。@foreach@ifを組み合わせても実装できますが@forelseを使うと簡潔に記述できるのでオススメです。

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>ユーザーが登録されていません</p>
@endforelse

$loop

ループ内では$loopという特別な変数を宣言なしで使えます。$loop様々なプロパティを持っており、わざわざこれを使わなくても実装できることの方が多いですが、ご紹介しておきます。

以下は、1回目のループならばture、それ以外ならfalseであるfirstプロパティを使った例です。

あとで紹介する@classとTailewind CSSを併用して、最初の要素には上下に、それ以外の要素には下にだけボーダーを表示しています。

@foreach ($users as  $user)
    <p @class([
        'border-y' => $loop->first,
        'border-b' => !$loop->first,
    ])>このユーザーのID{{ $user->id }}</p>
@endforeach

条件付きクラス

@class

特にTailwind CSSを使っているとき、条件を満たすときだけクラスを指定したいことがあります。そういうときは@classの出番です。

<span @class([
    'p-4',                      {{-- 常にclassに含まれる --}}
    'bg-red-500' => $hasError,  {{-- $hasErrorが真のときclassに含まれる --}}
])>テキスト</span>

@style

@classと同じように、style属性に対しても条件指定できます。

<span @style([
    'padding: 1rem',                       {{-- 常にstyleに含まれる --}}
    'background-color: red' => $hasError,  {{-- $hasErrorが真のときstyleに含まれる --}}
])>テキスト</span>

PHPコード

@php

@phpを使うことでBladeテンプレートにPHPコードを記述することができます。@phpブロック内で定義した変数をブロック外で使うこともできます。

@php
    $hasError = true;
@endphp

<span @class([
    'p-4',
    'bg-red-500' => $hasError,
])>テキスト</span>

@php()

@php()を使えば@endphpが不要なので、1行でコードを書けます。

@php(dd($hasError))

コメント

{{-- コメント --}}

Bladeテンプレート内でのコメントには{{-- コメント --}}を使います。

HTMLのコメントである<!-- コメント -->も使えますが、こちらはレンダーされたHTMLに残るため、開発者ツールを通してユーザーに晒されてしまいます。

バリデーション

@error()

formリクエストの際にPHP側のvalidate()でエラーが生じたとき、エラーメッセージの表示には@error()を使います。引数として対象となるフォームデータ名を渡します。

エラーメッセージはブロック内で使える特別な変数$messageに格納されています。

<input name="email" />

@error('email')
    <span class="text-red-500">{{ $message }}</span>
@enderror
$request->validate([
    'email' => 'required|email',
]);

コンポーネント

コンポーネントとは

コンポーネントとは、ボタンやヘッダーメニューなど複数ページで繰り返し使うBladeの記述を別ファイルに保存し、ページで呼び出して部品のように配置できる機能です。

コンポーネントには、クラスとBladeテンプレートの2ファイルから構成される通常のコンポーネントと、Bladeテンプレートだけで構成される匿名コンポーネントがあります。

匿名コンポーネントの方が管理しやすく、おそらく通常のコンポーネントにしかできないことはないので、筆者は匿名コンポーネントばかり使っています。

この記事では匿名コンポーネントの使い方に絞って解説します。

ファイルの作り方

以下のコマンドで作ることもできますが、/resources/views/components配下に空のファイルを自分で作ることもできます。拡張子は.blade.phpです。

php artisan make:component フォルダ名.コンポーネント名 --view

配置方法

他のBladeテンプレートでコンポーネントを呼び出して配置するには、<x-フォルダ名.コンポーネント名/>タグを記述します。例えば/resources/views/components/form/input.blade.phpの場合は<x-form.input/>です。なお、コンポーネント内から他のコンポーネントを呼び出して配置することもできます。

データの受け渡し

コンポーネントが受け取るデータを定義するには@propsを使います。デフォルト値を指定することもできます。

/resources/views/components/proverb.blade.php

@props(['name', 'message' => '天は人の上に人を作らず'])

<div>
    {{ $name }}曰く「{{ $message }}」
</div>

タグに属性を指定するようにして、コンポーネントにデータを渡すことができます。PHP変数を渡したい場合は、属性名の前に:を付けます。

@php($message='学問は米をつきながらも出来るものなり')

<x-proverb name="福沢諭吉" :message="$message" />  {{-- 簡略形として:$messageとすることもできる --}}

注意点として、属性名の前に:を付けることができるのは、コンポーネントだけであり、普通のHTMLタグの属性名の前に:を付けて変数を渡しても、その記述がそのままレンダーされるだけです。

$attributes

受け渡しする属性の名前を全て指定する必要はありません。加工したり変数展開する必要がなければ$attributesでまとめて渡せます。

以下の例では$attributesx-on:click="submit" class="px-4 py-2"の部分が展開されます。

/resources/views/components/submit-button.blade.php

<button {{ $attributes }}>送信</button>
<x-submit-button x-on:click="submit"  class="px-4 py-2" />

$attributes->merge()

Tailwind CSSを使っていると、コンポーネント側で基本スタイルを指定しておき、配置するとき色だけ指定したいことがあります。単に属性を渡すだけだとclass属性が2つになり衝突してしまうため、マージする必要があります。

/resources/views/components/submit-button.blade.php

<button {{ $attributes->merge(['class' => 'px-4 py-2']) }}>送信</button>
<x-submit-button class="bg-blue-200" />

注意点として、Tailewind CSSの同じ種類のクラスはマージされません。例えば、コンポーネントに基本スタイルとしてbg-red-200が指定されておりbg-blue-200が渡されきたとすると、class="bg-red-200 bg-blue-200"のように展開されてしまい、どちらの色になるかわかりません。この問題に対処するには別パッケージの導入が必要ですが、この記事では立ち入りません。

$attributes->class()

class属性のマージには簡略形があり、上記の例は以下のように記述することができます。

/resources/views/components/submit-button.blade.php

<button {{ $attributes->class('px-4 py-2') }}>送信</button>

さらにclass()@classのように条件指定ができます。

@props(['hasError' => false])

<span {{ $attributes->class([
    'p-4',                      {{-- 常にclassに含まれる --}}
    'bg-red-500' => $hasError,  {{-- $hasErrorが真のときclassに含まれる --}}
]) }}>テキスト</span>

$slot

HTMLタグと同じようにコンポーネントにも、文字やタグ、さらには別のコンポーネントを入れ子にすることができます。

<x-button>
    <span class="text-red-500">絶対に押すな</span>
</x-button>

これをコンポーネントで表示するには$slotという名前の変数を展開します。この変数は宣言なしで使えます。

/resources/views/components/button.blade.php

<button>{{ $slot }}</button>

x-slot:スロット名

複数のスロットが必要なら<x-slot:スロット名>の出番です。以下の例ではheaderという名前の追加スロットを使用しています。

/resources/views/components/article.blade.php

<div {{ $attributes }}>
    <header>{{ $header }}</header>
    <main>{{ $slot }}</main>
</div>
<x-article>
    <x-slot:header>タイトル</x-slot>
    本文....
</x-article>

x-slotに渡した属性を展開することもできます。もちろん$attributesのメソッドも使えます。

/resources/views/components/article.blade.php

<div {{ $attributes }}>
    <header {{ $header->attributes }}>{{ $header }}</header>
    <main>{{ $slot }}</main>
</div>
シェアしてね〜🤞