Laravel 5.7 のテンプレートエンジン Blade の使い方( @yield , @section , @include )

メモ:

Blade では、サイト全体で共通する部分を1つのテンプレートとして用意し、それぞれの画面でベースとなるテンプレートを継承するような処理を行うことができます。また、テンプレートの中では if などの制御構文を使うことができ柔軟なレイアウトを作成することができます。

ここでは、ディレクトリを使った整理の仕方も見ていきたいので次のような構造を用意します。

resources
    |-views
         |-layouts
                |-default.blade.php
         |-contact
                |-form.blade.php

ベーステンプレートの使い方

例えば、サイト全体で共通する部分を resources\views\layouts ディレクトリに次のようなテンプレートとして用意します。

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 
    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
 
    <title>{{ config('app.name', 'bnote') }}</title>
 
    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body class="site">
    <div class="container container-stack">
        @yield('content')
    </div>
</body>
</html>

上記テンプレートで継承に絡む部分は、 @yield(‘content’) になります。このテンプレートをベースとして @yield(‘content’) の部分が差し替わります。

ついでに上記テンプレートの中に登場するヘルパなどについても確認したいと思います。

app()->getLocale()

app()->getLocale() は、言語指定の部分を コンフィグファイルから取得します。Laravel プロジェクトの config ディレクトリにある app.php に設定された app.locale の値を取得します。

config\app.php
'locale' => 'ja',

csrf_token()

クロス・サイト・リクエスト・フォージェリ ( CSRF ) の対策としてトークン値を取得します。

config(‘app.name’, ‘bnote’)

title タグで使用している config は、コンフィグファイルから値を取得します。

上記例では、 config ディレクトリにある app.php に設定された name の値を取得するように指定していますが app.php は次のようになっています。

'name' => env('APP_NAME', 'Laravel'),

app.php の中では、 env 関数が呼ばれているため プロジェクトディレクトリ直下の .env ファイルに設定された APP_NAME の値を取得します。

APP_NAME=bnote

config(‘app.name’, ‘bnote’) は、結果として .env ファイルの APP_NAME に設定された値を取得します。

asset(‘css/app.css’)

asset 関数は、 ホスト名/css/app.css のパスを返します。

ファイルの実態は、Laravel のプロジェクトディレクトリにある public ディレクトリを参照します。

上記例では、 public\css\ ディレクトリに app.css ファイルを配置しています。

@yield

継承にあたる部分ですが、 @yield が別途定義した @section に置き換わる形で実行されます。プログラム中では、 @section の記述があるファイルを呼び出すので継承というイメージなのだと思います。

例えば、次のように継承します。

@extends('layouts.default')
 
@section('content')
<div class="row col">
    <form method="POST" action="#">
        <div class="form-group">
            <label for="formInputName">Name</label>
            <input type="text" class="form-control" id="formInputName" name="name" value="">
        </div>
        <div class="form-group">
            <label for="formInputEmail">Email address</label>
            <input type="text" class="form-control" id="formInputEmail" name="email" value="">
        </div>
        <button type="submit" class="btn btn-ghost_w">Submit</button>
    </form>
</div>
@endsection

@extends が継承にあたる部分となります。継承したいファイルが layouts ディレクトリに存在するためディレクトリ名とファイル名を「.(ドット)」で繋げています。

ビューの呼び出しも同じように「.(ドット)」で繋げて記述します。

置換先の識別には、引数に指定した名前が使われます。ここでは、 ‘content’ がそれにあたります。

return view('contact.form');

以上で default.blade.php を継承し @yield の部分が @section ~ @endsection の部分で置き換わった形で出力されます。

ちなみに ブロックとして定義する必要が無い場合 @section は、次のように書くこともできます。

@section('content', 'hoge')

子テンプレートに定義した内容を親テンプレートに展開する( @show )

親テンプレートに @section ~ @show を用意することで、子テンプレート側からテンプレートの展開をコントロールできるようになります。

子テンプレートの定義により展開したい部分を @section ~ @show で括ります。

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{{ config('app.name', 'bnote') }}</title>
</head>
<body class="site">
    <div class="container container-stack">
        @section('sidebar')

        @show

        @yield('content')
    </div>
</body>
</html>

子テンプレート側は、親テンプレートに差し込みたい時のみ @section ~ @endsection を記述し展開します。ページ別に追加したり追加したくない部品がある場合に使える機能になります。

@extends('layouts.default')

@section('sidebar')
    <nav class="global-nav">
        <ul class="nav-list">
        <li class="nav-label">Top</li>
        </ul>
    </nav>
@endsection
 
@section('content')
<div class="row col">
    <form method="POST" action="#">
        <div class="form-group">
            <label for="formInputName">Name</label>
            <input type="text" class="form-control" id="formInputName" name="name" value="">
        </div>
        <button type="submit" class="btn btn-ghost_w">Submit</button>
    </form>
</div>
@endsection

親テンプレートの section に子テンプレートの section を追加したい( @parent )

子テンプレートの定義を親テンプレートに差し込むには、 @section ~ @show を使いましたが両方合わせたものを出力する事も可能です。

この場合、子テンプレート側で @parent と記述することで親テンプレートの定義も展開することができます。

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{{ config('app.name', 'bnote') }}</title>
</head>
<body class="site">
    <div class="container container-stack">
        <nav class="global-nav">
            <ul class="nav-list">
            @section('sidebar')
                <li>親定義</li>
            @show
            </ul>
        </nav>

        @yield('content')
    </div>
</body>
</html>

@parent の部分に親テンプレートで定義した section が展開され、子定義側に記述した section と一緒に出力されます。

@extends('layouts.default')

@section('sidebar')
    @parent
    <li class="nav-label">子定義</li>
@endsection
 
@section('content')
<div class="row col">
    <form method="POST" action="#">
        <div class="form-group">
            <label for="formInputName">Name</label>
            <input type="text" class="form-control" id="formInputName" name="name" value="">
        </div>
        <button type="submit" class="btn btn-ghost_w">Submit</button>
    </form>
</div>
@endsection

※ Laravel 5.7 日本語ドキュメントには @@parent と記載がありましたが @parent で動作しました。

サブビューの読み込み

Blade テンプレートエンジンには、継承以外に include という外部テンプレートを読み込む機能も存在します。

大きな違いは、読込元のビューで使用可能な変数は、読込先のビューでも利用可能ということです。

読込元のテンプレートに @include を用意し、読み込むテンプレートを指定します。

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>{{ config('app.name', 'bnote') }}</title>
</head>
<body class="site">
    <div class="container container-stack">
    </div>
@include('layouts.footer')
</body>
</html>

読込先のテンプレートは次のように用意します。

<footer id="footer" class="footer">
    <div class="copyright col">
        <p>Copyright 1997-2018 BBB All rights reserved.</p>
    </div>
</footer>

以上でテンプレートの継承とインクルードが使えるようになりました。