ブログ名

PHP LaravelでWEBアプリ開発 【ビュー③】

前回の記事で紹介したBladeには、レイアウトを継承し、いくつものテンプレートをセクションとして組み合わせてレイアウトを作成していく機能が用意されています。
今回はBlade式のレイアウトの作り方について解説します。

レイアウトの定義と継承

前回の記事までで紹介してきたテンプレート(index.blade.php)は、基本的に一つのテンプレートの中でページ全体の表示を記述していました。
これは一つ一つのテンプレートが完結していてわかりやすいのですが、多数のページを持ったWebサイトを構築する場合はかなり面倒になります。
多くのページがあるサイトでは、全体の統一感を持たせるために共通したデザインでページが表示されるようにしているのが一般的です。
その為にはサイト全体で共通するベースとなるレイアウトを用意して、それをもとにして各ページのデザインを行うようなやり方をする必要があります。
こうしたサイト全体を統一したデザインでレイアウトするために、Bladeには「継承」と「セクション」という機能が用意されています。

継承とは

「継承」は、PHPのクラスで利用しているものと同様です、
既に存在しているクラスを受け継いで、新しいクラスを作成するための機能です。

Bladeの継承も、考え方は同じです。
すなわち、既にあるテンプレートのレイアウトを継承して、新しいテンプレートを作成します。
継承元のテンプレートに用意されている表示は、すべて新たに継承して作られたテンプレートでそのまま表示されます。
新しいテンプレートは、継承元にはない要素を追加するだけとなります。

セクションとは

軽症でページをデザインする時に、ページ内の要素として活用されるのが「セクション」です。
セクションはレイアウト内に用意される区画です。
レイアウト用テンプレートでは、ページ内にセクションの区画を用意しておき、継承して作られた新しいテンプレートで、そのセクションの内容を用意しておきます。
こうすることで、指定の内容がセクションに組み込まれ、レイアウトが完成します。

この「継承」と「セクション」により、ベースとなるレイアウトから同じレイアウトのページをいくつも作っていくことが可能となります。

@sectionと@yield

セクションの利用のために、Bladeには@sectoinと@yieldという2つのディレクティブが用意されています。
この2つを使いこなすことが、レイアウト作成のポイントとなります。

@sectionについて

レイアウトで様々な区画を定義するために用いられるのが、@sectionです。
@sectionはページに表示されるコンテンツの区画を定義します。
これは以下のように記述します。

@section(名前)
・・・・・・表示内容・・・・・・
@endsection

これで、指定した名前でセクションが用意されます。
このセクションは、同じ名前の@yieldにはめ込まれて表示されます。
また@sectionは、継承したページで@sectionによって上書きすることもできます。

@yieldについて

@yieldは、セクションの内容をはめ込んで表示するためのものです。
これは以下のように記述します。

@yield(名前)

@yieldで指定した名前のセクションがあると、そのセクションが@yieldのところにはめ込まれます。
@yieldは配置場所を示すためのもので、具体的なコンテンツを用意する必要はないため、@endyieldのようなものはありません。

ベース・レイアウトを作成する

では、実際に簡単なサンプルを作成してみます。
まずはベースとなるレイアウト用のテンプレートからです。

レイアウトのテンプレートは、「resources」内の「views」フォルダの中にある「layouts」フォルダに作成します。
「layouts」フォルダの中に、「helloapp.blade.php」という名前で新しいファイルを作成してください。
これがベースとなるレイアウト用のファイルです。
作成したら、以下のように記述しておきます。

[helloapp.blade.php]

<html>
<head>
    <title>@yield('title')</title>
    <style>
    body {font-size:16pt; color:#999; mergin:5px;}
    .menutitle {font-size:14pt; font-weight:bold; margin:0px;}
    .content {margin:10px;}
    .footer {text-align:right; font-size:10pt; margin:10px;
                border-bottom:solid 1px #ccc; color:#ccc;}
    </style>
</head>
<body>
    <h1>@yield('title')</h1>
    @section('menubar')
    <ul>
        <p class="menutitle">※メニュー</p>
        <li>@show</li>
    </ul>
    <div class="content">
    @yield('content')
    </div>
    <div class="footer">
    @yield('footer')
    </div>
</body>
</html>

ここでは何ヵ所かにディレクティブが用意されています。
以下に簡単にまとめておきます。

    <title>@yield('title')</title>
    <h1>@yield('title')</h1>

タイトル表示の部分には、'title'という名前で@yieldを用意してあります。
ここにtitleのコンテンツを設定します。
またbody 内のh1にも同様に@yieldを用意して、タイトルを表示するようにしてあります。

    @section('menubar')

これはメニュー表示の区画です。
セクションは区画を定義するものですが、一番土台となるレイアウトで@sectionを用意する場合は、@endsectionではなく@showというディレクティブでセクションの終わりを指定します。

    @yield('content')
    @yield('footer')

残る2つの@yieldはコンテンツとフッターをはめ込むために用意しています。
継承したレイアウトで、これらの名前で@sectionを用意しておけば、そのセクションのレイアウトがこれらの@yeildにはめ込まれることになります。

継承レイアウトの作成

続いてこのレイアウト用のテンプレートを継承して、実際のWebページに使うテンプレートを用意します。
「index.blade.php」を以下のように修正します。

[index.blade.php]

@extends('layouts.helloapp')

@section('title', 'Index')

@section('menubar')
    @parent
    インデックスページ
@endsection

@section('content')
    <p>ここが本文のコンテンツ</P>
    <p>必要なだけ記述できます。</p>
@endsection

@section('footer')
copyright 2019
@endsection

今回は、今までのindex.blade.phpとは大きく変わって、HTMLらしい記述がほとんどなくなっています。

@extendsについて

まず最初に設定すべきは、レイアウトの継承設定です。
これは以下のように記述します。

@extends('layouts.helloapp')

これで「layouts」フォルダ内の「helloapp.blade.php」というレイアウトファイルをロードし、親レイアウトとして継承します。
これがないとレイアウトの継承そのものが機能しなくなりますので注意してください。

@sectionの書き方

続いて@sectionの表示内容の書き方です。
これには2通りあります。
1つはタイトルの表示に使った書き方です。

@section('title', 'Index')

単純にテキストや数字などをセクションに表示させるだけなら、@sectionの引数内に当てはめるセクション名と、そこに表示する値をそれぞれ引数に用意します。
今回は'title'という名前のセクションに、'Index'というテキスト値を設定します。

もう1つの書き方は、@endsectionを併用した書き方です。
例えば以下の部分となります。

@section('menubar')
    @parent
    インデックスページ
@endsection

@sectionから@endsectionまでの部分が、セクションの内容になります。
親レイアウトに'menubar'というyieldがあれば、そこにはめ込まれて表示されます。
ただし今回の親レイアウトには、'menubar'という@yieldではなく、@sectionがあります。
この場合、@sectionは上書きして置き換わります。
このセクションには、@parentというディレクティブがありますが、これは親レイアウトのセクションを示します。
親の@sectionに子の@sectionを指定する場合、親の@section部分を子のセクションが上書きします。
この時、親にあるセクションも残して表示したいこともあります。
このような時に、@parentで親のセクションをはめ込んで表示させることができます。

その他の、@section('content')や@section('footer')などは、親レイアウト側は@yieldで場所を指定しているだけなので、その部分にセクションがはめ込まれて表示されます。

表示を確認する

親と子のレイアウトがそれぞれ用意できたら、実際に/helloにアクセスして表示を確認します。
helloapp.blade.phpのレイアウトに、index.blade.phpに用意したセクションがはめ込まれて表示されていることがわかります。

このようにレイアウトの継承を利用することで、子側のテンプレートにはセクションに表示する内容だけを記述すれば済むようになります。
そしてすべて親のレイアウトを継承することで、同じレイアウトでページが表示されるようになります。

f:id:shizuuuka0202:20191225230303j:plain

コンポーネントについて

継承を利用したレイアウトでは、親レイアウトの必要な部分に子側のセクションをはめ込んで表示を完成させました。
このやり方は、全体を一式揃えて作成するには大変便利な方法です。

しかし、時には一部を切り離して作成し、組み込みたいということもあります。
例えばタイトルやフッターの表示などは、それぞれ独立した部品として用意しておければとても便利です。
各レイアウトからタイトルやフッターの部分を切り離すことで、純粋にそのページのコンテンツだけに集中することができます。
また必要に応じてされらの部品を修正すれば、サイト全体の表示を更新できるようになります。

このような場合に用いられるのが「コンポーネント」です。
コンポーネントは、1つのテンプレートとして独立して用意されるレイアウト用の部品です。
コンポーネントはテンプレートから読み込まれ、必要な場所に組み込まれます。

@componentディレクティブ

コンポーネントは、普通のテンプレートとして内容を作成します。
コンポーネントといっても、書き方は普通のテンプレートと違いはありません。
作成されたコンポーネントは、@componentというディレクティブを使って表示場所を設定されます。
これは以下のように記述します。

@component(名前)
・・・・・・コンポーネントの表示内容・・・・・・
@endcomponent

コンポーネントの名前は、「views」フォルダにあるファイル名で指定します。
例えば、「components」フォルダ内に「ok.blade.php」というファイル名で用意した場合は、`components.ok'と指定すれば読み込むことができます。

コンポーネントを作成する

では実際に、コンポーネントを作成してみましょう。
「views」フォルダの中に、新たに「components」というフォルダを用意して、この中にコンポーネントを用意します。
「components」フォルダを作成して、その中に「message.blade.php」という名前でファイルを作成します。
以下のようにソースコードを記述します。

[message.blade.php]

<style>
.message {border:double 4px #ccc; margin:10px; padding:10px;
            background-color:#fafafa}
.msg_title {margin:10px 20px; font-size:16pt; font-weight:bold;}
.msg_content {margin:10px 20px; font-size:12pt}
</style>
<div class="message">
    <p class="msg_title">{{$msg_title}}</p>
    <p class="msg_content">{{$msg_content}}</p>
</div>

ここでは、{{$msg_title}}と{{$msg_content}}という変数を表示するテンプレートを用意しておきました。
それ以外に難しい点はなく、ごく普通のHTMLタグベースのテンプレートです。

コンポーネントを組み込む

ではこのmessageコンポーネントをテンプレートに組み込んで表示させてみましょう。
ここでは、index.blade.phpのcontentセクションの中に組み込んでみます。
@section('content')のセクション部分を以下のように修正してください。

[index.blade.php]

@extends('layouts.helloapp')

@section('title', 'Index')

@section('menubar')
    @parent
    インデックスページ
@endsection

@section('content')
    <p>ここが本文のコンテンツ</P>
    <p>必要なだけ記述できます。</p>

    @component('components.message')
        @slot('msg_title')
        CAUTION!
        @endslot

        @slot('msg_content')
        これはメッセージの表示です。
        @endslot
    @endcomponent

@endsection

@section('footer')
copyright 2019
@endsection

f:id:shizuuuka0202:20191225230530j:plain

修正したら、/helloにアクセスしてみましょう。
コンテンツの下部に、四角い枠線で囲われたメッセージが表示されます。
これが、messageコンポーネントの表示です。
このようにコンポーネントを組み込むことで、様々な表示をレイアウトの中に組み込むことができます。

スロットについて

ここでは、message.blade.phpの中に{{$msg_title}}と{{$msg_content}}という変数を用意していました。
コンポーネントを利用する際には、これらの変数に値を渡さなければなりません。
それを行っているのが、「スロット」です。
スロットは{{ }}で指定された変数に値を設定するためのものです。
これは以下のように記述します。

@slot(名前)
・・・・・・設定する内容・・・・・・
@endslot

ここでは、@component('components.message')として「components」フォルダのmessage.blade.phpコンポーネントとして組み込むことを指定しています。
そしてこの@component内に、@slot('msg_title')と@slot('msg_content')を用意してあります。
これらのスロットの内容が、コンポーネントの$msg_titleと$msg_contentにはめ込まれて表示されます。

コンポーネントを使用して、ページに表示される様々な部品を用意しておけば、それらを組み合わせてページをデザインすることができるようになります。
そしてそれらはすべて統一されたデザインで表示されることになります。

サブビューについて

コンポーネントは使い勝手の良い部品ですが、ページフッターやサイドバーなどのように定型のコンテンツなどは、もっと単純に「用意されたテンプレートをただはめ込んで表示できればいい」というものであったりします。
こうしたものは、テンプレートを読み込んでそのままテンプレート内にはめ込むことができます。
こうした、「あるビューから別のビューを読み込んではめ込んだもの」をサブビューと言います。

サブビューは専用のテンプレートがあるわけではなく、ごく普通のテンプレートとして用意しておき、それをそのまま読み込んで表示するだけのシンプルな仕組みです。

スロットのようなものは、サブビューでは使えません。
ただし、コントローラからテンプレートに渡された変数などは、そのままサブビューのテンプレート内でも使用することができます。
また、サブビューを読み込む際に変数を渡すこともできます。
サブビューは以下のようにして読み込みます。

@include(テンプレート名, [・・・・・・値の指定・・・・・・])

第1引数には、読み込むテンプレートファイルを指定します。
単にテンプレートを表示するだけならこれだけで構いません。
何らかの値をサブビューのテンプレートに渡したい場合は、第2引数に配列(連想配列)にまとめた値を用意しておきます。

message.blade.phpをサブビューで読み込む

では実際にやってみます。
先ほど作成したmessage.blade.phpを、サブビューとしてコンテンツ内に読み込み、表示させてみます。
index.blade.phpの@section('content')部分を以下のように修正します。

[index.blade.php]

@extends('layouts.helloapp')

@section('title', 'Index')

@section('menubar')
    @parent
    インデックスページ
@endsection

@section('content')
    <p>ここが本文のコンテンツ</P>
    <p>必要なだけ記述できます。</p>

    @include('components.message', ['msg_title'=>'OK', 'msg_content'=>'サブビューの表示'])

@endsection

@section('footer')
copyright 2019
@endsection

f:id:shizuuuka0202:20191225231012j:plain

/helloにアクセスすると、先ほどと同様にメッセージのボックスが表示されます。
ここでは、@includeの第2引数にmsg_titleとmsg_contentの値を配列として渡しています。
こうすることで、必要な値をテンプレート側に渡して表示させることができます。

@eachのよるコレクションビュー

表示の一部を切り離して作成する、ということを考えたとき、意外と利用頻度が高いのが「繰り返しの表示」です。
例えばデータを繰り返しディレクティブなどで表示させるとき、表示する各項目のレイアウトを切り離して作成出来たら便利です。
テーブルの表示、リストの表示、通常のテキストコンテンツの表示など、あらかじめ用意しておいたデータを繰り返し出力していくことはよくあります。
こうした場合に役立つのが「@each」ディレクティブです。

@eachディレクティブはあらかじめ用意されていた配列やコレクションから順に値を取り出して、指定のテンプレートにはめ込んで出力するものです。
以下のように記述します。

@each(テンプレート名, 配列, 変数名)

第2引数には、表示するデータをまとめた配列やコレクションを指定します。
そして第3引数には、配列から取り出したデータを代入する変数名を指定します。
テンプレート側では、この変数を使用してデータを受け取り、表示を行います。

@eachによる表示を利用する

では実際ににやってみます。
まずは、繰り返し表示する項目のテンプレートを用意します。
今回は「components」フォルダの中に「item.blade.php」という名前でファイルを作成します。
内容は以下のように記述します。

[item.blade.php]

<li>{{$item['name']}} [{{$item['mail']}}]</li>

ここでは、$itemという変数からnameとmailの値を取り出して表示しています。
従って、nameとmailをまとめた配列データをさらに配列としてまとめたものを用意します。
続いて、index.blade.phpの@section('content')を修正します。
@eachを使用して、item.blade.phpでデータの項目を表示するようにします。

[index.blade.php]

@extends('layouts.helloapp')

@section('title', 'Index')

@section('menubar')
    @parent
    インデックスページ
@endsection

@section('content')
    <p>ここが本文のコンテンツ</P>

    @each('components.item', $data, 'item')

@endsection

@section('footer')
copyright 2019
@endsection

ここでは@eachで、$dataという変数を'item'に入れて繰り返すようにしています。
アクションメソッド側では$data変数を用意しておき、テンプレートに渡します。
HelloController.phpのindexアクションメソッドを下記のように修正します。

[HelloController.php]

    public function index() {
        $data = [
            ['name'=>'テスト1', 'mail'=>'test1@example.com'],
            ['name'=>'テスト2', 'mail'=>'test2@example.com'],
            ['name'=>'テスト3', 'mail'=>'test3@example.com']
        ];
        return view('hello.index', ['data'=>$data]);
    }

f:id:shizuuuka0202:20191225231248j:plain

これで完成です。
$dataには、['name'=>○○, 'mail'=>○○]という形式の配列をデータとしてまとめてあります。
@eachでは、この一つ一つの配列が取り出され、そこからnameとmailの値を取り出して項目として出力していきます。
@eachは、テンプレート名を指定して呼び出すだけなので、新しいテンプレートを作成したらテンプレート名を書き換えるだけでデータの表示を変えることができます。


次の記事へ

前の記事へ 目次へ戻る