CodeIgniter + Smarty速習ガイド

# CodeIgniter + Smarty速習ガイド

## 🎯 Smartyとは？
- **テンプレートエンジン**：PHPコードとHTMLを分離
- **拡張子**: `.tpl` ファイル
- **メリット**: デザイナーとプログラマーの分業が可能

## 📁 ファイル構造
```
application/
├── controllers/           ← PHP処理
├── models/               ← DB操作
├── views/templates/      ← .tplファイル（HTML）
│   ├── header.tpl
│   ├── footer.tpl
│   └── contact/
│       ├── form.tpl
│       ├── confirm.tpl
│       └── complete.tpl
├── views/templates_c/    ← コンパイル済み（自動生成）
├── views/cache/          ← キャッシュ（自動生成）
└── libraries/Smarty.php ← Smarty設定
```

## 🔄 データの流れ
```
Controller → Smarty → Template(.tpl) → HTML表示
    ↓           ↓
 assign()   変数使用
```

## 📝 基本的な使い方

### コントローラーでのデータ設定
```php
// コントローラー側
class Welcome extends CI_Controller {
    
    public function __construct() {
        parent::__construct();
        $this->load->library('smarty');  // Smarty読み込み
    }
    
    public function index() {
        // データを準備
        $data = [
            'title' => 'ページタイトル',
            'message' => 'こんにちは',
            'users' => [
                ['name' => '田中', 'age' => 25],
                ['name' => '佐藤', 'age' => 30]
            ]
        ];
        
        // Smartyテンプレートに変数を渡す
        $this->smarty->assign('title', $data['title']);
        $this->smarty->assign('message', $data['message']);
        $this->smarty->assign('users', $data['users']);
        
        // または一括で渡す
        foreach($data as $key => $value) {
            $this->smarty->assign($key, $value);
        }
        
        // テンプレートを表示
        $this->smarty->display('welcome.tpl');
    }
}
```

### テンプレート(.tpl)での変数表示
```html
<!-- welcome.tpl -->
<!DOCTYPE html>
<html>
<head>
    <title>{$title}</title>  <!-- 変数表示 -->
</head>
<body>
    <h1>{$title}</h1>
    <p>{$message}</p>
    
    <!-- 配列のループ表示 -->
    <ul>
    {foreach $users as $user}
        <li>{$user.name} ({$user.age}歳)</li>
    {/foreach}
    </ul>
</body>
</html>
```

## 🔧 Smarty基本構文

### 1. 変数表示
```html
<!-- 基本表示 -->
{$変数名}

<!-- 配列・連想配列 -->
{$user.name}        <!-- $user['name'] -->
{$users[0].name}    <!-- $users[0]['name'] -->
{$users[$i].name}   <!-- $users[$i]['name'] -->
```

### 2. HTMLエスケープ（重要！）
```html
<!-- XSS対策：必ずescapeを使用 -->
{$user_input|escape}

<!-- デフォルト値設定 -->
{$name|default:'名前なし'}

<!-- 改行をBRタグに変換 -->
{$message|nl2br|escape}
```

### 3. 条件分岐
```html
<!-- if文 -->
{if $user_count > 0}
    <p>ユーザーが{$user_count}人います</p>
{else}
    <p>ユーザーがいません</p>
{/if}

<!-- 複数条件 -->
{if $user.role == 'admin'}
    <p>管理者です</p>
{elseif $user.role == 'user'}
    <p>一般ユーザーです</p>
{else}
    <p>ゲストです</p>
{/if}

<!-- 変数存在チェック -->
{if isset($errors.name)}
    <span class="error">{$errors.name}</span>
{/if}
```

### 4. ループ処理
```html
<!-- foreach（最もよく使う） -->
{foreach $users as $user}
    <li>{$user.name}</li>
{/foreach}

<!-- キーも取得 -->
{foreach $users as $key => $user}
    <li>{$key}: {$user.name}</li>
{/foreach}

<!-- 空の場合の処理 -->
{foreach $users as $user}
    <li>{$user.name}</li>
{foreachelse}
    <li>データがありません</li>
{/foreach}
```

### 5. CodeIgniter関数の使用
```html
<!-- URL生成 -->
<form action="{site_url('contact/submit')}" method="post">

<a href="{site_url('user/profile')}">プロフィール</a>

<img src="{base_url('assets/images/logo.png')}" alt="ロゴ">

<!-- 現在のURL -->
<p>現在のページ: {$current_url}</p>
```

## 📋 フォーム処理の実例

### コントローラー側
```php
public function contact() {
    $data = [
        'errors' => [],
        'form_data' => []
    ];
    
    if ($this->input->method() === 'post') {
        // バリデーション処理
        $this->form_validation->set_rules('name', '名前', 'required');
        $this->form_validation->set_rules('email', 'メール', 'required|valid_email');
        
        if (!$this->form_validation->run()) {
            $data['errors'] = $this->form_validation->error_array();
        }
        
        $data['form_data'] = [
            'name' => $this->input->post('name'),
            'email' => $this->input->post('email')
        ];
    }
    
    // Smartyに渡す
    $this->smarty->assign('errors', $data['errors']);
    $this->smarty->assign('form_data', $data['form_data']);
    $this->smarty->display('contact/form.tpl');
}
```

### テンプレート側
```html
<!-- contact/form.tpl -->
<form method="post" action="{site_url('contact')}">
    <div>
        <label>名前</label>
        <input type="text" name="name" value="{$form_data.name|default:''|escape}">
        {if isset($errors.name)}
            <span class="error">{$errors.name}</span>
        {/if}
    </div>
    
    <div>
        <label>メール</label>
        <input type="email" name="email" value="{$form_data.email|default:''|escape}">
        {if isset($errors.email)}
            <span class="error">{$errors.email}</span>
        {/if}
    </div>
    
    <button type="submit">送信</button>
</form>
```

## 🔨 よく使うパターン

### 1. ヘッダー・フッター共通化
```html
<!-- header.tpl -->
<!DOCTYPE html>
<html>
<head>
    <title>{$title|default:'デフォルトタイトル'}</title>
    <link rel="stylesheet" href="{base_url('assets/css/style.css')}">
</head>
<body>

<!-- footer.tpl -->
    <script src="{base_url('assets/js/script.js')}"></script>
</body>
</html>

<!-- メインテンプレート -->
{include file="header.tpl"}
<main>
    <h1>{$page_title}</h1>
    <!-- コンテンツ -->
</main>
{include file="footer.tpl"}
```

### 2. ページネーション表示
```html
<!-- ページネーション -->
{if $total_pages > 1}
<div class="pagination">
    {if $current_page > 1}
        <a href="{site_url("list/page/".($current_page-1))}">前へ</a>
    {/if}
    
    {for $i=1 to $total_pages}
        {if $i == $current_page}
            <span class="current">{$i}</span>
        {else}
            <a href="{site_url("list/page/$i")}">{$i}</a>
        {/if}
    {/for}
    
    {if $current_page < $total_pages}
        <a href="{site_url("list/page/".($current_page+1))}">次へ</a>
    {/if}
</div>
{/if}
```

### 3. テーブル表示
```html
<!-- データテーブル -->
<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>メール</th>
            <th>登録日</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        {foreach $users as $user}
        <tr>
            <td>{$user.id}</td>
            <td>{$user.name|escape}</td>
            <td>{$user.email|escape}</td>
            <td>{$user.created_at}</td>
            <td>
                <a href="{site_url("user/edit/{$user.id}")}">編集</a>
                <a href="{site_url("user/delete/{$user.id}")}" 
                   onclick="return confirm('削除しますか？')">削除</a>
            </td>
        </tr>
        {foreachelse}
        <tr>
            <td colspan="5">データがありません</td>
        </tr>
        {/foreach}
    </tbody>
</table>
```

## ⚠️ 注意点・トラブル対策

### 1. セキュリティ対策
```html
<!-- 必ずHTMLエスケープ -->
❌ {$user_input}
✅ {$user_input|escape}

<!-- フォームのCSRF対策 -->
<form method="post">
    <!-- CodeIgniterが自動でCSRFトークンを挿入 -->
</form>
```

### 2. よくあるエラー
```
「Smarty error: unable to read template file」
→ テンプレートファイルのパスを確認

「Undefined variable」
→ コントローラーでassignし忘れ

「syntax error in template」
→ {if}と{/if}の対応、構文ミス
```

### 3. デバッグ方法
```html
<!-- 変数の中身確認 -->
{$変数名|@print_r}
{$配列|@var_dump}

<!-- 変数が存在するかチェック -->
{if isset($変数名)}
    存在します
{else}
    存在しません
{/if}
```

### 4. パフォーマンス向上
```php
// キャッシュ有効化（本番環境）
$this->smarty->setCaching(Smarty::CACHING_LIFETIME_CURRENT);

// 開発環境ではキャッシュオフ
$this->smarty->setCaching(Smarty::CACHING_OFF);
```

## 🚀 時短Tips

### 1. よく使うコントローラーテンプレート
```php
public function index() {
    // 共通データ
    $data = [
        'page_title' => 'ページタイトル',
        'errors' => [],
        'success' => false
    ];
    
    // 処理
    if ($this->input->method() === 'post') {
        // フォーム処理
    }
    
    // Smartyに一括で渡す
    foreach($data as $key => $value) {
        $this->smarty->assign($key, $value);
    }
    
    $this->smarty->display('template_name.tpl');
}
```

### 2. エラー表示の共通パーツ
```html
<!-- _error.tpl（共通エラー表示） -->
{if isset($errors) && $errors}
<div class="alert alert-danger">
    {foreach $errors as $field => $error}
        <p>{$error}</p>
    {/foreach}
</div>
{/if}

<!-- 使用時 -->
{include file="_error.tpl"}
```

## 📚 まとめ

**覚える優先順位：**
1. `{$変数名|escape}` - 変数表示＋エスケープ
2. `{if}{/if}` - 条件分岐
3. `{foreach}{/foreach}` - ループ
4. `{site_url()}` - URL生成
5. `{include file=""}` - 共通パーツ読み込み

**安全に作るコツ：**
- 必ず`|escape`を付ける
- エラーハンドリングを忘れない
- 共通パーツを活用する