模板继承 #

一、模板继承概述 #

1.1 继承概念 #

Twig模板继承允许定义基础模板和子模板,子模板可以覆盖父模板的区块。

text
┌─────────────────────────────────────────────────────┐
│                  模板继承结构                        │
├─────────────────────────────────────────────────────┤
│                                                     │
│   base.html.twig (基础模板)                         │
│   ├── header区块                                    │
│   ├── content区块                                   │
│   └── footer区块                                    │
│                                                     │
│   page.html.twig (子模板)                           │
│   └── 继承 base.html.twig                           │
│       └── 覆盖 content区块                          │
│                                                     │
└─────────────────────────────────────────────────────┘

1.2 三级继承 #

text
base.html.twig           # 基础布局
└── layout.html.twig     # 页面布局
    └── page.html.twig   # 具体页面

二、基础模板 #

2.1 创建基础模板 #

twig
{# templates/base.html.twig #}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Symfony App{% endblock %}</title>
    
    {% block stylesheets %}
        <link rel="stylesheet" href="{{ asset('css/style.css') }}">
    {% endblock %}
</head>
<body>
    <header>
        {% block header %}
            <nav class="navbar">
                <a href="{{ path('app_home') }}">首页</a>
                <a href="{{ path('app_about') }}">关于</a>
                <a href="{{ path('app_contact') }}">联系</a>
            </nav>
        {% endblock %}
    </header>

    <main>
        {% block body %}{% endblock %}
    </main>

    <footer>
        {% block footer %}
            <p>&copy; {{ "now"|date("Y") }} Symfony App</p>
        {% endblock %}
    </footer>

    {% block javascripts %}
        <script src="{{ asset('js/app.js') }}"></script>
    {% endblock %}
</body>
</html>

2.2 区块定义 #

twig
{# 定义区块 #}
{% block name %}
    默认内容
{% endblock %}

{# 简写 #}
{% block name %}默认内容{% endblock %}

{# 带名称结束标签 #}
{% block content %}
    内容
{% endblock content %}

三、子模板继承 #

3.1 基本继承 #

twig
{# templates/home/index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}首页 - {{ parent() }}{% endblock %}

{% block body %}
    <h1>欢迎来到Symfony</h1>
    <p>这是一个强大的PHP框架</p>
{% endblock %}

3.2 调用父区块 #

twig
{# templates/user/profile.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}用户资料 - {{ parent() }}{% endblock %}

{% block stylesheets %}
    {{ parent() }}
    <link rel="stylesheet" href="{{ asset('css/profile.css') }}">
{% endblock %}

{% block body %}
    <h1>{{ user.name }}</h1>
    <p>{{ user.email }}</p>
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    <script src="{{ asset('js/profile.js') }}"></script>
{% endblock %}

3.3 完全覆盖 #

twig
{# templates/admin/dashboard.html.twig #}
{% extends 'base.html.twig' %}

{# 完全覆盖header区块 #}
{% block header %}
    <nav class="admin-nav">
        <a href="{{ path('admin_dashboard') }}">仪表盘</a>
        <a href="{{ path('admin_users') }}">用户管理</a>
        <a href="{{ path('admin_settings') }}">设置</a>
    </nav>
{% endblock %}

{% block body %}
    <h1>管理后台</h1>
    <div class="dashboard-stats">
        {# 统计数据 #}
    </div>
{% endblock %}

四、多级继承 #

4.1 三级继承示例 #

基础模板:

twig
{# templates/base.html.twig #}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}App{% endblock %}</title>
    {% block meta %}{% endblock %}
    {% block stylesheets %}
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    {% endblock %}
</head>
<body>
    {% block body %}{% endblock %}
    {% block javascripts %}{% endblock %}
</body>
</html>

布局模板:

twig
{# templates/layout.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    <div class="wrapper">
        <aside class="sidebar">
            {% block sidebar %}
                <nav>
                    <a href="{{ path('app_home') }}">首页</a>
                    <a href="{{ path('app_dashboard') }}">仪表盘</a>
                </nav>
            {% endblock %}
        </aside>

        <main class="content">
            {% block content %}{% endblock %}
        </main>
    </div>
{% endblock %}

页面模板:

twig
{# templates/dashboard/index.html.twig #}
{% extends 'layout.html.twig' %}

{% block title %}仪表盘 - {{ parent() }}{% endblock %}

{% block sidebar %}
    {{ parent() }}
    <nav class="sub-nav">
        <a href="{{ path('dashboard_overview') }}">概览</a>
        <a href="{{ path('dashboard_analytics') }}">分析</a>
    </nav>
{% endblock %}

{% block content %}
    <h1>仪表盘</h1>
    <div class="stats">
        {# 统计内容 #}
    </div>
{% endblock %}

4.2 条件继承 #

twig
{# 根据条件选择模板 #}
{% extends user ? 'layout_logged_in.html.twig' : 'layout_guest.html.twig' %}

{# 使用变量选择模板 #}
{% extends layout_template|default('base.html.twig') %}

五、区块嵌套 #

5.1 嵌套区块 #

twig
{# templates/base.html.twig #}
{% block content %}
    <div class="container">
        {% block inner_content %}
            默认内容
        {% endblock %}
    </div>
{% endblock %}
twig
{# templates/page.html.twig #}
{% extends 'base.html.twig' %}

{% block inner_content %}
    <h1>页面标题</h1>
    <p>页面内容</p>
{% endblock %}

5.2 区块引用 #

twig
{# 定义区块 #}
{% block title %}页面标题{% endblock %}

{# 引用区块 #}
<h1>{{ block('title') }}</h1>

{# 条件引用 #}
{% if block('sidebar') is defined %}
    {{ block('sidebar') }}
{% endif %}

六、布局组织 #

6.1 目录结构 #

text
templates/
├── base.html.twig              # 基础模板
├── layouts/
│   ├── admin.html.twig         # 后台布局
│   ├── frontend.html.twig      # 前台布局
│   └── email.html.twig         # 邮件布局
├── partials/
│   ├── header.html.twig        # 头部
│   ├── footer.html.twig        # 底部
│   ├── sidebar.html.twig       # 侧边栏
│   └── nav.html.twig           # 导航
├── home/
│   └── index.html.twig         # 首页
├── user/
│   ├── profile.html.twig       # 用户资料
│   └── settings.html.twig      # 用户设置
└── admin/
    ├── dashboard.html.twig     # 后台首页
    └── users.html.twig         # 用户管理

6.2 布局模板 #

后台布局:

twig
{# templates/layouts/admin.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
<div class="admin-wrapper">
    <aside class="admin-sidebar">
        {% include 'partials/admin_nav.html.twig' %}
    </aside>
    
    <div class="admin-main">
        <header class="admin-header">
            {% block admin_header %}
                <h1>{% block page_title %}{% endblock %}</h1>
            {% endblock %}
        </header>
        
        <div class="admin-content">
            {% block content %}{% endblock %}
        </div>
    </div>
</div>
{% endblock %}

前台布局:

twig
{# templates/layouts/frontend.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
<div class="frontend-wrapper">
    {% include 'partials/header.html.twig' %}
    
    <main class="main-content">
        {% block hero %}{% endblock %}
        {% block content %}{% endblock %}
    </main>
    
    {% include 'partials/footer.html.twig' %}
</div>
{% endblock %}

6.3 使用布局 #

twig
{# templates/admin/users.html.twig #}
{% extends 'layouts/admin.html.twig' %}

{% block page_title %}用户管理{% endblock %}

{% block content %}
<div class="card">
    <div class="card-header">
        <h3>用户列表</h3>
    </div>
    <div class="card-body">
        <table class="table">
            {# 表格内容 #}
        </table>
    </div>
</div>
{% endblock %}

七、包含和嵌入 #

7.1 include包含 #

twig
{# 包含模板 #}
{{ include('partials/header.html.twig') }}

{# 传递变量 #}
{{ include('partials/user_card.html.twig', {
    user: user,
    showEmail: true
}) }}

{# 仅传递特定变量 #}
{{ include('partials/product.html.twig', with_context = false) }}

{# 合并变量 #}
{{ include('partials/item.html.twig', {
    showPrice: true
}, with_context = true) }}

7.2 embed嵌入 #

twig
{# 定义可嵌入模板 #}
{# templates/partials/card.html.twig #}
<div class="card">
    <div class="card-header">
        {% block card_header %}默认标题{% endblock %}
    </div>
    <div class="card-body">
        {% block card_body %}默认内容{% endblock %}
    </div>
    <div class="card-footer">
        {% block card_footer %}{% endblock %}
    </div>
</div>
twig
{# 使用embed #}
{% embed 'partials/card.html.twig' %}
    {% block card_header %}
        <h3>自定义标题</h3>
    {% endblock %}
    
    {% block card_body %}
        <p>自定义内容</p>
    {% endblock %}
    
    {% block card_footer %}
        <button class="btn">确定</button>
    {% endblock %}
{% endembed %}

{# 传递变量 #}
{% embed 'partials/card.html.twig' with {
    class: 'bg-primary',
    title: '卡片标题'
} %}
    {% block card_header %}
        <h3>{{ title }}</h3>
    {% endblock %}
{% endembed %}

7.3 use使用 #

twig
{# 定义可复用区块 #}
{# templates/blocks/sidebar.html.twig #}
{% block sidebar_menu %}
    <ul>
        <li><a href="/">首页</a></li>
        <li><a href="/about">关于</a></li>
    </ul>
{% endblock %}

{% block sidebar_widgets %}
    <div class="widget">小工具</div>
{% endblock %}
twig
{# 使用区块 #}
{% use 'blocks/sidebar.html.twig' %}

<aside class="sidebar">
    {{ block('sidebar_menu') }}
    {{ block('sidebar_widgets') }}
</aside>

{# 重命名区块 #}
{% use 'blocks/sidebar.html.twig' with sidebar_menu as base_menu %}

{{ block('base_menu') }}

八、组件化模板 #

8.1 定义组件 #

twig
{# templates/components/button.html.twig #}
{% set type = type|default('primary') %}
{% set size = size|default('md') %}

<button class="btn btn-{{ type }} btn-{{ size }} {{ class|default('') }}"
        {% if disabled %}disabled{% endif %}>
    {% block content %}
        {{ text|default('按钮') }}
    {% endblock %}
</button>

8.2 使用组件 #

twig
{# 简单使用 #}
{{ include('components/button.html.twig', {
    text: '提交',
    type: 'success',
    size: 'lg'
}) }}

{# 嵌入使用 #}
{% embed 'components/button.html.twig' with {
    type: 'danger',
    size: 'sm'
} %}
    {% block content %}
        <i class="icon-delete"></i> 删除
    {% endblock %}
{% endembed %}

8.3 表单组件 #

twig
{# templates/components/form_field.html.twig #}
<div class="form-group {{ error ? 'has-error' : '' }}">
    {% if label %}
        <label for="{{ id }}">{{ label }}</label>
    {% endif %}
    
    <input type="{{ type|default('text') }}"
           id="{{ id }}"
           name="{{ name }}"
           value="{{ value|default('') }}"
           class="form-control {{ class|default('') }}"
           {% if placeholder %}placeholder="{{ placeholder }}"{% endif %}
           {% if required %}required{% endif %}>
    
    {% if error %}
        <span class="error-message">{{ error }}</span>
    {% endif %}
    
    {% if help %}
        <span class="help-text">{{ help }}</span>
    {% endif %}
</div>
twig
{# 使用表单组件 #}
{{ include('components/form_field.html.twig', {
    id: 'username',
    name: 'username',
    label: '用户名',
    placeholder: '请输入用户名',
    required: true,
    help: '用户名长度为4-20个字符'
}) }}

九、最佳实践 #

9.1 模板组织建议 #

text
模板组织建议:
├── 使用三层继承结构
├── 基础模板只定义结构
├── 布局模板添加通用元素
├── 页面模板填充具体内容
├── 组件使用include或embed
└── 避免过深的继承层次

9.2 区块命名规范 #

twig
{# 推荐的区块命名 #}
{% block title %}{% endblock %}
{% block meta %}{% endblock %}
{% block stylesheets %}{% endblock %}
{% block header %}{% endblock %}
{% block navigation %}{% endblock %}
{% block hero %}{% endblock %}
{% block content %}{% endblock %}
{% block sidebar %}{% endblock %}
{% block footer %}{% endblock %}
{% block javascripts %}{% endblock %}

十、总结 #

本章学习了:

  • 模板继承概念
  • 基础模板定义
  • 子模板继承
  • 多级继承
  • 区块嵌套和引用
  • 布局组织
  • include和embed使用
  • 组件化模板

下一章将学习 模板高级

最后更新:2026-03-28