模板继承 #
一、模板继承概述 #
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>© {{ "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