九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、Laravelのバージョン7.x
がついに公開されるということで一足先にどのような機能が追加になったかをチェックしてみました。
いくつかLaravel 7.x
には新しい機能が追加になっていますが、その中でもひときわ興味を引かれたのが、
Bladeの新コンポーネント機能
です。
正確に言うと、以前からBlade
にはコンポーネント機能が存在していたのですが、今回はその機能が大幅に拡張され、いろいろと便利に使えるようになっていますので、今回の記事でまとめてみることにしました。
ぜひ皆さんのお役に立てると嬉しいです😊✨
【追記:2020.3.14】Laravel 7.1.2未満のバージョンにBlade Component
由来のXSS(クロスサイトスクリプティング)の脆弱性が報告されています。できるだけ早くアップデートすることをおすすめします。
開発環境: Larave 7.x
目次
何が一番変わったか
まず、これまでBlade
コンポーネントは、以下のようにディレクティブを使って呼び出していました。
@component('components.search-box') @endcomponent
これまでと同様この形も使えますが、今回のアップデートでは以下のような専用タグも使えるようになっています。
<x-search-box />
つまり、x-
から始まってそれ以降はファイル名になります。(階層が深い場合は、ドットでつないで<x-boxes.search />
という具合になります)
また、artisan
コマンドに「コンポーネントを管理するクラス」を作成するmake:component
が追加になっています。
例えば、こんな感じで使います。
php artisan make:component SearchBox
では、1つずつ見ていきましょう!
コンポーネント・クラスをつくるartisanコマンド
先ほども書いたように、今回のアップデートで新しいartisan
コマンドが追加され、コンポーネントを管理するクラスをつくることができますので、まずはこの方法でコンポーネントをつくってみたいと思います。
なお、今回は検索ボックスのためのコンポーネントを作ります。
以下のコマンドを実行してください。
php artisan make:component SearchBox
すると、/app/View/Components
フォルダにSearchBox.php
というファイルが作成されます。
そして、SearchBox.php
のrender()
内で指定されているビューも以下のように/resources/views/components
フォルダ内に自動的に作成されます。
では、この/resources/views/components/search-box.blade.php
を開いて以下のように検索ボックスを追加してください。
<!-- コンポーネント本体 --> <form action="/search"> <input type="text" name="q"> <select> <option>▼以下から選択<option> <option value="book">本<option> <option value="music">音楽</option> <option value="game">ゲーム</option> <option value="food">食物</option> <option value="hobby">ホビー</option> </select> <button type="submit">検索</button> </form>
これでコンポーネント・クラスの作成は完了です。
以下のようにコンポーネントを呼び出してブラウザから確認してみましょう。
<html> <body> <x-search-box /> </body> </html>
以下のように表示されました。
なお、シンプルなコンポーネントを使いたい場合、わざわざビューを用意するのはめんどうなものです。その場合は、以下のように--inline
オプションをつけるとrender()
内にヒアドキュメントを使った簡易バージョンのコードを作成してくれます。(もちろんビューは作成されません)
php artisan make:component YourComponent --inline
public function render() { return <<<'blade' <div> (ここにコンポーネントの内容を用意してください) </div> blade; }
コンポーネントへデータを送る
ここまででは、単にフォームを作っただけですので、毎回同じ内容が表示されることになりますが、次はコンポーネントにデータを送ってその内容に応じて表示内容を変化させてみましょう。
タグのプロパティを使う
では、まず<x-search-box />
にHTMLタグのようなtitle
プロパティを作ってタイトルが指定できるようにしてみましょう。
/app/View/Components/SearchBox.php
を開いて以下の部分を追加してください。
<?php namespace App\View\Components; use Illuminate\View\Component; class SearchBox extends Component { public $title; /** * Create a new component instance. * * @return void */ public function __construct($title) { $this->title = $title; } /** * Get the view / contents that represent the component. * * @return \Illuminate\View\View|string */ public function render() { return view('components.search-box'); } }
これで、<x-search-box />
タグでtitle
プロパティが使えるようになりました。
<x-search-box title="検索フォーム" />
では、/resources/views/components/search-box.blade.php
の中でタイトルを表示できるようにしておきましょう。
<form action="/search"> {{ $title }}<br> <input type="text" name="q"> <select> <option>▼以下から選択<option> <option value="book">本<option> <option value="music">音楽</option> <option value="game">ゲーム</option> <option value="food">食物</option> <option value="hobby">ホビー</option> </select> <button type="submit">検索</button> </form>
ブラウザで確認すると以下のように表示されます。
変数をコンポーネントに送る
今回のアップデートでこの部分が一番気をつけないといけないのですが、コンポーネントへ変数の内容を送る場合は、少し指定方法が変わっています。
これまでの流れで言うと、以下のように変数を指定したくなってしまいますが、これはうまくいきません。
<!-- 注意: これは間違った例です! --> <x-search-box title="{{ $title }}" />
正解を言いますと、以下のように独自の書き方をしないといけません。(ちょっとVue
に似たカンジですね)
<x-search-box :title="$title" />
クラス内からデータを送る
コンポーネント・クラスの中からでも簡単にコンポーネントへデータを送ることができます。
例えば、/app/View/Components/SearchBox.php
を開いてsections()
というメソッドを追加してください。
<?php namespace App\View\Components; use Illuminate\View\Component; class SearchBox extends Component { // 省略 public function sections() { return [ 'book' => '本', 'music' => '音楽', 'game' => 'ゲーム', 'food' => '食物', 'hobby' => 'ホビー' ]; } // 省略 }
実は、これで設定は完了です。(つまり、いちいちビューに変数としてバインディングしますよ、というコードを書かなくてもOKなんです😊✨)
では、/resources/views/components/search-box.blade.php
の中でsections()
の中身を使ってみましょう。
<form action="/search"> <!--省略--> <select> <option>▼以下から選択<option> @foreach($sections as $key => $name) <option value="{{ $key }}">{{ $name }}</option> @endforeach </select> <button type="submit">検索</button> </form>
これでクラス内のsections()
からデータを呼び出すことができます。
デフォルト値を判別した結果を送信する
例えば、先ほど作ったコードの中にはセレクトボックスがありましたが、この初期値を「ゲーム」にする例を見てみましょう。
/app/View/Components/SearchBox.php
を開いて中身を以下のようにしてください。
<?php namespace App\View\Components; use Illuminate\View\Component; class SearchBox extends Component { public $selected; // 初期値(sectionsのkey) /** * Create a new component instance. * * @return void */ public function __construct($selected) { $this->selected = $selected; } public function sections() { return [ 'book' => '本', 'music' => '音楽', 'game' => 'ゲーム', 'food' => '食物', 'hobby' => 'ホビー' ]; } public function isSelected($option) { return $option === $this->selected; } /** * Get the view / contents that represent the component. * * @return \Illuminate\View\View|string */ public function render() { return view('components.search-box'); } }
この中で重要なのが、__construct()
に$selected
を設定している部分とisSelected()
の部分です。
まず、$selected
は初期値、つまり今回の場合では game
が入ってくる予定になっています。
そして、isSelected()
が true
/ false
の値をコンポーネント側に送ることになります。(なお、別にisSelected()
ではなく、別の名前でも問題ありません)
では、コンポーネント側をみてみましょう。
<form action="/search"> <input type="text" name="q"> <select> <option>▼以下から選択<option> @foreach($sections as $key => $name) <option value="{{ $key }}" {{ $isSelected($key) ? 'selected="selected"' : '' }}>{{ $name }}</option> @endforeach </select> <button type="submit">検索</button> </form>
<option>
タグの中で三項演算子を使った条件分けがあるのがわかっていただけると思います。(ただし、$isSelected()
と$
マークがついていることに注意してください)
そして、コンポーネントを呼び出す部分はこうなります。
<x-search-box selected="game" />
スタイルやクラスを設定する
例えば、コンポーネントにstyle
やclass
などのHTMLプロパティを直接使いたい、以下のような場合です。
<x-search-box style="background:#eee;padding:10px;" :title="$title" />
この場合、$attributes
という変数を使うことで実装が可能です。
<form action="/search" {{ $attributes }}> <!-- 省略 --> </form>
この場合、実際にレンダリングされるHTMLは次のとおりです。
<form action="/search" style="background:#eee;padding:10px;"> <!-- 省略 --> </form>
なお、$attributes
は単なる変数ではなくComponentAttributeBag
のインスタンスなので、例えば以下のようにデータを追加することもできます。
<form action="/search" {{ $attributes->merge(['class' => 'my-class']) }}> <!--省略--> </form>
実際のHTMLはこうなります。
<form action="/search" class="my-class" style="background:#eee;padding:10px;"> <!-- 省略 --> </form>
※$attributes
には、only()
やexcept()
などのメソッドも使えます。詳しくはIlluminate\View\ComponentAttributeBagをご覧ください。
スロットをつかう
今回の新しい<x-***** />
タグでもスロットが使えます。
ひとつだけスロットを使う場合
スロットをひとつだけ使う場合は、とてもシンプルです。
例として送信ボタンをスロット化してみましょう。
<form action="/search"> <!-- 省略 --> <!-- ↓↓↓ここにボタンが入ります --> {{ $slot }} </form>
使い方はこうなります。
<x-search-box> <button type="submit">商品検索</button> </x-search-box>
スロットを複数使う場合
スロットが複数必要な場合は、x-slot
タグを使って各スロットに名前を付ける必要があります。
例を見てみましょう。
<x-search-box :title="$title"> <x-slot name="title">タイトル</x-slot> <x-slot name="button"> <button type="submit">商品検索</button> </x-slot> </x-search-box>
そして、コンポーネント内ではそれぞれname="*****"
の中身が変数として使えます。
<form action="/search"> {{ $title }} <!-- 省略 --> {{ $button }} </form>
実際のHTMLはこうなります。
<form action="/search"> タイトル <!-- 省略 --> <button type="submit">商品検索</button> </form>
無名コンポーネント
ここまでいろいろと紹介してきましたが、今回のアップデートでシンプルなコンポーネントの場合、実はコンポーネント・クラスを作る必要はなく、単に/resources/views/components
内にファイルを用意するだけでOKです。
例として、今日の日付を表示するだけのシンプルなコンポーネント「today」を作ってみましょう。
/resources/views/components/today.blade.php
というファイルを作って中身を以下のようにします。
{{ date('Y-m-d') }}
使い方はこうなります。
<x-today />
どうでしょう。
これだけで簡単にコンポーネントが使えるようになるわけです。
また、もちろんデータをコンポーネント側に送信することもできて、例えば挨拶文をコンポーネントで表示する場合は以下のようにするとOKです。
{{ $greeting }} {{ date('Y-m-d') }}
<x-today greeting="こんにちは。" />
実際にブラウザで表示するとこうなります。
なお、この無名コンポーネントで気をつけないといけないのが、「どれがプロパティでどれが変数の送信かわからなくなる」という部分です。
例えば、以下の例を見てください。
<x-today style="font-weight:bold;" greeting="こんにちは。" />
これは先ほどの例にstyle
を追加したものですが、これを$attributes
を使って以下のようにプロパティを取得しようとすると、少し問題が発生します。
<div {{ $attributes }}> {{ $greeting }} {{ date('Y-m-d') }} </div>
実際のHTMLはこうなります。
<div style="font-weight:bold;" greeting="こんにちは。"> こんにちは。 2020-03-02 </div>
そうです。greeting
までstyle
のように使われてしまうわけです。
これを解決するためにあるのが、@props()
です。
今回の例で言うと以下のようにすることでgreeting
を除外することができます。
@props(['greeting']) <div {{ $attributes }}> {{ $greeting }} {{ date('Y-m-d') }} </div>
出力結果はこうなりました。
<div style="font-weight:bold;"> こんにちは。 2020-03-02 </div>
おわりに
ということで今回は、Laravel 7.x
の新しいコンポーネントについてご紹介しました。
専用のタグをHTMLと同じように使えるようになるので、デザイナーさんが見ても保守がしやすくなるんじゃないでしょうか。
また、もちろんコンポーネントは使い回しがしやすいので一度いいものをつくっておけばあとで楽ができることになります。
ぜひ皆さんも将来のために頑張ってコンポーネントを作って見てくださいね。
ではでは〜!