今回は、VueJSのコンポーネントの話ではありません。Inertiaの話でもありません。Laravelのブレードコンポーネントの話です。使い始めると結構いいものです。

デモ

こんな入力画面あるとします。

もともとのブレードは、こんな感じです。

...
<div class="card-body">
    <form method="POST" action="{{ route('register') }}">
        @csrf

        <div class="row mb-3">
            <label for="email" class="col-md-4 col-form-label text-md-end">メールアドレス</label>

            <div class="col-md-6">
                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

                @error('email')
                    <span class="invalid-feedback" role="alert">
                        <strong>{{ $message }}</strong>
                    </span>
                @enderror
            </div>
       </div>

       <div class="row mb-0">
            <div class="col-md-6 offset-md-4">
                <button type="submit" class="btn btn-primary">
                    登録
                </button>
            </div>
        </div>
    </form>
</div>
...

今度は、コンポーネントを使用した、ブレードファイルの中身を見てみましょう。

...
<div class="card-body">
    <x-form action="{{ route('register') }}">

        <div class="row mb-3">
            <x-label name="email">メールアドレス</x-label>

            <div class="col-md-6">
                <x-input type="email" name="email" required autocomplete="email"  />
            </div>
        </div>

        <div class="row mb-0">
            <div class="col-md-6 offset-md-4">
                <x-button type="submit" level="primary">登録</x-button>
            </div>
        </div>
    </x-form>
</div>
...

見た目、とてもシンプルになりました。しかし、見慣れないHTMLタグがx-form, x-label, x-input, x-buttonと4つありますね。
もちろんそれらがコンポーネントなのですが、どうやってそれらのタグが利用できるようになるのでしょうか? 

コンポーネントの作成

コンポーネントの作成には、2つ方法があり、app/View/Componentsのディレクトリ下にクラスファイルを作成する方法と、resources/views/componentsに匿名コンポーネントを作成する方法があります。ここでは使いやすい後者を紹介します。

先のデモの4つのコンポーネントは、以下のようなファイルに定義されています。

resources/views/components
├── button.blade.php
├── form.blade.php
├── input.blade.php
└── label.blade.php

button.blade.phpというコンポーネントブレードファイルを作成すれば、それだけで、他のブレードファイルで、<x-button>のタグとして使用可能になります。

コンポーネントの定義

まずは、一番外側のx-formの定義です。

@props([
    'method' => 'POST',
    'action',
])

@php
  $method = Str::upper($method);
@endphp

<form
  method="{{ $method ===  'GET' ? 'GET' : 'POST' }}"
  action="{{ $action }}"
  {{ $attributes }}>
  @csrf

  @if (! in_array($method, ['GET', 'POST']))
    @method($method)
  @endif

  {{ $slot }}

</form>

御覧のように、通常のブレードファイルと同じように、@if@phpが使えます。しかし、@propsは見たことありませんね。

@propsでは、コンポーネントを使用するブレードから、コンポーネントにデータを渡すための変数の宣言と初期化をします。

ここでは、methodactionの変数がありますが

<x-form action="{{ route('register') }}">

では、actionにしかデータを渡していません。しかし、@propsにおいてmethodのデフォルトはPOSTと指定されているので、実際に出力されるHTMLは、以下のように初期化されます。

<form method="POST" action="http://localhost/register">
<input type="hidden" name="_token" value="8GdTncryczY6s2gkHCMFYJyRlvab2tkLbVp9iRUE">
..
</form>

もう1つ重要なのは、{{ $slot }}です。
そこの部分には、<x-form></x-form>のタグの間の値がすべて渡されます。

x-labelでも同様に{{ $slot }}を使います。

@props([
    'name',
])

<label for="{{ $name }}" class="col-md-4 col-form-label text-md-end">{{ $slot }}</label>

x-buttonも同様です。

@props([
    'type'  => 'button',
    'level' => 'default',
])

<button type="{{ $type }}" {{ $attributes->class("btn btn-$level")}}>{{ $slot }}</button>

@attributes->classは、既存のclassの属性に引数のclassをマージします。
例えば、

<x-button type="submit" class="m-2" level="primary">登録</x-button>

なら、

<button type="submit" class="btn btn-primary m-2">登録</button>

となります。

最後に、x-inputの定義です。

@props([
    'type'  => 'text',
    'name',
    'value' => null,
])

<input
  type="{{ $type }}"
  name="{{ $name }}"
  value="{{ old($name, $value) }}"

  @error($name)
    {{ $attributes->class('form-control is-invalid') }}
  @else
    {{ $attributes->class('form-control') }}
  @enderror
/>

@error($name)
  <span class="invalid-feedback" role="alert">
      <strong>{{ $message }}</strong>
  </span>
@enderror

以下のようにコンポーネントで定義していない属性(requiredやautocomplete)も渡していますが、

<x-input type="email" name="email" required autocomplete="email"  />

これらは、そのまま最終のHTMLに渡されます。

<input type="email" name="email" value="" class="form-control" required autocomplete="email">

By khino