動作環境:Laravel 8

準備

ルーターでルートでindexというビューファイルが読み込まれるようにします。

routes/web.php

Route::get('/', function () {
	return view('index');
});

resources/viewsindex.blade.phpファイルを作成します。

resources/views/index.blade.php

<h1>コンポーネントのテスト</h1>

ルートにアクセスして「コンポーネントのテスト」が表示されることを確認してください。
今回はこのファイルに作成したコンポーネントを配置していきます。

簡単なコンポーネント

resources/viewscomponentsというディレクトリを新たに作ってください。
コンポーネントファイルは基本的にはこのディレクトリの中にbladeファイルを作成していきます。
はじめにwelcome.blade.phpというファイルを作成してみましょう。

resources/views/components/welcome.blade.php

<div class="welcome">
	<h2>Welcome</h2>
	<p>これはコンポーネントです。</p>
</div>

index.blade.phpでこのコンポーネントを表示します。
コンポーネントはx-コンポーネント名でタグのように記述することで表示することができます。
今回は、welcome.blade.phpファイルを作成したので、<x-welcome />で表示することができます。
タグを「/」で閉じることを忘れないでください。

resources/views/index.blade.php

<h1>コンポーネントのテスト</h1>
<x-welcome />

ブラウザで表示すると下記のようになっていると思います。

<h1>コンポーネントのテスト</h1>
<div class="welcome">
	<h2>Welcome</h2>
	<p>これはコンポーネントです。</p>
</div>

コンポーネントに値を渡す

先ほどは固定されたHTMLを表示するだけでしたが、コンポーネントの読み込み元から値を渡して表示してみます。
試しにタイトルを変更できるようにしてみましょう。

HTMLの属性のように記述します。

resources/views/index.blade.php

<x-welcome title="タイトルの書き換え" />

これでコンポーネントでは「$title」変数が使用できるようになります。

resources/views/components/welcome.blade.php

<div class="welcome">
	<h2>{{ $title }}</h2>
	<p>これはコンポーネントです。</p>
</div>

変数を渡す場合は属性名の前に「:」を付けます。

resources/views/index.blade.php

@php
$title = "タイトルの書き換え";
@endphp
<x-welcome :title="$title" />

スロットでタグの中をそのまま表示

値は次のようにタグで囲んだ状態で渡すこともできます。

resources/views/index.blade.php

<x-welcome>
    <p>スロットメッセージ</p>
</x-welcome>

コンポーネントでは$slotという変数を使用することで表示することができます。

resources/views/components/welcome.blade.php

<div class="welcome">
	<h2>Welcome</h2>
	{{ $slot }}
</div>

複数のスロットを使用する

別々の場所にスロットで表示したいこともあると思います。

resources/views/components/welcome.blade.php

<div class="alert">
	{{ $alert }}
</div>

<div class="welcome">
	<h2>Welcome</h2>
	{{ $slot }}
</div>

次のようにコンポーネントの中にさらにx-slotを配置することで、別のスロットして扱われます。
この場合nameに設定した値が変数名になります。

resources/views/index.blade.php

<x-welcome>
	<x-slot name="alert">
		<p>エラーが発生しました。</p>
	</x-slot>
	<p>スロットメッセージ</p>
</x-welcome>

属性値をまとめて表示

コンポーネントの使用元でid,classを指定したいとします。

resources/views/index.blade.php

<x-welcome class="welcome" id="welcome-id">
    <p>スロットメッセージ</p>
</x-welcome>

コンポーネントでは$attributesという変数を使用することで、id,classをひとつひとつ指定しないでも、まとめて表示することができます。

resources/views/components/welcome.blade.php

<div {{ $attributes }}>
    <h2>Welcome</h2>
	{{ $slot }}
</div>

コンポーネントのクラスと組み合わせる

コンポーネント側で設定していたクラスも生かしたい場合は$attributes->mergeという機能を使います。

resources/views/components/welcome.blade.php

<div {{ $attributes->merge(['class' => 'component-welcome']) }}>
    <h2>Welcome</h2>
	{{ $slot }}
</div>

attributes(属性)にデータ値を含めない

例えば次のように属性値のほかに$title変数を扱いたいとします。

resources/views/index.blade.php

<x-welcome class="welcome" id="welcome-id" title="タイトル">
	<p>スロットメッセージ</p>
</x-welcome>

そのままattributesを展開してしまうとtitleも入ってしまいます。
次のように@propsを設定するとデータ値になりattributesに含まれません。

resources/views/components/welcome.blade.php

@props(['title'])

<div {{ $attributes->merge(['class' => 'component-welcome']) }}>
	<h2>{{ $title }}</h2>
	{{ $slot }}
</div>

レイアウトテンプレートをコンポーネントで作る

今までの機能を組み合わせることでレイアウトテンプレートを作ることもできそうですね。
たとえば次のようにレイアウトファイルを作ります。

resources/views/components/layouts/app.blade.php

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="utf-8">
		<title>{{ $title }}</title>
	</head>
	<body>
		<header>
			<h1>{{ $title }}</h1>
		</header>
		<main>{{ $slot }}</main>
		<footer>&copy webopixel</footer>
	</body>
</html>

index.blade.phpで次のように作成したレイアウトファイルを指定します。
componentsにディレクトリを作成して配置した場合は「.」で繋げます。
layoutsディレクトリに入れた場合は、x-layouts.appをいう指定になります。

resources/views/index.blade.php

<x-layouts.app title="コンポーネントのテスト">
    <section>
        <h2>コンポーネントアレイアウト</h2>
        <p>コンテンツ内容</p>
    </section>
</x-layouts.app>

コンポーネントで独立したデータを扱う

たとえばコンポーネントでデータベースの値を表示したいとします。
コントローラーからメインになるビューに値を渡し、そこからコンポーネントに値を渡すことで実現できますが、コンポーネントだけでデータの取得処理を行えるようになれば、簡単に再利用できるようになり便利そうですね。

ユーザー一覧を表示するコンポーネントを作ってみましょう。
次のコマンドを実行してください。

$ php artisan make:component UserList

app/View/Components/UserList.phpresources/views/components/user-list.blade.phpの2つのファイルが作成されます。

UserList.phpを次のように編集します。

app/View/Components/UserList.php

<?php
namespace App\View\Components;

use Illuminate\View\Component;
use App\Models\User;

class UserList extends Component
{
	public $users;

	/**
	 * Create a new component instance.
	 *
	 * @return void
	 */
	public function __construct()
	{
		$this->users = User::all();
	}

	/**
	 * Get the view / contents that represent the component.
	 *
	 * @return \Illuminate\Contracts\View\View|string
	 */
	public function render()
	{
		return view('components.user-list');
	}
}

コンポーネントは次のように作成します。

resources/views/components/user-list.blade.php

<div>
	<h3>ユーザー一覧</h3>
	@foreach ($users as $user)
		{{ $user->name }}
	@endforeach
</div>

これで予めコントローラーでデータを取得しなくても、コンポーネントを読み込むだけで表示することができます。

<x-user-list />

今回は以上です。
Laravelにはコンポーネント機能自体は以前からありましたが、新しい書き方では簡潔に書けるようになりましたね。
特にReactやVueなどフロントフレームワークを使ったことがあるなら、理解は早そうですが、@componentディレクティブの方がそれらと明示的にわかれてるので判別しやすいというのもあると思います。