PHPの基礎 - テンプレート方式

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要

テンプレート方式とは、HTMLのデザイン部分とPHPのロジック部分を分離することである。
これにより、デザイナと開発者が別々に作業しやすくなり、コードの保守性も向上する。

テンプレート方式を採用する主なメリットを以下に示す。

  • コードの可読性向上
  • メンテナンス性の向上
  • チーム開発での作業効率向上
  • デザインの一貫性維持が容易


代表的なテンプレートの実装方法を以下に示す。

  • 基本的な分離方式
    PHPファイルとHTMLファイルを分けて、includeで結合する。
  • テンプレートエンジンの使用
    Smarty、Twig等のテンプレートエンジンを使用する。


なお、テンプレート方式を導入する場合は、プロジェクトの規模や要件に応じて適切な方法を選択することが重要である。
小規模なプロジェクトの場合は基本的な分離方式で十分の可能性があり、大規模なプロジェクトではテンプレートエンジンの採用を検討する。


基本的な分離方式

PHPファイルとHTMLファイルを分けて、include文で結合する方法である。

 // index.php
 
 $title   = "Webサイトのタイトル";
 $content = "ここに本文を入力する";
 
 include 'template.html';


 <!-- template.html -->
 
 <!DOCTYPE html>
 <html>
 <head>
    <!-- 変数titleを使用する -->
    <title><?php echo $title; ?></title>
 </head>
 <body>
    <!-- 変数contentを使用する -->
    <?php echo $content; ?>
 </body>
 </html>


例えば、PHPを使用したテーブルを実装する場合は、以下に示すようなテンプレート方式が推奨される。

 // template.php
 
 <table class="data-table">
    <thead>
       <tr>
          <th>ID</th>
          <th>名前</th>
          <th>メール</th>
       </tr>
    </thead>
    <tbody>
    <?php foreach($users as $user): ?>  // PHPの代替構文の使用, {の代わりに:を使用
       <tr>
          <td><?php echo htmlspecialchars($user['id']); ?></td>
          <td><?php echo htmlspecialchars($user['name']); ?></td>
          <td><?php echo htmlspecialchars($user['email']); ?></td>
       </tr>
    <?php endforeach; ?>  // }の代わりにend構文名を使用 (他にも、endforeach, endif, endwhile等がある)
    </tbody>
 </table>
 <?php else: ?>
    <p>データがありません。</p>
 <?php endif; ?>


実務では、テンプレート方式が推奨される理由として、以下に示すことが挙げられる。

  • 保守性
    HTMLとPHPの分離により、デザインの修正が容易である。
  • 可読性
    マークアップ構造が視覚的に把握しやすい。
  • チーム開発
    フロントエンドとバックエンドの開発者が分担しやすい。
  • セキュリティ
    XSS対策などのセキュリティ処理が漏れにくい。


さらに、現代的な開発では以下に示すようなアプローチも採用されている。

  • テンプレートエンジン
    Blade、Twig等の使用
  • MVCフレームワーク
    Laravel、Symphony等の使用
  • コンポーネント指向の実装



代替構文

 // 一般的な記法
 <?php 
 foreach($users as $user) {
    echo "<tr>";
    echo "<td>" . htmlspecialchars($user['id']) . "</td>";
    echo "<td>" . htmlspecialchars($user['name']) . "</td>";
    echo "<td>" . htmlspecialchars($user['email']) . "</td>";
    echo "</tr>";
 }
 ?>
 
 // 代替構文
 <?php foreach($users as $user): ?>
    <tr>
       <td><?php echo htmlspecialchars($user['id']); ?></td>
       <td><?php echo htmlspecialchars($user['name']); ?></td>
       <td><?php echo htmlspecialchars($user['email']); ?></td>
    </tr>
 <?php endforeach; ?>


代替構文の特徴

  • { の代わりに : を使用
  • } の代わりに end構文名を使用 (endforeach, endif, endwhile等)
  • HTMLとPHPを混ぜて書く場合に、可読性が良い。
  • 特に、テンプレートエンジンでよく使用される。


HTMLを多く含むテンプレートでは、代替構文を使用する方が構造が分かりやすくなるため、よく使用される。
特にテンプレートエンジン (Laravel Blade、Smarty等) でも似た記法が採用されている。

この構文は、以下に示すような制御構造でも使用できる。

 <?php if($condition): ?>
    // HTMLなど
 <?php endif; ?>
 
 <?php while($condition): ?>
    // HTMLなど
 <?php endwhile; ?>
 
 <?php for($i = 0; $i < 10; $i++): ?>
    // HTMLなど
 <?php endfor; ?>




レイアウトの共通化

ヘッダ部やフッタ部では、共通部分を別ファイルとして管理することにより、保守性が向上する。

 // header.php
 
 <!DOCTYPE html>
 <html>
 <head>
    <!-- 必要に応じて、ヘッダ部分で変数を確認する処理を入れる -->
    <?php
       if (!isset($title)) {
          $title = "デフォルトタイトル";
       }
    ?>
    <title><?php echo $title; ?></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/css/style.css">
 </head>
 <body>
    <header>
       <nav>
          <ul>
             <li><a href="/">ホーム</a></li>
             <li><a href="/about">会社概要</a></li>
             <li><a href="/contact">お問い合わせ</a></li>
          </ul>
       </nav>
    </header>


 // footer.php
 
    <footer>
       <div class="footer-content">
          <p>&copy; 2024 会社名 All Rights Reserved.</p>
       </div>
    </footer>
    <script src="/js/main.js"></script>
 </body>
 </html>


 // content.php (このファイルをトップページとする)
 
 <?php
   $title = "ページタイトル";
 
   include 'header.php';
 ?>
 
 <!-- メインコンテンツ -->
 <main>
 <div class="content">
    <h1>ようこそ</h1>
    <p>これはメインコンテンツです。</p>
 </div>
 </main>
 
 <?php
    include 'footer.php';
 ?>



テンプレートエンジンの使用

Smarty、Twig等のテンプレートエンジンを使用する方法がある。
これらは独自の構文を持ち、より柔軟なテンプレート制御が可能である。

Twigを使用する場合

 // index.php
 
 <?php
 require_once 'vendor/autoload.php';
 
 $loader = new \Twig\Loader\FilesystemLoader('templates');
 $twig = new \Twig\Environment($loader);
 
 echo $twig->render('template.twig', [
    'title'   => 'Webサイトのタイトル',
    'content' => 'ここに本文を入力する'
 ]);


 {# template.twig #}
 
 <!DOCTYPE html>
 <html>
 <head>
    <title>{{ title }}</title>
 </head>
 <body>
    {{ content }}
 </body>
 </html>


Laravelを使用する場合

LaravelのBladeテンプレートエンジンは、PHPの機能を損なうことなく、保守性の高いテンプレートを作成できる機能を提供している。

Bladeテンプレートの特徴を以下に示す。

  • @extends、@section、@yield によるレイアウトの継承
  • {{ }} による変数の出力 (自動エスケープ付き)
  • {!! !!} によるHTMLエスケープなしの出力
  • @if、@foreach等のディレクティブ
  • コンポーネントによる再利用可能なUI部品の作成
  • バリデーションエラーの表示が簡単
  • @auth、@guest等の認証関連ディレクティブ


Bladeの基本的な構造
 // routes/web.php
 
 Route::get('/', function () {
    return view('welcome', [
       'title'   => 'Webサイトのタイトル',
       'content' => 'ここに本文を入れる'
    ]);
 });


 // resources/views/layouts/app.blade.php
 
 <!DOCTYPE html>
 <html>
 <head>
    <title>@yield('title')</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    @vite([
       'resources/css/app.css',
       'resources/js/app.js'
    ])
 </head>
 <body>
    <header>
       @include('layouts.navigation')
    </header>
 
    <main>
       @yield('content')
    </main>
 
    <footer>
       @include('layouts.footer')
    </footer>
 </body>
 </html>


 // resources/views/welcome.blade.php
 
 @extends('layouts.app')
 
 @section('title', $title)
 
 @section('content')
    <div class="container">
       <h1>{{ $title }}</h1>
       <p>{{ $content }}</p>
 
       {{-- 条件分岐の例 --}}
       @if (Auth::check())
          <p>ようこそ、{{ Auth::user()->name }}さん</p>
       @else
          <p>ログインしてください</p>
       @endif
 
       {{-- ループの例 --}}
       @foreach ($items as $item)
          <div class="item">
             {{-- エスケープなしで出力する場合 --}}
             {!! $item->description !!}
          </div>
       @endforeach
    </div>
 @endsection


コンポーネントの使用例
 // resources/views/components/button.blade.php
 
 <button {{ $attributes->merge(['class' => 'btn']) }}>
    {{ $slot }}
 </button>


 // resources/views/welcome.blade.php での使用例
 
 <x-button class="btn-primary">
    クリックしてください
 </x-button>


共通パーツの分離例
 // resources/views/layouts/navigation.blade.php
 
 <nav>
    <ul>
       <li><a href="{{ route('home') }}">ホーム</a></li>
       <li><a href="{{ route('about') }}">会社概要</a></li>
       <li><a href="{{ route('contact') }}">お問い合わせ</a></li>
    </ul>
 </nav>


フラッシュメッセージの表示例
 @if (session('success'))
    <div class="alert alert-success">
       {{ session('success') }}
    </div>
 @endif


バリデーションエラーの表示例
 @error('email')
    <div class="alert alert-danger">{{ $message }}</div>
 @enderror