Twig基础 #

一、Twig概述 #

1.1 什么是Twig #

Twig是Symfony默认的模板引擎,提供简洁、安全的模板语法。

text
┌─────────────────────────────────────────────────────┐
│                  Twig 特点                          │
├─────────────────────────────────────────────────────┤
│  • 简洁语法:{{ }} 输出,{% %} 逻辑,{# #} 注释     │
│  • 安全性:自动转义,沙箱模式                       │
│  • 可扩展:自定义过滤器、函数、标签                 │
│  • 高性能:编译缓存                                 │
│  • 面向对象:模板继承、区块覆盖                     │
└─────────────────────────────────────────────────────┘

1.2 基本语法 #

twig
{# 这是注释 #}

{# 输出变量 #}
{{ name }}

{# 逻辑控制 #}
{% if user.active %}
    <p>用户已激活</p>
{% endif %}

{# 循环 #}
{% for item in items %}
    <li>{{ item.name }}</li>
{% endfor %}

二、变量输出 #

2.1 输出变量 #

twig
{# 简单变量 #}
{{ name }}
{{ user.name }}
{{ user['name'] }}

{# 对象属性 #}
{{ user.getName() }}
{{ user.name }}

{# 数组元素 #}
{{ users[0] }}
{{ users.0 }}

{# 嵌套访问 #}
{{ user.address.city }}
{{ order.items[0].product.name }}

2.2 输出过滤器 #

twig
{# 转义 #}
{{ content|e }}
{{ content|e('html') }}

{# 原始输出(不转义) #}
{{ html|raw }}

{# 默认值 #}
{{ user.name|default('匿名用户') }}

{# 长度 #}
{{ items|length }}

{# 类型转换 #}
{{ value|string }}
{{ value|integer }}
{{ value|float }}
{{ value|boolean }}

三、文本处理 #

3.1 字符串过滤器 #

twig
{# 大小写转换 #}
{{ name|upper }}
{{ name|lower }}
{{ name|capitalize }}
{{ name|title }}

{# 去除空白 #}
{{ text|trim }}
{{ text|trim('-') }}
{{ text|trim(side='left') }}

{# 截取 #}
{{ text|slice(0, 10) }}
{{ text|slice(0, 10) ~ '...' }}

{# 字符串操作 #}
{{ text|replace({'foo': 'bar'}) }}
{{ text|split(',') }}
{{ text|split('', 3) }}
{{ text|first }}
{{ text|last }}

{# 编码 #}
{{ url|url_encode }}
{{ text|json_encode }}
{{ text|html_encode }}

3.2 格式化 #

twig
{# 字符串格式化 #}
{{ "Hello, %s!"|format(name) }}
{{ "%s has %d items"|format(user.name, count) }}

{# 数字格式化 #}
{{ price|number_format }}
{{ price|number_format(2) }}
{{ price|number_format(2, ',', '.') }}

{# 日期格式化 #}
{{ date|date }}
{{ date|date('Y-m-d') }}
{{ date|date('Y-m-d H:i:s') }}
{{ date|date('Y年m月d日') }}

{# 相对时间 #}
{{ date|ago }}

3.3 文本处理示例 #

twig
{# 标题处理 #}
<h1>{{ article.title|capitalize }}</h1>

{# 摘要截取 #}
<p>{{ article.content|striptags|slice(0, 200) }}...</p>

{# 价格显示 #}
<span class="price">¥{{ product.price|number_format(2) }}</span>

{# 日期显示 #}
<time>{{ article.createdAt|date('Y-m-d H:i') }}</time>

{# URL友好化 #}
<a href="/article/{{ article.title|slug }}">{{ article.title }}</a>

四、控制结构 #

4.1 条件判断 #

twig
{# if语句 #}
{% if user.active %}
    <p>用户已激活</p>
{% endif %}

{# if-else #}
{% if user.role == 'admin' %}
    <p>管理员</p>
{% else %}
    <p>普通用户</p>
{% endif %}

{# if-elseif-else #}
{% if score >= 90 %}
    <p>优秀</p>
{% elseif score >= 80 %}
    <p>良好</p>
{% elseif score >= 60 %}
    <p>及格</p>
{% else %}
    <p>不及格</p>
{% endif %}

{# 逻辑运算 #}
{% if user.active and user.verified %}
    <p>活跃且已验证</p>
{% endif %}

{% if user.role == 'admin' or user.role == 'super_admin' %}
    <p>管理员权限</p>
{% endif %}

{% if not user.banned %}
    <p>用户正常</p>
{% endif %}

{# in运算符 #}
{% if user.role in ['admin', 'editor'] %}
    <p>有编辑权限</p>
{% endif %}

{# 测试运算符 #}
{% if user.name is defined %}
    <p>{{ user.name }}</p>
{% endif %}

{% if user is null %}
    <p>用户不存在</p>
{% endif %}

{% if items is empty %}
    <p>暂无数据</p>
{% endif %}

{% if number is even %}
    <p>偶数</p>
{% endif %}

{% if number is odd %}
    <p>奇数</p>
{% endif %}

4.2 循环 #

twig
{# 遍历数组 #}
<ul>
{% for user in users %}
    <li>{{ user.name }}</li>
{% endfor %}
</ul>

{# 遍历关联数组 #}
<dl>
{% for key, value in config %}
    <dt>{{ key }}</dt>
    <dd>{{ value }}</dd>
{% endfor %}
</dl>

{# 循环变量 #}
<table>
{% for item in items %}
    <tr class="{{ loop.index is odd ? 'odd' : 'even' }}">
        <td>{{ loop.index }}</td>
        <td>{{ item.name }}</td>
    </tr>
{% endfor %}
</table>

{# loop变量属性 #}
loop.index      {# 当前索引(从1开始) #}
loop.index0     {# 当前索引(从0开始) #}
loop.revindex   {# 倒序索引(从1开始) #}
loop.revindex0  {# 倒序索引(从0开始) #}
loop.first      {# 是否第一个 #}
loop.last       {# 是否最后一个 #}
loop.length     {# 总数量 #}
loop.parent     {# 父循环 #}

{# else分支 #}
<ul>
{% for user in users %}
    <li>{{ user.name }}</li>
{% else %}
    <li>暂无用户</li>
{% endfor %}
</ul>

{# 循环控制 #}
{% for i in 1..10 %}
    {% if i > 5 %}
        {% break %}
    {% endif %}
    <p>{{ i }}</p>
{% endfor %}

{% for i in 1..10 %}
    {% if i is even %}
        {% continue %}
    {% endif %}
    <p>{{ i }}</p>
{% endfor %}

{# 范围循环 #}
{% for i in 0..10 %}
    {{ i }}
{% endfor %}

{% for i in 'a'..'z' %}
    {{ i }}
{% endfor %}

{# 步长循环 #}
{% for i in 0..10|step(2) %}
    {{ i }}
{% endfor %}

五、数组处理 #

5.1 数组过滤器 #

twig
{# 排序 #}
{% for item in items|sort %}
    {{ item }}
{% endfor %}

{# 反转 #}
{% for item in items|reverse %}
    {{ item }}
{% endfor %}

{# 合并 #}
{% set all = items1|merge(items2) %}

{# 键名 #}
{% for key in items|keys %}
    {{ key }}
{% endfor %}

{# 列取值 #}
{% for name in users|column('name') %}
    {{ name }}
{% endfor %}

{# 过滤 #}
{% for user in users|filter(u => u.active) %}
    {{ user.name }}
{% endfor %}

{# 映射 #}
{% for name in users|map(u => u.name|upper) %}
    {{ name }}
{% endfor %}

{# 去重 #}
{% for tag in tags|unique %}
    {{ tag }}
{% endfor %}

{# 批量处理 #}
{% for row in items|batch(3) %}
    <div class="row">
        {% for item in row %}
            <div class="col">{{ item }}</div>
        {% endfor %}
    </div>
{% endfor %}

5.2 数组操作示例 #

twig
{# 用户列表 #}
<table>
    <thead>
        <tr>
            <th>#</th>
            <th>姓名</th>
            <th>邮箱</th>
            <th>状态</th>
        </tr>
    </thead>
    <tbody>
        {% for user in users|sort((a, b) => a.name <=> b.name) %}
            <tr>
                <td>{{ loop.index }}</td>
                <td>{{ user.name }}</td>
                <td>{{ user.email }}</td>
                <td>{{ user.active ? '活跃' : '禁用' }}</td>
            </tr>
        {% else %}
            <tr>
                <td colspan="4">暂无用户</td>
            </tr>
        {% endfor %}
    </tbody>
</table>

{# 分组显示 #}
{% for row in products|batch(4) %}
    <div class="row">
        {% for product in row %}
            <div class="col-md-3">
                <div class="card">
                    <img src="{{ product.image }}">
                    <h5>{{ product.name }}</h5>
                    <p>¥{{ product.price|number_format(2) }}</p>
                </div>
            </div>
        {% endfor %}
    </div>
{% endfor %}

六、变量操作 #

6.1 变量定义 #

twig
{# set定义变量 #}
{% set name = 'John' %}
{% set age = 25 %}
{% set active = true %}

{# 定义数组 #}
{% set colors = ['red', 'green', 'blue'] %}
{% set user = {'name': 'John', 'age': 25} %}

{# 定义多行文本 #}
{% set content %}
    <div class="content">
        <p>多行内容</p>
    </div>
{% endset %}

{# 使用变量 #}
<p>{{ name }}</p>
<p>{{ colors|join(', ') }}</p>
{{ content }}

6.2 变量作用域 #

twig
{# 全局变量 #}
{% set global = 'global' %}

{% for i in 1..3 %}
    {# 循环内可访问全局变量 #}
    {{ global }}
    
    {# 循环内变量外部不可访问 #}
    {% set local = 'local' ~ i %}
{% endfor %}

{# local变量这里不可访问 #}

{# 使用with创建作用域 #}
{% with %}
    {% set temp = 'temporary' %}
    {{ temp }}
{% endwith %}

{# temp变量这里不可访问 #}

七、包含和导入 #

7.1 include包含 #

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

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

{# 传递多个变量 #}
{{ include('partials/product.html.twig', {
    product: product,
    showPrice: true,
    showStock: false
}) }}

{# 条件包含 #}
{% if user %}
    {{ include('partials/user_panel.html.twig') }}
{% else %}
    {{ include('partials/login_panel.html.twig') }}
{% endif %}

{# 忽略错误 #}
{{ include('optional/template.html.twig', ignore_missing = true) }}

7.2 模板片段 #

twig
{# 定义片段 #}
{# templates/partials/cards.html.twig #}

{% card(title, content) %}
    <div class="card">
        <div class="card-header">{{ title }}</div>
        <div class="card-body">{{ content }}</div>
    </div>
{% endcard %}

{# 使用片段 #}
{% from 'partials/cards.html.twig' import card %}

{{ card('标题', '内容') }}

八、URL和路径 #

8.1 生成URL #

twig
{# 相对路径 #}
<a href="{{ path('app_home') }}">首页</a>

{# 带参数 #}
<a href="{{ path('app_user_show', {id: user.id}) }}">查看用户</a>

{# 绝对URL #}
<a href="{{ url('app_home') }}">首页</a>

{# 检查路由 #}
{% if path('app_admin_dashboard') is defined %}
    <a href="{{ path('app_admin_dashboard') }}">管理后台</a>
{% endif %}

8.2 资源路径 #

twig
{# 静态资源 #}
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
<script src="{{ asset('js/app.js') }}"></script>
<img src="{{ asset('images/logo.png') }}" alt="Logo">

{# 绝对路径资源 #}
<img src="{{ absolute_url(asset('images/logo.png')) }}">

{# 版本化资源 #}
<link rel="stylesheet" href="{{ asset('css/style.css', version='1.0.0') }}">

九、安全输出 #

9.1 自动转义 #

twig
{# 默认自动转义HTML #}
{{ content }}  {# 自动转义 #}

{# 指定转义策略 #}
{{ content|e('html') }}
{{ content|e('js') }}
{{ content|e('css') }}
{{ content|e('url') }}

{# 不转义 #}
{{ trusted_html|raw }}

{# 关闭自动转义 #}
{% autoescape false %}
    {{ content }}
{% endautoescape %}

{# 开启自动转义 #}
{% autoescape 'html' %}
    {{ content }}
{% endautoescape %}

9.2 安全示例 #

twig
{# 用户输入内容 #}
<div class="content">
    {{ userComment|e }}
</div>

{# 富文本(确保已过滤) #}
<div class="article">
    {{ article.content|raw }}
</div>

{# JSON数据 #}
<script>
    var userData = {{ user|json_encode|raw }};
</script>

{# 属性值 #}
<div class="user" data-name="{{ user.name|e('html_attr') }}">
    {{ user.name }}
</div>

十、总结 #

本章学习了:

  • Twig基本语法
  • 变量输出和过滤器
  • 文本处理
  • 控制结构(条件、循环)
  • 数组处理
  • 变量定义和作用域
  • 模板包含
  • URL生成
  • 安全输出

下一章将学习 模板继承

最后更新:2026-03-28