Laravelのビュー(blade)の使い方

Laravelを使って研究室の材料・サンプル管理システムを開発していきます。

以下の前回の終了時点の状態を再現できます。

$ git clone https://kiku3.tsbio.info/git/study-laravel.git study_laravel
$ git checkout step1

一度ここで自分のブランチをつくっておくとよいです。

$ git checkout -b my1

今後は基本的にsrcディレクトリで作業します。VisualStudioCodeを使っている場合は、code srcとするとよいです。

ルート(route)とビュー(view)の設定

WWWの元々の仕組みでは、http://host/path/to/file というURLが、hostというコンピュータの、/path/to/file というファイルを指し示していました。 fileがPHPのファイルであったとき、そこに書かれたプログラムに従ってつくられたHTMLをブラウザーが表示します。

laravelではURLとPHPのファイルが分離されています。 つまり、どのURLに対して、どのスクリプトを動かすのかという関係を自由に設定することができ、それはルート(route)の設定といいます。

静的なHTMLを返すルートの定義

ブラウザーがベースURL (ここでは/とします)にアクセスしたときに、下記のようなものが表示されるようにしてきます。

ウェブサイトイメージ

header

Lab’s stock

  • オリゴDNA
main
  • 一覧
  • 追加
id名前配列作成日所有者
1gene1_abcACGAGTC…240102佐藤
2gene2_FTCGAAGTC…250516鈴木
3gene3_RGGCAATC…260720高橋

このソースコード(HTML)を resoureces/views ディレクトリに index.blade.php という名前でつくります。 この index.blade.php をビュー(view)と呼びます。

resources/views/index.blade.php
<!DOCTYPE html>
<html lang="ja">
<body>
  <header>
    <h1>My Lab's Stock</h1>
    <ul id='module-tabs'>
      <li><a id='oligo-tab' href='/oligo'>オリゴDNA</a></li>
    </ul>
  </header>
  <main>
    <div class='oligo-panel'>
      <ul id='oligo-tabs'>
        <li><a id='oligo-index-tab' href='/oligo'>一覧</a></li>
        <li><a id='oligo-create-tab' href='/oligo/create'>追加</a></li>
      </ul>
      <div class='feature-panels'>
        <div id='oligo-index-panel'>
          <table>
            <thead>
              <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
            </thead>
            <tbody>
              <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
              <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
              <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </main>
</body>
</html>

/へのアクセスで、resources/views/index.blade.php が表示されるように routes/web.php を以下のように編集します。

routes/web.php
<?php
 
use Illuminate\Support\Facades\Route;
 
/*
 * ビューを返すだけのルート
 */
Route::get('/', function () {
    return view('index');  // ここで指定した名前に.blade.phpをつけたファイルをresources/views以下につくる
});

http://localhost:8002/ にアクセスすると、index.blade.php が表示されることを確かめてください。

bladeを使ったHTMLの構造化

bladeはlaravelの既定のテンプレートエンジンです。 bladeはHTMLテンプレートの中に、PHPのデータ(変数や配列)を埋め込んで、最終的なウェブページをつくり出します。 以下で示すように、レイアウトの統一や、<や>などのエスケープ処理を簡単にできるようにしてくれます。

bladeの詳しい説明は https://readouble.com/laravel/13.x/ja/blade.html を参照してください。

リソース(オリゴDNA)の一覧(index)と登録(create)のページをつくっていきます。それぞれのページで表示する固有の部分を resources/views/oligo 以下に、index.blade.php と create.blade.php としてつくります。

oligo/index.blade.php
<table>
  <thead>
    <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
  </thead>
  <tbody>
    <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
    <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
    <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
  </tbody>
</table>
create.blade.phpのほうは後ほどつくることにします。 これはまだHTMLとしては不完全ですが、/oligoにアクセスすると表示されることを確認します。

/oligo へのアクセスを resources/views/oligo 以下の index.blade.php へ、/oligo/create へのアクセスを create.blade.php へ経路を設定するために、routes/web.php を編集します。

routes/web.php
<?php
 
use Illuminate\Support\Facades\Route;
 
/*
 * ビューを返すだけのルート
 */
Route::get('/', function () {
    return view('index');
});
Route::get('/oligo', function () {
    return view('oligo.index');  // resources/views以下のディレクトリは . でつなぐ
});
Route::get('/oligo/create', function () {
    return view('oligo.create');  // resources/views以下のディレクトリは . でつなぐ
});

共通レイアウトのつくり方1 component

indexもcreateもHTMLの構造の中で同じ位置にあります。ここではそれをfeatureというクラスにします。 研究室の材料として管理するリソース(オリゴDNAとか)を module というレベルで表し、それぞれのリソース管理に必要な機能を feature というレベルで表すことにします。

indexとcreateを同じレベルの構造とするためにdivタグでくくります。そしてclass属性としてfeature-panelをつけ、idをoligo-indexとoligo-createとします。 つまりHTMLを以下のような構造にします。

</p>
<div id='oligo-index' class='feature-panel'>index.blade.phpの中身</div>

外側の div.feature-panel の部分は、index.blade.php と create.blade.php で同じなので、これをbladeのコンポーネント(featureコンポーネント)としてつくります。

コンポーネントは resources/views/components ディレクトリ以下につくります。今回は feature.blade.php とします。

components/feature.blade.php
<div class='feature-panel' {{ $attributes }}>
  {{ $slot }}
</div>

このfeature.blade.phpを使うために、index.blade.phpを以下の編集します。

oligo/index.blade.php
<x-feature id='oligo-index'>
  <table>
    <thead>
      <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
    </thead>
    <tbody>
      <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
      <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
      <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
    </tbody>
  </table>
</x-feature>

/oligo へアクセスすると以下のHTMLが返ってきます。

<div class='feature-panel' id="oligo-index">
  <table>
    <thead>
      <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
    </thead>
    <tbody>
      <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
      <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
      <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
    </tbody>
  </table>
</div>

routes/web.php が /oligo へのアクセスに対し resources/views/oligo/index.blade.php を返すように設定されています。index.blade.phpの中にある <x-feature> がbladeに対して、components/feature.blade.phpを呼ぶ指示になっています。このとき <x-feature> で囲まれた部分が、feature.blade.phpの中では $slot という変数に入ります。同時に、x-featureタグの属性 (今回はid='oligo-index') が $attributes に入ります($attributesIlluminate\View\ComponentAttributeBagのオブジェクト)。feature.blade.php自身もbladeによってレンダリングされ、上記のHTMLが返ってきます。

bladeのビューの中では x- で始まるHTMLタグはコンポーネントを呼び出します。

{{ ... }}<?php echo ... ?> に置き換えられます。

共通レイアウトのつくり方2 extends

featureコンポーネントを使うことで、index.blade.phpとcreate.blade.phpで共通の構造 <div class='feature-panel'> がつくれるようになりました。

これをoligo moduleのfeaturesにいれる構造をつくります。モジュールにはそれぞれ独自の機能が必要なので、これは個別に作成します。oligo/module.blade.phpを以下のようにつくります。

oligo/module.blade.php
<!-- モジュールのメニュー -->
<nav>
  <ul class='module-tabs' role='tablist'>
    <li><a href='/oligo/index' role='tab' id='oligo-index-tab'>一覧</a></li>
    <li><a href='/oligo/create' role='tab' id='oligo-create-tab'>追加</a></li>
  </ul>
</nav>
 
<!-- featureが並ぶところ -->
<div class='features'>
  @yield('feature')
</div>

oligo/module.blade.php を呼ぶように oligo/index.blade.php を以下のように編集します。

oligo/index.blade.php
@extends('oligo.module')
 
@section('feature')
<x-feature id='oligo-index'>
  <table>
    <thead>
      <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
    </thead>
    <tbody>
      <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
      <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
      <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
    </tbody>
  </table>
</x-feature>
@endsection

index.blade.php 内の @section から @endsection で囲まれた範囲が feature という変数に入ります。oligo/module.blade.php 側では @yield('feature’) とすることで、feature変数 (すなわち @section から @endsectionの部分)に置き換えられます。@extendsの部分が oligo/module.blade.phpを呼ぶ部分です。

/oligo へアクセスすると以下のHTMLが返ってきます。

<!-- モジュールのメニュー -->
<nav>
  <ul class='module-tabs' role='tablist'>
    <li><a href='/oligo/index' role='tab' id='oligo-index-tab'>一覧</a></li>
    <li><a href='/oligo/create' role='tab' id='oligo-create-tab'>追加</a></li>
  </ul>
</nav>

<!-- featureが並ぶところ -->
<div class='features'>
  <div class='feature-panel' id="oligo-index">
  <table>
    <thead>
      <tr><th>id</th><th>名前</th><th>配列</th><th>作成日</th><th>所有者</th></tr>
    </thead>
    <tbody>
      <tr><td>1</td><td>gene1_abc</td><td>ACGAGTC...</td><td>240102</td><td>佐藤</td></tr>
      <tr><td>2</td><td>gene2_F</td><td>TCGAAGTC...</td><td>250516</td><td>鈴木</td></tr>
      <tr><td>3</td><td>gene3_R</td><td>GGCAATC...</td><td>260720</td><td>高橋</td></tr>
    </tbody>
  </table>
</div>
</div>

さらに上位の構造をつくる

oligo/module.blade.php の div.module-panel をかぶせます。それを app.blade.php にいれて、さらに外側に html.blade.php を加えます。

bladeコンポーネントはファイルの置き場所が定められています。なので、個別の設定がはいるものはextendsを使うようにするとよいです。

oligo/module.blade.php
@extends('layouts.app')
 
@section('module')
  <x-module id='oligo'>
    <!-- モジュールのメニュー -->
    <nav>
      <ul class='module-tabs' role='tablist'>
        <li><a href='/oligo/index' role='tab' id='oligo-index-tab'>一覧</a></li>
        <li><a href='/oligo/create' role='tab' id='oligo-create-tab'>追加</a></li>
      </ul>
    </nav>
    
    <!-- featureが並ぶところ -->
    <div class='features'>
      @yield('feature')
    </div>
  </x-module>
@endsection
components/module.blade.php
<div class='module-panel' id="$module" role="tabpanel">
  {{ $slot }}
</div>
layouts/app.blade.php
@extends('layouts.html')
 
@section('body')
  <header>
    <h1>My Lab's Stock</h1>
    <ul id='module-tabs'>
      <li><a id='oligo-tab' href='/oligo'>オリゴDNA</a></li>
    </ul>
  </header>
  <main id='modules'>
    @yield('module')
  </main>
@endsection
layouts/html.blade.php
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>My Lab's Stock</title>
  </head>
  <body>
    @yield('body')
  </body>
</html>

module1/feature1.blade.php
  x-feature によって components/feature.blade.php を呼ぶ
  extends によって module1/module.blade.php を呼ぶ

module1/module.blade.php
  x-module によって components/module.blade.php を呼ぶ
  extends によって layouts/app.blade.php を呼ぶ

layouts/app.blade.php
  extends によって layouts/html.blade.php を呼ぶ

<!DOCTYPE html> ← layouts/html.blade.php
<html lang="ja">
  <head>...</head>
  <body>
    <header>...</header> ← layouts/app.blade.php
    <main id='modules'>
      <div class='module-panel'> ← components/module.blade.php
        <nav>...</nav> ← module1/module.blade.php
        <div class='features'>
          <div class='feature-panel'> ← components/feature.blade.php
            <!-- module1/feature1.blade.php -->
          </div>
        </div>
      </div>
    </main>
  </body>
</html>

bladeの使いかた

bladeの詳しい説明は https://readouble.com/laravel/13.x/ja/blade.html を参照してください。

PHPのコードを書く

bladeのビューはPHPのスクリプトでもあるので、以下のように書くことができる。

<?php
  echo 'Hello';
?>

bladeの @php ディレクティブを使うと次のように書ける。

@php(echo 'Hello';)

複数行に渡る場合は次のようにする。

@php
  $message = "Hello";
  echo $message;
@endphp

出力

PHPの変数の値は{{ }}で囲むと、echo htmlspecialchars(…) という処理がされる。

@php($var = 'Hello');
{{ $var }} # Hello になる
@php($var = '<h1>Hello</h1>');
{{ $var }} # &lt;h1&gt;Hello&lt;/h1&gt; になる
htmlspecialcharsされたくないときは{!! !!}で囲む。
@php($var = '<h1>Hello</h1>');
{!! $var !!} # <h1>Hello</h1> になる

制御構造

モジュールのメニューは foreach を使って繰り返しにする。 bladeのビューでは @foreach – @foreachend という形で使える

oligo/module.blade.php
@extends('layouts.app')
 
@section('module')
  <x-module id='oligo'>
    <!-- モジュールのメニュー -->
    <nav>
      <ul class='module-tabs' role='tablist'>
        @foreach([
          'index' => '一覧',
          'create' => '追加',
        ] as $page => $label)
          <li><a href='/oligo/{{ $page }} 'id='oligo-{{ $page }}-tab' role='tab'>{{ $label }}</a></li>
        @endforeach
      </ul>
    </nav>
    
    <!-- featureが並ぶところ -->
    <div class='features'>
      @yield('feature')
    </div>
  </x-module>
@endsection

同様に @for, @while, @if, @elseif, @else, @switch, @case, @break, @default, @continue なども使える。

bladeの拡張として、@isset($var) = if(isset($var)), @empty($var) = if(empty($var)), @unless もある。

.envの設定値の活用

.envで設定した値は configディレクトリ以下のファイルを通じてlaravelのシステムに取り込まれる。config関数を使って、その値を参照することができる。

.envAPP_NAMEconfig/app.php'name' =&gt; env('APP_NAME', 'Laravel') という記述を通じて、app.name の値になる。 app.name の値は config('app.name') として取得できる。 config 関数の引数のappはconfig/app.phpのappと対応している。 同様にconfig/services.phpの中にある設定値は、services.をつけて取得することができる。

htmlタグのlang属性も .env のAPP_LOCALEから取得できる。

できるだけ設定値を参照するように app.blade.php と html.blade.php を書き換えた。

layouts/app.blade.php
@extends('layouts.html')
 
@section('body')
  <header>
    <h1>{{ config('app.name') }}</h1>
    <ul id='module-tabs'>
      <li><a id='oligo-tab' href='/oligo'>オリゴDNA</a></li>
    </ul>
  </header>
  <main id='modules'>
    @yield('module')
  </main>
@endsection

layouts/html.blade.php
<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
  <head>
    <meta charset="utf-8">
    <title>{{ config('app.name') }}</title>
  </head>
  <body>
    @yield('body')
  </body>
</html>